• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/webui/chromeos/imageburner_ui.h"
6 
7 #include <algorithm>
8 
9 #include "base/i18n/rtl.h"
10 #include "base/memory/singleton.h"
11 #include "base/message_loop.h"
12 #include "base/path_service.h"
13 #include "base/string_util.h"
14 #include "base/task.h"
15 #include "base/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/download/download_types.h"
18 #include "chrome/browser/download/download_util.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/common/jstemplate_builder.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/common/zip.h"
24 #include "content/browser/browser_thread.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "grit/browser_resources.h"
27 #include "grit/generated_resources.h"
28 #include "grit/locale_settings.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 
32 static const char kPropertyPath[] = "path";
33 static const char kPropertyTitle[] = "title";
34 static const char kPropertyDirectory[] = "isDirectory";
35 static const char kImageBaseURL[] =
36     "http://chrome-master.mtv.corp.google.com/chromeos/dev-channel/";
37 static const char kImageFetcherName[] = "LATEST-x86-generic";
38 static const char kImageDownloadURL[] =
39     "https://dl.google.com/dl/chromeos/recovery/latest_mario_beta_channel";
40 static const char kImageFileName[] = "chromeos_image.bin.zip";
41 static const char kTempImageFolderName[] = "chromeos_image";
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 //
45 // ImageBurnUIHTMLSource
46 //
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 class ImageBurnUIHTMLSource : public ChromeURLDataManager::DataSource {
50  public:
ImageBurnUIHTMLSource()51   ImageBurnUIHTMLSource()
52       : DataSource(chrome::kChromeUIImageBurnerHost, MessageLoop::current()) {
53   }
54 
55   // Called when the network layer has requested a resource underneath
56   // the path we registered.
StartDataRequest(const std::string & path,bool is_incognito,int request_id)57   virtual void StartDataRequest(const std::string& path,
58                                 bool is_incognito,
59                                 int request_id) {
60     DictionaryValue localized_strings;
61     localized_strings.SetString("burnConfirmText1",
62         l10n_util::GetStringUTF16(IDS_IMAGEBURN_CONFIM_BURN1));
63     localized_strings.SetString("burnConfirmText2",
64         l10n_util::GetStringUTF16(IDS_IMAGEBURN_CONFIM_BURN2));
65     localized_strings.SetString("burnUnsuccessfulMessage",
66         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_UNSUCCESSFUL));
67     localized_strings.SetString("burnSuccessfulMessage",
68         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_SUCCESSFUL));
69     localized_strings.SetString("downloadAbortedMessage",
70         l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_UNSUCCESSFUL));
71     localized_strings.SetString("title",
72         l10n_util::GetStringUTF16(IDS_IMAGEBURN_TITLE));
73     localized_strings.SetString("listTitle",
74         l10n_util::GetStringUTF16(IDS_IMAGEBURN_ROOT_LIST_TITLE));
75     localized_strings.SetString("downloadStatusStart",
76         l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_STARTING_STATUS));
77     localized_strings.SetString("downloadStatusInProgress",
78         l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_IN_PROGRESS_STATUS));
79     localized_strings.SetString("downloadStatusComplete",
80         l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_COMPLETE_STATUS));
81     localized_strings.SetString("downloadStatusCanceled",
82         l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_CANCELED_STATUS));
83     localized_strings.SetString("burnStatusStart",
84         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_STARTING_STATUS));
85     localized_strings.SetString("burnStatusInProgress",
86         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_IN_PROGRESS_STATUS));
87     localized_strings.SetString("burnStatusComplete",
88         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_COMPLETE_STATUS));
89     localized_strings.SetString("burnStatusCanceled",
90         l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_CANCELED_STATUS));
91 
92     SetFontAndTextDirection(&localized_strings);
93 
94     static const base::StringPiece imageburn_html(
95         ResourceBundle::GetSharedInstance().GetRawDataResource(
96         IDR_IMAGEBURNER_HTML));
97     const std::string full_html = jstemplate_builder::GetI18nTemplateHtml(
98         imageburn_html, &localized_strings);
99 
100     scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
101     html_bytes->data.resize(full_html.size());
102     std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
103 
104     SendResponse(request_id, html_bytes);
105   }
106 
GetMimeType(const std::string &) const107   virtual std::string GetMimeType(const std::string&) const {
108     return "text/html";
109   }
110 
111  private:
~ImageBurnUIHTMLSource()112   virtual ~ImageBurnUIHTMLSource() {}
113 
114   DISALLOW_COPY_AND_ASSIGN(ImageBurnUIHTMLSource);
115 };
116 
117 ////////////////////////////////////////////////////////////////////////////////
118 //
119 // ImageBurnTaskProxy
120 //
121 ////////////////////////////////////////////////////////////////////////////////
122 
123 class ImageBurnTaskProxy
124   : public base::RefCountedThreadSafe<ImageBurnTaskProxy> {
125  public:
ImageBurnTaskProxy(const base::WeakPtr<ImageBurnHandler> & handler)126   explicit ImageBurnTaskProxy(const base::WeakPtr<ImageBurnHandler>& handler)
127       : handler_(handler) {}
128 
CreateImageDir()129   void CreateImageDir() {
130     if (handler_)
131       handler_->CreateImageDirOnFileThread(this);
132   }
133 
OnImageDirCreated(bool success)134   void OnImageDirCreated(bool success) {
135     if (handler_)
136       handler_->OnImageDirCreatedOnUIThread(success);
137   }
138 
BurnImage()139   void BurnImage() {
140     if (handler_)
141       handler_->BurnImageOnFileThread();
142     DeleteOnUIThread();
143   }
144 
UnzipImage()145   void UnzipImage() {
146     if (handler_)
147       handler_->UnzipImageOnFileThread(this);
148   }
149 
UnzipComplete(bool success)150   void UnzipComplete(bool success) {
151     if (handler_)
152       handler_->UnzipComplete(success);
153   }
154 
155   // ImageBurnTaskProxy is created on the UI thread, so in some cases,
156   // we need to post back to the UI thread for destruction.
DeleteOnUIThread()157   void DeleteOnUIThread() {
158     BrowserThread::PostTask(
159         BrowserThread::UI, FROM_HERE,
160         NewRunnableMethod(this, &ImageBurnTaskProxy::DoNothing));
161   }
162 
DoNothing()163   void DoNothing() {}
164 
165  private:
166   base::WeakPtr<ImageBurnHandler> handler_;
167 
168   friend class base::RefCountedThreadSafe<ImageBurnTaskProxy>;
~ImageBurnTaskProxy()169   ~ImageBurnTaskProxy() {}
170 
171   DISALLOW_COPY_AND_ASSIGN(ImageBurnTaskProxy);
172 };
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 //
176 // ImageBurnHandler
177 //
178 ////////////////////////////////////////////////////////////////////////////////
179 
ImageBurnHandler(TabContents * contents)180 ImageBurnHandler::ImageBurnHandler(TabContents* contents)
181     :tab_contents_(contents),
182      download_manager_(NULL),
183      download_item_observer_added_(false),
184      active_download_item_(NULL),
185      resource_manager_(NULL) {
186   chromeos::CrosLibrary::Get()->GetMountLibrary()->AddObserver(this);
187   chromeos::CrosLibrary::Get()->GetBurnLibrary()->AddObserver(this);
188   resource_manager_ = ImageBurnResourceManager::GetInstance();
189   zip_image_file_path_.clear();
190   image_file_path_.clear();
191   image_target_.clear();
192 }
193 
~ImageBurnHandler()194 ImageBurnHandler::~ImageBurnHandler() {
195   chromeos::CrosLibrary::Get()->GetMountLibrary()->RemoveObserver(this);
196   chromeos::CrosLibrary::Get()->GetBurnLibrary()->RemoveObserver(this);
197   if (active_download_item_)
198     active_download_item_->RemoveObserver(this);
199   if (download_manager_)
200       download_manager_->RemoveObserver(this);
201 }
202 
Attach(WebUI * web_ui)203 WebUIMessageHandler* ImageBurnHandler::Attach(WebUI* web_ui) {
204   return WebUIMessageHandler::Attach(web_ui);
205 }
206 
RegisterMessages()207 void ImageBurnHandler::RegisterMessages() {
208   web_ui_->RegisterMessageCallback("getRoots",
209       NewCallback(this, &ImageBurnHandler::HandleGetRoots));
210   web_ui_->RegisterMessageCallback("downloadImage",
211       NewCallback(this, &ImageBurnHandler::HandleDownloadImage));
212   web_ui_->RegisterMessageCallback("burnImage",
213       NewCallback(this, &ImageBurnHandler::HandleBurnImage));
214   web_ui_->RegisterMessageCallback("cancelBurnImage",
215       NewCallback(this, &ImageBurnHandler::HandleCancelBurnImage));
216 }
217 
DiskChanged(chromeos::MountLibraryEventType event,const chromeos::MountLibrary::Disk * disk)218 void ImageBurnHandler::DiskChanged(chromeos::MountLibraryEventType event,
219                                    const chromeos::MountLibrary::Disk* disk) {
220   if (event == chromeos::MOUNT_DISK_REMOVED ||
221       event == chromeos::MOUNT_DISK_CHANGED ||
222       event == chromeos::MOUNT_DISK_UNMOUNTED) {
223     web_ui_->CallJavascriptFunction("rootsChanged");
224   }
225 }
226 
DeviceChanged(chromeos::MountLibraryEventType event,const std::string & device_path)227 void ImageBurnHandler::DeviceChanged(chromeos::MountLibraryEventType event,
228                                      const std::string& device_path) {
229   if (event == chromeos::MOUNT_DEVICE_REMOVED)
230     web_ui_->CallJavascriptFunction("rootsChanged");
231 }
232 
233 
ProgressUpdated(chromeos::BurnLibrary * object,chromeos::BurnEventType evt,const ImageBurnStatus & status)234 void ImageBurnHandler::ProgressUpdated(chromeos::BurnLibrary* object,
235                                        chromeos::BurnEventType evt,
236                                        const ImageBurnStatus& status) {
237   UpdateBurnProgress(status.amount_burnt, status.total_size,
238                      status.target_path, evt);
239   if (evt == chromeos::BURN_COMPLETE) {
240     FinalizeBurn(true);
241   } else if (evt == chromeos::BURN_CANCELED) {
242     FinalizeBurn(false);
243   }
244 }
245 
OnDownloadUpdated(DownloadItem * download)246 void ImageBurnHandler::OnDownloadUpdated(DownloadItem* download) {
247   if (download->IsCancelled()) {
248     DownloadCompleted(false);  // Should stop observation.
249     DCHECK(!download_item_observer_added_);
250   } else if (download->IsComplete()) {
251     zip_image_file_path_ = download->full_path();
252     DownloadCompleted(true);  // Should stop observation.
253     DCHECK(!download_item_observer_added_);
254   } else if (download->IsPartialDownload()) {
255       scoped_ptr<DictionaryValue> result_value(
256           download_util::CreateDownloadItemValue(download, 0));
257       web_ui_->CallJavascriptFunction("downloadUpdated", *result_value);
258   }
259 }
260 
OnDownloadOpened(DownloadItem * download)261 void ImageBurnHandler::OnDownloadOpened(DownloadItem* download) {
262   if (download->safety_state() == DownloadItem::DANGEROUS)
263     download->DangerousDownloadValidated();
264 }
265 
ModelChanged()266 void ImageBurnHandler::ModelChanged() {
267   // Find our item and observe it.
268   std::vector<DownloadItem*> downloads;
269   download_manager_->GetTemporaryDownloads(
270       resource_manager_->GetImageDir(), &downloads);
271   if (download_item_observer_added_)
272     return;
273   for (std::vector<DownloadItem*>::const_iterator it = downloads.begin();
274       it != downloads.end();
275       ++it) {
276     if ((*it)->original_url() == *image_download_url_) {
277       download_item_observer_added_ = true;
278       (*it)->AddObserver(this);
279       active_download_item_ = *it;
280       break;
281     }
282   }
283 }
284 
OnImageDirCreated(bool success,ImageBurnTaskProxy * task)285 void ImageBurnHandler::OnImageDirCreated(bool success,
286                                          ImageBurnTaskProxy* task) {
287   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
288   // Transfer to UI thread.
289   BrowserThread::PostTask(
290       BrowserThread::UI, FROM_HERE,
291       NewRunnableMethod(task, &ImageBurnTaskProxy::OnImageDirCreated,
292                         success));
293 }
294 
OnDownloadStarted(bool success)295 void ImageBurnHandler::OnDownloadStarted(bool success) {
296   if (success)
297     resource_manager_->set_download_started(true);
298   else
299     DownloadCompleted(false);
300 }
301 
HandleGetRoots(const ListValue * args)302 void ImageBurnHandler::HandleGetRoots(const ListValue* args) {
303   ListValue results_value;
304   DictionaryValue info_value;
305   chromeos::MountLibrary* mount_lib =
306       chromeos::CrosLibrary::Get()->GetMountLibrary();
307   const chromeos::MountLibrary::DiskMap& disks = mount_lib->disks();
308   if (!resource_manager_->burn_in_progress()) {
309     for (chromeos::MountLibrary::DiskMap::const_iterator iter =  disks.begin();
310          iter != disks.end();
311          ++iter) {
312       if (iter->second->is_parent()) {
313         FilePath disk_path = FilePath(iter->second->system_path()).DirName();
314         std::string title = "/dev/" + disk_path.BaseName().value();
315         if (!iter->second->on_boot_device()) {
316           DictionaryValue* page_value = new DictionaryValue();
317           page_value->SetString(std::string(kPropertyTitle), title);
318           page_value->SetString(std::string(kPropertyPath), title);
319           page_value->SetBoolean(std::string(kPropertyDirectory), true);
320           results_value.Append(page_value);
321         }
322       }
323     }
324   }
325 
326   info_value.SetString("functionCall", "getRoots");
327   info_value.SetString(std::string(kPropertyPath), "");
328   web_ui_->CallJavascriptFunction("browseFileResult",
329                                   info_value, results_value);
330 }
331 
HandleDownloadImage(const ListValue * args)332 void ImageBurnHandler::HandleDownloadImage(const ListValue* args) {
333   ExtractTargetedDeviceSystemPath(args);
334   if (resource_manager_->GetImageDir().empty()) {
335     // Create image dir on File thread.
336     scoped_refptr<ImageBurnTaskProxy> task =
337         new ImageBurnTaskProxy(AsWeakPtr());
338     BrowserThread::PostTask(
339         BrowserThread::FILE, FROM_HERE,
340         NewRunnableMethod(task.get(), &ImageBurnTaskProxy::CreateImageDir));
341   } else {
342     OnImageDirCreatedOnUIThread(true);
343   }
344 }
345 
DownloadCompleted(bool success)346 void ImageBurnHandler::DownloadCompleted(bool success) {
347   resource_manager_->SetDownloadFinished(success);
348   if (active_download_item_) {
349     active_download_item_->RemoveObserver(this);
350     active_download_item_ = NULL;
351   }
352   download_item_observer_added_ = false;
353   if (download_manager_)
354     download_manager_->RemoveObserver(this);
355 
356   if (success) {
357     UnzipImage();
358   } else {
359     UnzipComplete(success);
360   }
361 }
362 
UnzipComplete(bool success)363 void ImageBurnHandler::UnzipComplete(bool success) {
364   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
365 
366   DictionaryValue signal_value;
367   if (success) {
368     signal_value.SetString("state", "COMPLETE");
369     web_ui_->CallJavascriptFunction("downloadUpdated", signal_value);
370     web_ui_->CallJavascriptFunction("promptUserDownloadFinished");
371   } else {
372     signal_value.SetString("state", "CANCELLED");
373     web_ui_->CallJavascriptFunction("downloadUpdated", signal_value);
374     web_ui_->CallJavascriptFunction("alertUserDownloadAborted");
375   }
376 }
377 
HandleBurnImage(const ListValue * args)378 void ImageBurnHandler::HandleBurnImage(const ListValue* args) {
379   scoped_refptr<ImageBurnTaskProxy> task = new ImageBurnTaskProxy(AsWeakPtr());
380   BrowserThread::PostTask(
381         BrowserThread::FILE, FROM_HERE,
382         NewRunnableMethod(task.get(), &ImageBurnTaskProxy::BurnImage));
383 }
384 
HandleCancelBurnImage(const ListValue * args)385 void ImageBurnHandler::HandleCancelBurnImage(const ListValue* args) {
386   image_target_.clear();
387 }
388 
CreateImageDirOnFileThread(ImageBurnTaskProxy * task)389 void ImageBurnHandler::CreateImageDirOnFileThread(ImageBurnTaskProxy* task) {
390   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
391 
392   resource_manager_->CreateImageDir(this, task);
393 }
394 
OnImageDirCreatedOnUIThread(bool success)395 void ImageBurnHandler::OnImageDirCreatedOnUIThread(bool success) {
396   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
397   if (success) {
398     zip_image_file_path_ =
399         resource_manager_->GetImageDir().Append(kImageFileName);
400     resource_manager_->CreateImageUrl(tab_contents_, this);
401   } else {
402     DownloadCompleted(success);
403   }
404 }
405 
BurnImageOnFileThread()406 void ImageBurnHandler::BurnImageOnFileThread() {
407   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
408 
409   if (resource_manager_->burn_in_progress())
410     return;
411   resource_manager_->set_burn_in_progress(true);
412 
413   if (chromeos::CrosLibrary::Get()->GetBurnLibrary()->
414       DoBurn(image_file_path_, image_target_)) {
415     DictionaryValue signal_value;
416     signal_value.SetString("state", "IN_PROGRESS");
417     signal_value.SetString("path", image_target_.value());
418     signal_value.SetInteger("received", 0);
419     signal_value.SetString("progress_status_text", "");
420     web_ui_->CallJavascriptFunction("burnProgressUpdated", signal_value);
421   }
422 }
423 
FinalizeBurn(bool successful)424 void ImageBurnHandler::FinalizeBurn(bool successful) {
425   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
426   web_ui_->CallJavascriptFunction(successful ? "burnSuccessful"
427                                              : "burnUnsuccessful");
428   resource_manager_->set_burn_in_progress(false);
429 }
430 
UpdateBurnProgress(int64 total_burnt,int64 image_size,const std::string & path,chromeos::BurnEventType event)431 void ImageBurnHandler::UpdateBurnProgress(int64 total_burnt,
432                                           int64 image_size,
433                                           const std::string& path,
434                                           chromeos::BurnEventType event) {
435   DictionaryValue progress_value;
436   progress_value.SetString("progress_status_text",
437       GetBurnProgressText(total_burnt, image_size));
438   if (event == chromeos::BURN_UPDATED)
439     progress_value.SetString("state", "IN_PROGRESS");
440   else if (event == chromeos::BURN_CANCELED)
441     progress_value.SetString("state", "CANCELLED");
442   else if (event == chromeos::BURN_COMPLETE)
443     progress_value.SetString("state", "COMPLETE");
444   progress_value.SetInteger("received", total_burnt);
445   progress_value.SetInteger("total", image_size);
446   progress_value.SetString("path", path);
447 
448   web_ui_->CallJavascriptFunction("burnProgressUpdated", progress_value);
449 }
450 
GetBurnProgressText(int64 total_burnt,int64 image_size)451 string16 ImageBurnHandler::GetBurnProgressText(int64 total_burnt,
452                                                int64 image_size) {
453   DataUnits amount_units = GetByteDisplayUnits(total_burnt);
454   string16 burnt_size = FormatBytes(total_burnt, amount_units, true);
455 
456   base::i18n::AdjustStringForLocaleDirection(&burnt_size);
457 
458   if (image_size) {
459     amount_units = GetByteDisplayUnits(image_size);
460     string16 total_text = FormatBytes(image_size, amount_units, true);
461     base::i18n::AdjustStringForLocaleDirection(&total_text);
462 
463     return l10n_util::GetStringFUTF16(IDS_IMAGEBURN_BURN_PROGRESS,
464                                       burnt_size,
465                                       total_text);
466   } else {
467     return l10n_util::GetStringFUTF16(IDS_IMAGEBURN_BURN_PROGRESS_SIZE_UNKNOWN,
468                                       burnt_size);
469   }
470 }
471 
OnImageUrlCreated(GURL * image_url,bool success)472 void ImageBurnHandler::OnImageUrlCreated(GURL* image_url, bool success) {
473   if (!success) {
474     DownloadCompleted(false);
475     return;
476   }
477   image_download_url_ = image_url;
478 
479   download_manager_ = tab_contents_->profile()->GetDownloadManager();
480   download_manager_->AddObserver(this);
481 
482   if (!resource_manager_->download_started()) {
483     resource_manager_->set_download_started(true);
484     if (!resource_manager_->image_download_requested()) {
485       resource_manager_->set_image_download_requested(true);
486       ImageBurnDownloader::GetInstance()->AddListener(this,
487           *image_download_url_);
488       ImageBurnDownloader::GetInstance()->DownloadFile(*image_download_url_,
489                                                        zip_image_file_path_,
490                                                        tab_contents_);
491     }
492   } else if (resource_manager_->download_finished()) {
493     DownloadCompleted(true);
494   }
495 }
496 
ExtractTargetedDeviceSystemPath(const ListValue * list_value)497 void ImageBurnHandler::ExtractTargetedDeviceSystemPath(
498     const ListValue* list_value) {
499   Value* list_member;
500   if (list_value->Get(0, &list_member) &&
501       list_member->GetType() == Value::TYPE_STRING) {
502     const StringValue* string_value =
503         static_cast<const StringValue*>(list_member);
504     std::string image_dest;
505     string_value->GetAsString(&image_dest);
506     image_target_ = FilePath(image_dest);
507   } else {
508     LOG(ERROR) << "Unable to get path string";
509   }
510 }
511 
UnzipImage()512 void ImageBurnHandler::UnzipImage() {
513   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
514 
515   scoped_refptr<ImageBurnTaskProxy> task = new ImageBurnTaskProxy(AsWeakPtr());
516   BrowserThread::PostTask(
517         BrowserThread::FILE, FROM_HERE,
518         NewRunnableMethod(task.get(), &ImageBurnTaskProxy::UnzipImage));
519 }
520 
UnzipImageOnFileThread(ImageBurnTaskProxy * task)521 void ImageBurnHandler::UnzipImageOnFileThread(ImageBurnTaskProxy* task) {
522   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
523 
524   bool success = UnzipImageImpl();
525   BrowserThread::PostTask(
526         BrowserThread::UI, FROM_HERE,
527         NewRunnableMethod(task, &ImageBurnTaskProxy::UnzipComplete, success));
528 }
529 
UnzipImageImpl()530 bool ImageBurnHandler::UnzipImageImpl() {
531   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
532 
533   const FilePath& img_dir = resource_manager_->GetImageDir();
534   if (!Unzip(zip_image_file_path_, img_dir))
535     return false;
536 
537   image_file_path_.clear();
538   file_util::FileEnumerator file_enumerator(
539       img_dir, false,  // recursive
540       file_util::FileEnumerator::FILES);
541   for (FilePath path = file_enumerator.Next();
542       !path.empty();
543        path = file_enumerator.Next()) {
544     if (path != zip_image_file_path_) {
545       image_file_path_ = path;
546       break;
547     }
548   }
549   return !image_file_path_.empty();
550 }
551 
552 ////////////////////////////////////////////////////////////////////////////////
553 //
554 // ImageBurnResourceManager
555 //
556 ////////////////////////////////////////////////////////////////////////////////
557 
ImageBurnResourceManager()558 ImageBurnResourceManager::ImageBurnResourceManager()
559     : image_download_requested_(false),
560       download_started_(false),
561       download_finished_(false),
562       burn_in_progress_(false),
563       download_manager_(NULL),
564       download_item_observer_added_(false),
565       active_download_item_(NULL),
566       image_url_(new GURL(kImageDownloadURL)),
567       config_file_url_(std::string(kImageBaseURL) + kImageFetcherName),
568       config_file_requested_(false),
569       config_file_fetched_(true) {
570   image_dir_.clear();
571 }
572 
~ImageBurnResourceManager()573 ImageBurnResourceManager::~ImageBurnResourceManager() {
574   if (!image_dir_.empty()) {
575     file_util::Delete(image_dir_, true);
576   }
577   if (active_download_item_)
578     active_download_item_->RemoveObserver(this);
579   if (download_manager_)
580     download_manager_->RemoveObserver(this);
581 }
582 
583 // static
GetInstance()584 ImageBurnResourceManager* ImageBurnResourceManager::GetInstance() {
585   return Singleton<ImageBurnResourceManager>::get();
586 }
587 
OnDownloadUpdated(DownloadItem * download)588 void ImageBurnResourceManager::OnDownloadUpdated(DownloadItem* download) {
589   if (download->IsCancelled()) {
590     image_url_.reset();
591     ConfigFileFetched(false);
592 
593     // ConfigFileFetched should remove observer.
594     DCHECK(!download_item_observer_added_);
595     DCHECK(active_download_item_ == NULL);
596   } else if (download->IsComplete()) {
597     std::string image_url;
598     if (file_util::ReadFileToString(config_file_path_, &image_url)) {
599       image_url_.reset(new GURL(std::string(kImageBaseURL) + image_url));
600       ConfigFileFetched(true);
601     } else {
602       image_url_.reset();
603       ConfigFileFetched(false);
604     }
605   }
606 }
607 
ModelChanged()608 void ImageBurnResourceManager::ModelChanged() {
609   std::vector<DownloadItem*> downloads;
610   download_manager_->GetTemporaryDownloads(GetImageDir(), &downloads);
611   if (download_item_observer_added_)
612     return;
613   for (std::vector<DownloadItem*>::const_iterator it = downloads.begin();
614       it != downloads.end();
615       ++it) {
616     if ((*it)->url() == config_file_url_) {
617       download_item_observer_added_ = true;
618       (*it)->AddObserver(this);
619       active_download_item_ = *it;
620       break;
621     }
622   }
623 }
624 
OnDownloadStarted(bool success)625 void ImageBurnResourceManager::OnDownloadStarted(bool success) {
626   if (!success)
627     ConfigFileFetched(false);
628 }
629 
CreateImageDir(Delegate * delegate,ImageBurnTaskProxy * task)630 void ImageBurnResourceManager::CreateImageDir(
631     Delegate* delegate,
632     ImageBurnTaskProxy* task) {
633   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
634 
635   bool success = true;
636   if (image_dir_.empty()) {
637     CHECK(PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &image_dir_));
638     image_dir_ = image_dir_.Append(kTempImageFolderName);
639     success = file_util::CreateDirectory(image_dir_);
640   }
641   delegate->OnImageDirCreated(success, task);
642 }
643 
GetImageDir()644 const FilePath& ImageBurnResourceManager::GetImageDir() {
645   return image_dir_;
646 }
647 
SetDownloadFinished(bool finished)648 void ImageBurnResourceManager::SetDownloadFinished(bool finished) {
649   if (!download_started_)
650     return;
651   if (!finished)
652     download_started_ = false;
653   download_finished_ = finished;
654 }
655 
CreateImageUrl(TabContents * tab_contents,Delegate * delegate)656 void ImageBurnResourceManager::CreateImageUrl(TabContents* tab_contents,
657     Delegate* delegate) {
658   if (config_file_fetched_) {
659     delegate->OnImageUrlCreated(image_url_.get(), true);
660     return;
661   }
662   downloaders_.push_back(delegate);
663 
664   if (config_file_requested_)
665     return;
666   config_file_requested_ = true;
667 
668   config_file_path_ = GetImageDir().Append(kImageFetcherName);
669 
670   download_manager_ = tab_contents->profile()->GetDownloadManager();
671   download_manager_->AddObserver(this);
672 
673   ImageBurnDownloader* downloader = ImageBurnDownloader::GetInstance();
674   downloader->AddListener(this, config_file_url_);
675   downloader->DownloadFile(config_file_url_, config_file_path_, tab_contents);
676 }
677 
ConfigFileFetched(bool fetched)678 void ImageBurnResourceManager::ConfigFileFetched(bool fetched) {
679   if (active_download_item_) {
680     active_download_item_->RemoveObserver(this);
681     active_download_item_ = NULL;
682   }
683   download_item_observer_added_ = false;
684   if (download_manager_)
685     download_manager_->RemoveObserver(this);
686   if (!fetched)
687     config_file_requested_ = false;
688   config_file_fetched_ = fetched;
689   for (size_t i = 0; i < downloaders_.size(); ++i)
690     downloaders_[i]->OnImageUrlCreated(image_url_.get(), fetched);
691   downloaders_.clear();
692 }
693 
694 ////////////////////////////////////////////////////////////////////////////////
695 //
696 // ImageBurnDownloaderTaskProxy
697 //
698 ////////////////////////////////////////////////////////////////////////////////
699 
700 class ImageBurnDownloaderTaskProxy
701     : public base::RefCountedThreadSafe<ImageBurnDownloaderTaskProxy> {
702  public:
ImageBurnDownloaderTaskProxy()703   explicit ImageBurnDownloaderTaskProxy() {}
704 
CreateFileStream(const GURL & url,const FilePath & target_path,TabContents * tab_contents)705   void CreateFileStream(const GURL& url,
706                         const FilePath& target_path,
707                         TabContents* tab_contents) {
708     ImageBurnDownloader::GetInstance()->CreateFileStreamOnFileThread(url,
709         target_path, tab_contents, this);
710   }
711 
OnFileStreamCreated(const GURL & url,const FilePath & file_path,TabContents * tab_contents,net::FileStream * created_file_stream)712   void OnFileStreamCreated(const GURL& url,
713                            const FilePath& file_path,
714                            TabContents* tab_contents,
715                            net::FileStream* created_file_stream) {
716     ImageBurnDownloader::GetInstance()->OnFileStreamCreatedOnUIThread(url,
717         file_path, tab_contents, created_file_stream);
718   }
719 
720  private:
~ImageBurnDownloaderTaskProxy()721   ~ImageBurnDownloaderTaskProxy() {}
722 
723   friend class base::RefCountedThreadSafe<ImageBurnDownloaderTaskProxy>;
724 
725   DISALLOW_COPY_AND_ASSIGN(ImageBurnDownloaderTaskProxy);
726 };
727 
728 ////////////////////////////////////////////////////////////////////////////////
729 //
730 // ImageBurnDownloader
731 //
732 ////////////////////////////////////////////////////////////////////////////////
733 
734 // static
GetInstance()735 ImageBurnDownloader* ImageBurnDownloader::GetInstance() {
736   return Singleton<ImageBurnDownloader>::get();
737 }
738 
DownloadFile(const GURL & url,const FilePath & file_path,TabContents * tab_contents)739 void ImageBurnDownloader::DownloadFile(const GURL& url,
740     const FilePath& file_path, TabContents* tab_contents) {
741   // First we have to create file stream we will download file to.
742   // That has to be done on File thread.
743   scoped_refptr<ImageBurnDownloaderTaskProxy> task =
744       new ImageBurnDownloaderTaskProxy();
745   BrowserThread::PostTask(
746       BrowserThread::FILE, FROM_HERE,
747       NewRunnableMethod(task.get(),
748           &ImageBurnDownloaderTaskProxy::CreateFileStream, url, file_path,
749           tab_contents));
750 }
751 
CreateFileStreamOnFileThread(const GURL & url,const FilePath & file_path,TabContents * tab_contents,ImageBurnDownloaderTaskProxy * task)752 void ImageBurnDownloader::CreateFileStreamOnFileThread(
753     const GURL& url, const FilePath& file_path,
754     TabContents* tab_contents, ImageBurnDownloaderTaskProxy* task) {
755 
756   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
757   DCHECK(!file_path.empty());
758 
759   scoped_ptr<net::FileStream> file_stream(new net::FileStream);
760   if (file_stream->Open(file_path, base::PLATFORM_FILE_CREATE_ALWAYS |
761                         base::PLATFORM_FILE_WRITE))
762     file_stream.reset(NULL);
763 
764   // Call callback method on UI thread.
765   BrowserThread::PostTask(
766         BrowserThread::UI, FROM_HERE,
767         NewRunnableMethod(task,
768             &ImageBurnDownloaderTaskProxy::OnFileStreamCreated,
769             url, file_path, tab_contents, file_stream.release()));
770 }
771 
OnFileStreamCreatedOnUIThread(const GURL & url,const FilePath & file_path,TabContents * tab_contents,net::FileStream * created_file_stream)772 void ImageBurnDownloader::OnFileStreamCreatedOnUIThread(const GURL& url,
773     const FilePath& file_path, TabContents* tab_contents,
774     net::FileStream* created_file_stream) {
775   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
776 
777   if (created_file_stream) {
778     DownloadManager* download_manager =
779         tab_contents->profile()->GetDownloadManager();
780     DownloadSaveInfo save_info;
781     save_info.file_path = file_path;
782     save_info.file_stream = linked_ptr<net::FileStream>(created_file_stream);
783     DownloadStarted(true, url);
784     download_manager->DownloadUrlToFile(url,
785                                         tab_contents->GetURL(),
786                                         tab_contents->encoding(),
787                                         save_info,
788                                         tab_contents);
789   } else {
790     DownloadStarted(false, url);
791   }
792 }
793 
AddListener(Listener * listener,const GURL & url)794 void ImageBurnDownloader::AddListener(Listener* listener, const GURL& url) {
795   listeners_.insert(std::make_pair(url, listener));
796 }
797 
DownloadStarted(bool success,const GURL & url)798 void ImageBurnDownloader::DownloadStarted(bool success, const GURL& url) {
799   std::pair<ListenerMap::iterator, ListenerMap::iterator> listener_range =
800       listeners_.equal_range(url);
801   for (ListenerMap::iterator current_listener = listener_range.first;
802        current_listener != listener_range.second;
803        ++current_listener) {
804     current_listener->second->OnDownloadStarted(success);
805   }
806   listeners_.erase(listener_range.first, listener_range.second);
807 }
808 
809 ////////////////////////////////////////////////////////////////////////////////
810 //
811 // ImageBurnUI
812 //
813 ////////////////////////////////////////////////////////////////////////////////
ImageBurnUI(TabContents * contents)814 ImageBurnUI::ImageBurnUI(TabContents* contents) : WebUI(contents) {
815   ImageBurnHandler* handler = new ImageBurnHandler(contents);
816   AddMessageHandler((handler)->Attach(this));
817   ImageBurnUIHTMLSource* html_source = new ImageBurnUIHTMLSource();
818   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
819 }
820