Home | History | Annotate | Download | only in chromeos
      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:
     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.
     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 
    107   virtual std::string GetMimeType(const std::string&) const {
    108     return "text/html";
    109   }
    110 
    111  private:
    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:
    126   explicit ImageBurnTaskProxy(const base::WeakPtr<ImageBurnHandler>& handler)
    127       : handler_(handler) {}
    128 
    129   void CreateImageDir() {
    130     if (handler_)
    131       handler_->CreateImageDirOnFileThread(this);
    132   }
    133 
    134   void OnImageDirCreated(bool success) {
    135     if (handler_)
    136       handler_->OnImageDirCreatedOnUIThread(success);
    137   }
    138 
    139   void BurnImage() {
    140     if (handler_)
    141       handler_->BurnImageOnFileThread();
    142     DeleteOnUIThread();
    143   }
    144 
    145   void UnzipImage() {
    146     if (handler_)
    147       handler_->UnzipImageOnFileThread(this);
    148   }
    149 
    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.
    157   void DeleteOnUIThread() {
    158     BrowserThread::PostTask(
    159         BrowserThread::UI, FROM_HERE,
    160         NewRunnableMethod(this, &ImageBurnTaskProxy::DoNothing));
    161   }
    162 
    163   void DoNothing() {}
    164 
    165  private:
    166   base::WeakPtr<ImageBurnHandler> handler_;
    167 
    168   friend class base::RefCountedThreadSafe<ImageBurnTaskProxy>;
    169   ~ImageBurnTaskProxy() {}
    170 
    171   DISALLOW_COPY_AND_ASSIGN(ImageBurnTaskProxy);
    172 };
    173 
    174 ////////////////////////////////////////////////////////////////////////////////
    175 //
    176 // ImageBurnHandler
    177 //
    178 ////////////////////////////////////////////////////////////////////////////////
    179 
    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 
    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 
    203 WebUIMessageHandler* ImageBurnHandler::Attach(WebUI* web_ui) {
    204   return WebUIMessageHandler::Attach(web_ui);
    205 }
    206 
    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 
    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 
    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 
    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 
    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 
    261 void ImageBurnHandler::OnDownloadOpened(DownloadItem* download) {
    262   if (download->safety_state() == DownloadItem::DANGEROUS)
    263     download->DangerousDownloadValidated();
    264 }
    265 
    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 
    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 
    295 void ImageBurnHandler::OnDownloadStarted(bool success) {
    296   if (success)
    297     resource_manager_->set_download_started(true);
    298   else
    299     DownloadCompleted(false);
    300 }
    301 
    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 
    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 
    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 
    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 
    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 
    385 void ImageBurnHandler::HandleCancelBurnImage(const ListValue* args) {
    386   image_target_.clear();
    387 }
    388 
    389 void ImageBurnHandler::CreateImageDirOnFileThread(ImageBurnTaskProxy* task) {
    390   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    391 
    392   resource_manager_->CreateImageDir(this, task);
    393 }
    394 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    584 ImageBurnResourceManager* ImageBurnResourceManager::GetInstance() {
    585   return Singleton<ImageBurnResourceManager>::get();
    586 }
    587 
    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 
    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 
    625 void ImageBurnResourceManager::OnDownloadStarted(bool success) {
    626   if (!success)
    627     ConfigFileFetched(false);
    628 }
    629 
    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 
    644 const FilePath& ImageBurnResourceManager::GetImageDir() {
    645   return image_dir_;
    646 }
    647 
    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 
    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 
    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:
    703   explicit ImageBurnDownloaderTaskProxy() {}
    704 
    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 
    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:
    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
    735 ImageBurnDownloader* ImageBurnDownloader::GetInstance() {
    736   return Singleton<ImageBurnDownloader>::get();
    737 }
    738 
    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 
    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 
    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 
    794 void ImageBurnDownloader::AddListener(Listener* listener, const GURL& url) {
    795   listeners_.insert(std::make_pair(url, listener));
    796 }
    797 
    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 ////////////////////////////////////////////////////////////////////////////////
    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