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