Home | History | Annotate | Download | only in imageburner
      1 // Copyright (c) 2012 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/imageburner_ui.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_path.h"
      9 #include "base/i18n/rtl.h"
     10 #include "base/strings/string16.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/chromeos/imageburner/burn_controller.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/common/url_constants.h"
     16 #include "chrome/grit/generated_resources.h"
     17 #include "content/public/browser/web_ui.h"
     18 #include "content/public/browser/web_ui_data_source.h"
     19 #include "content/public/browser/web_ui_message_handler.h"
     20 #include "grit/browser_resources.h"
     21 #include "ui/base/l10n/l10n_util.h"
     22 #include "ui/base/l10n/time_format.h"
     23 #include "ui/base/text/bytes_formatting.h"
     24 #include "url/gurl.h"
     25 
     26 namespace chromeos {
     27 namespace imageburner {
     28 
     29 namespace {
     30 
     31 const char kPropertyDevicePath[] = "devicePath";
     32 const char kPropertyFilePath[] = "filePath";
     33 const char kPropertyLabel[] = "label";
     34 const char kPropertyDeviceType[] = "type";
     35 
     36 // Link displayed on imageburner ui.
     37 const char kMoreInfoLink[] =
     38     "http://www.chromium.org/chromium-os/chromiumos-design-docs/recovery-mode";
     39 
     40 content::WebUIDataSource* CreateImageburnerUIHTMLSource() {
     41   content::WebUIDataSource* source =
     42       content::WebUIDataSource::Create(chrome::kChromeUIImageBurnerHost);
     43 
     44   source->AddLocalizedString("headerTitle", IDS_IMAGEBURN_HEADER_TITLE);
     45   source->AddLocalizedString("headerDescription",
     46                              IDS_IMAGEBURN_HEADER_DESCRIPTION);
     47   source->AddLocalizedString("headerLink", IDS_IMAGEBURN_HEADER_LINK);
     48   source->AddLocalizedString("statusDevicesNone",
     49                              IDS_IMAGEBURN_NO_DEVICES_STATUS);
     50   source->AddLocalizedString("warningDevicesNone",
     51                              IDS_IMAGEBURN_NO_DEVICES_WARNING);
     52   source->AddLocalizedString("statusDevicesMultiple",
     53                              IDS_IMAGEBURN_MUL_DEVICES_STATUS);
     54   source->AddLocalizedString("statusDeviceUSB",
     55                              IDS_IMAGEBURN_USB_DEVICE_STATUS);
     56   source->AddLocalizedString("statusDeviceSD",
     57                              IDS_IMAGEBURN_SD_DEVICE_STATUS);
     58   source->AddLocalizedString("warningDevices",
     59                              IDS_IMAGEBURN_DEVICES_WARNING);
     60   source->AddLocalizedString("statusNoConnection",
     61                              IDS_IMAGEBURN_NO_CONNECTION_STATUS);
     62   source->AddLocalizedString("warningNoConnection",
     63                              IDS_IMAGEBURN_NO_CONNECTION_WARNING);
     64   source->AddLocalizedString("statusNoSpace",
     65                              IDS_IMAGEBURN_INSUFFICIENT_SPACE_STATUS);
     66   source->AddLocalizedString("warningNoSpace",
     67                              IDS_IMAGEBURN_INSUFFICIENT_SPACE_WARNING);
     68   source->AddLocalizedString("statusDownloading",
     69                              IDS_IMAGEBURN_DOWNLOADING_STATUS);
     70   source->AddLocalizedString("statusUnzip", IDS_IMAGEBURN_UNZIP_STATUS);
     71   source->AddLocalizedString("statusBurn", IDS_IMAGEBURN_BURN_STATUS);
     72   source->AddLocalizedString("statusError", IDS_IMAGEBURN_ERROR_STATUS);
     73   source->AddLocalizedString("statusSuccess", IDS_IMAGEBURN_SUCCESS_STATUS);
     74   source->AddLocalizedString("warningSuccess", IDS_IMAGEBURN_SUCCESS_DESC);
     75   source->AddLocalizedString("title", IDS_IMAGEBURN_PAGE_TITLE);
     76   source->AddLocalizedString("confirmButton", IDS_IMAGEBURN_CONFIRM_BUTTON);
     77   source->AddLocalizedString("cancelButton", IDS_IMAGEBURN_CANCEL_BUTTON);
     78   source->AddLocalizedString("retryButton", IDS_IMAGEBURN_RETRY_BUTTON);
     79   source->AddString("moreInfoLink", base::ASCIIToUTF16(kMoreInfoLink));
     80 
     81   source->SetUseJsonJSFormatV2();
     82   source->SetJsonPath("strings.js");
     83   source->AddResourcePath("image_burner.js", IDR_IMAGEBURNER_JS);
     84   source->SetDefaultResource(IDR_IMAGEBURNER_HTML);
     85   return source;
     86 }
     87 
     88 class WebUIHandler
     89     : public content::WebUIMessageHandler,
     90       public BurnController::Delegate {
     91  public:
     92   explicit WebUIHandler(content::WebContents* contents)
     93       : burn_controller_(BurnController::CreateBurnController(contents, this)) {
     94   }
     95 
     96   virtual ~WebUIHandler() {
     97   }
     98 
     99   // WebUIMessageHandler implementation.
    100   virtual void RegisterMessages() OVERRIDE {
    101     web_ui()->RegisterMessageCallback(
    102         "getDevices",
    103         base::Bind(&WebUIHandler::HandleGetDevices, base::Unretained(this)));
    104     web_ui()->RegisterMessageCallback(
    105         "burnImage",
    106         base::Bind(&WebUIHandler::HandleBurnImage, base::Unretained(this)));
    107     web_ui()->RegisterMessageCallback(
    108         "cancelBurnImage",
    109         base::Bind(&WebUIHandler::HandleCancelBurnImage,
    110                    base::Unretained(this)));
    111     web_ui()->RegisterMessageCallback(
    112         "webuiInitialized",
    113         base::Bind(&WebUIHandler::HandleWebUIInitialized,
    114                    base::Unretained(this)));
    115   }
    116 
    117   // BurnController::Delegate override.
    118   virtual void OnSuccess() OVERRIDE {
    119     web_ui()->CallJavascriptFunction("browserBridge.reportSuccess");
    120   }
    121 
    122   // BurnController::Delegate override.
    123   virtual void OnFail(int error_message_id) OVERRIDE {
    124     base::StringValue error_message(
    125         l10n_util::GetStringUTF16(error_message_id));
    126     web_ui()->CallJavascriptFunction("browserBridge.reportFail", error_message);
    127   }
    128 
    129   // BurnController::Delegate override.
    130   virtual void OnDeviceAdded(const disks::DiskMountManager::Disk& disk)
    131       OVERRIDE {
    132     base::DictionaryValue disk_value;
    133     CreateDiskValue(disk, &disk_value);
    134     web_ui()->CallJavascriptFunction("browserBridge.deviceAdded", disk_value);
    135   }
    136 
    137   // BurnController::Delegate override.
    138   virtual void OnDeviceRemoved(const disks::DiskMountManager::Disk& disk)
    139       OVERRIDE {
    140     base::StringValue device_path_value(disk.device_path());
    141     web_ui()->CallJavascriptFunction("browserBridge.deviceRemoved",
    142                                      device_path_value);
    143   }
    144 
    145   // BurnController::Delegate override.
    146   virtual void OnDeviceTooSmall(int64 device_size) OVERRIDE {
    147     base::string16 size;
    148     GetDataSizeText(device_size, &size);
    149     base::StringValue device_size_text(size);
    150     web_ui()->CallJavascriptFunction("browserBridge.reportDeviceTooSmall",
    151                                      device_size_text);
    152   }
    153 
    154   // BurnController::Delegate override.
    155   virtual void OnProgress(ProgressType progress_type,
    156                           int64 amount_finished,
    157                           int64 amount_total) OVERRIDE {
    158     const base::string16 time_remaining_text =
    159         l10n_util::GetStringUTF16(IDS_IMAGEBURN_PROGRESS_TIME_UNKNOWN);
    160     SendProgressSignal(progress_type, amount_finished, amount_total,
    161                        time_remaining_text);
    162   }
    163 
    164   // BurnController::Delegate override.
    165   virtual void OnProgressWithRemainingTime(
    166       ProgressType progress_type,
    167       int64 amount_finished,
    168       int64 amount_total,
    169       const base::TimeDelta& time_remaining) OVERRIDE {
    170     const base::string16 time_remaining_text = l10n_util::GetStringFUTF16(
    171         IDS_IMAGEBURN_DOWNLOAD_TIME_REMAINING,
    172         ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_REMAINING,
    173                                ui::TimeFormat::LENGTH_SHORT, time_remaining));
    174     SendProgressSignal(progress_type, amount_finished, amount_total,
    175                        time_remaining_text);
    176   }
    177 
    178   // BurnController::Delegate override.
    179   virtual void OnNetworkDetected() OVERRIDE {
    180     web_ui()->CallJavascriptFunction("browserBridge.reportNetworkDetected");
    181   }
    182 
    183   // BurnController::Delegate override.
    184   virtual void OnNoNetwork() OVERRIDE {
    185     web_ui()->CallJavascriptFunction("browserBridge.reportNoNetwork");
    186   }
    187 
    188  private:
    189   void CreateDiskValue(const disks::DiskMountManager::Disk& disk,
    190                        base::DictionaryValue* disk_value) {
    191     base::string16 label = base::ASCIIToUTF16(disk.drive_label());
    192     base::i18n::AdjustStringForLocaleDirection(&label);
    193     disk_value->SetString(std::string(kPropertyLabel), label);
    194     disk_value->SetString(std::string(kPropertyFilePath), disk.file_path());
    195     disk_value->SetString(std::string(kPropertyDevicePath), disk.device_path());
    196     disk_value->SetString(std::string(kPropertyDeviceType),
    197         disks::DiskMountManager::DeviceTypeToString(disk.device_type()));
    198   }
    199 
    200   // Callback for the "getDevices" message.
    201   void HandleGetDevices(const base::ListValue* args) {
    202     const std::vector<disks::DiskMountManager::Disk> disks
    203         = burn_controller_->GetBurnableDevices();
    204     base::ListValue results_value;
    205     for (size_t i = 0; i != disks.size(); ++i) {
    206       base::DictionaryValue* disk_value = new base::DictionaryValue();
    207       CreateDiskValue(disks[i], disk_value);
    208       results_value.Append(disk_value);
    209     }
    210     web_ui()->CallJavascriptFunction("browserBridge.getDevicesCallback",
    211                                      results_value);
    212   }
    213 
    214   // Callback for the webuiInitialized message.
    215   void HandleWebUIInitialized(const base::ListValue* args) {
    216     burn_controller_->Init();
    217   }
    218 
    219   // Callback for the "cancelBurnImage" message.
    220   void HandleCancelBurnImage(const base::ListValue* args) {
    221     burn_controller_->CancelBurnImage();
    222   }
    223 
    224   // Callback for the "burnImage" message.
    225   // It may be called with NULL if there is a handler that has started burning,
    226   // and thus set the target paths.
    227   void HandleBurnImage(const base::ListValue* args) {
    228     base::FilePath target_device_path;
    229     ExtractTargetedDevicePath(*args, 0, &target_device_path);
    230 
    231     base::FilePath target_file_path;
    232     ExtractTargetedDevicePath(*args, 1, &target_file_path);
    233 
    234     burn_controller_->StartBurnImage(target_device_path, target_file_path);
    235   }
    236 
    237   // Reports update to UI
    238   void SendProgressSignal(ProgressType progress_type,
    239                           int64 amount_finished,
    240                           int64 amount_total,
    241                           const base::string16& time_remaining_text) {
    242     base::DictionaryValue progress;
    243     int progress_message_id = 0;
    244     switch (progress_type) {
    245       case DOWNLOADING:
    246         progress.SetString("progressType", "download");
    247         progress_message_id = IDS_IMAGEBURN_DOWNLOAD_PROGRESS_TEXT;
    248         break;
    249       case UNZIPPING:
    250         progress.SetString("progressType", "unzip");
    251         break;
    252       case BURNING:
    253         progress.SetString("progressType", "burn");
    254         progress_message_id = IDS_IMAGEBURN_BURN_PROGRESS_TEXT;
    255         break;
    256       default:
    257         return;
    258     }
    259 
    260     progress.SetInteger("amountFinished", amount_finished);
    261     progress.SetInteger("amountTotal", amount_total);
    262     if (amount_total != 0) {
    263       base::string16 progress_text;
    264       GetProgressText(progress_message_id, amount_finished, amount_total,
    265                       &progress_text);
    266       progress.SetString("progressText", progress_text);
    267     } else {
    268       progress.SetString("progressText", "");
    269     }
    270     progress.SetString("timeLeftText", time_remaining_text);
    271 
    272     web_ui()->CallJavascriptFunction("browserBridge.updateProgress", progress);
    273   }
    274 
    275   // size_text should be previously created.
    276   void GetDataSizeText(int64 size, base::string16* size_text) {
    277     *size_text = ui::FormatBytes(size);
    278     base::i18n::AdjustStringForLocaleDirection(size_text);
    279   }
    280 
    281   // progress_text should be previously created.
    282   void GetProgressText(int message_id,
    283                        int64 amount_finished,
    284                        int64 amount_total,
    285                        base::string16* progress_text) {
    286     base::string16 finished;
    287     GetDataSizeText(amount_finished, &finished);
    288     base::string16 total;
    289     GetDataSizeText(amount_total, &total);
    290     *progress_text = l10n_util::GetStringFUTF16(message_id, finished, total);
    291   }
    292 
    293   // device_path has to be previously created.
    294   void ExtractTargetedDevicePath(const base::ListValue& list_value,
    295                                  int index,
    296                                  base::FilePath* device_path) {
    297     const base::Value* list_member;
    298     std::string image_dest;
    299     if (list_value.Get(index, &list_member) &&
    300         list_member->GetType() == base::Value::TYPE_STRING &&
    301         list_member->GetAsString(&image_dest)) {
    302       *device_path = base::FilePath(image_dest);
    303     } else {
    304       LOG(ERROR) << "Unable to get path string";
    305       device_path->clear();
    306     }
    307   }
    308 
    309   scoped_ptr<BurnController> burn_controller_;
    310 
    311   DISALLOW_COPY_AND_ASSIGN(WebUIHandler);
    312 };
    313 
    314 }  // namespace
    315 
    316 }  // namespace imageburner
    317 }  // namespace chromeos
    318 
    319 ////////////////////////////////////////////////////////////////////////////////
    320 //
    321 // ImageBurnUI
    322 //
    323 ////////////////////////////////////////////////////////////////////////////////
    324 
    325 ImageBurnUI::ImageBurnUI(content::WebUI* web_ui) : WebUIController(web_ui) {
    326   chromeos::imageburner::WebUIHandler* handler =
    327       new chromeos::imageburner::WebUIHandler(web_ui->GetWebContents());
    328   web_ui->AddMessageHandler(handler);
    329 
    330   Profile* profile = Profile::FromWebUI(web_ui);
    331   content::WebUIDataSource::Add(
    332       profile, chromeos::imageburner::CreateImageburnerUIHTMLSource());
    333 }
    334