imageburner_ui.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 "content/public/browser/web_ui.h"
17#include "content/public/browser/web_ui_data_source.h"
18#include "content/public/browser/web_ui_message_handler.h"
19#include "grit/browser_resources.h"
20#include "grit/generated_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
26namespace chromeos {
27namespace imageburner {
28
29namespace {
30
31const char kPropertyDevicePath[] = "devicePath";
32const char kPropertyFilePath[] = "filePath";
33const char kPropertyLabel[] = "label";
34const char kPropertyDeviceType[] = "type";
35
36// Link displayed on imageburner ui.
37const char kMoreInfoLink[] =
38    "http://www.chromium.org/chromium-os/chromiumos-design-docs/recovery-mode";
39
40content::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", ASCIIToUTF16(kMoreInfoLink));
80
81  source->SetJsonPath("strings.js");
82  source->AddResourcePath("image_burner.js", IDR_IMAGEBURNER_JS);
83  source->SetDefaultResource(IDR_IMAGEBURNER_HTML);
84  return source;
85}
86
87class WebUIHandler
88    : public content::WebUIMessageHandler,
89      public BurnController::Delegate {
90 public:
91  explicit WebUIHandler(content::WebContents* contents)
92      : burn_controller_(BurnController::CreateBurnController(contents, this)){
93  }
94
95  virtual ~WebUIHandler() {
96  }
97
98  // WebUIMessageHandler implementation.
99  virtual void RegisterMessages() OVERRIDE {
100    web_ui()->RegisterMessageCallback(
101        "getDevices",
102        base::Bind(&WebUIHandler::HandleGetDevices, base::Unretained(this)));
103    web_ui()->RegisterMessageCallback(
104        "burnImage",
105        base::Bind(&WebUIHandler::HandleBurnImage, base::Unretained(this)));
106    web_ui()->RegisterMessageCallback(
107        "cancelBurnImage",
108        base::Bind(&WebUIHandler::HandleCancelBurnImage,
109                   base::Unretained(this)));
110    web_ui()->RegisterMessageCallback(
111        "webuiInitialized",
112        base::Bind(&WebUIHandler::HandleWebUIInitialized,
113                   base::Unretained(this)));
114  }
115
116  // BurnController::Delegate override.
117  virtual void OnSuccess() OVERRIDE {
118    web_ui()->CallJavascriptFunction("browserBridge.reportSuccess");
119  }
120
121  // BurnController::Delegate override.
122  virtual void OnFail(int error_message_id) OVERRIDE {
123    StringValue error_message(l10n_util::GetStringUTF16(error_message_id));
124    web_ui()->CallJavascriptFunction("browserBridge.reportFail", error_message);
125  }
126
127  // BurnController::Delegate override.
128  virtual void OnDeviceAdded(const disks::DiskMountManager::Disk& disk)
129      OVERRIDE {
130    DictionaryValue disk_value;
131    CreateDiskValue(disk, &disk_value);
132    web_ui()->CallJavascriptFunction("browserBridge.deviceAdded", disk_value);
133  }
134
135  // BurnController::Delegate override.
136  virtual void OnDeviceRemoved(const disks::DiskMountManager::Disk& disk)
137      OVERRIDE {
138    StringValue device_path_value(disk.device_path());
139    web_ui()->CallJavascriptFunction("browserBridge.deviceRemoved",
140                                     device_path_value);
141  }
142
143  // BurnController::Delegate override.
144  virtual void OnDeviceTooSmall(int64 device_size) OVERRIDE {
145    string16 size;
146    GetDataSizeText(device_size, &size);
147    StringValue device_size_text(size);
148    web_ui()->CallJavascriptFunction("browserBridge.reportDeviceTooSmall",
149                                     device_size_text);
150  }
151
152  // BurnController::Delegate override.
153  virtual void OnProgress(ProgressType progress_type,
154                          int64 amount_finished,
155                          int64 amount_total) OVERRIDE {
156    const string16 time_remaining_text =
157        l10n_util::GetStringUTF16(IDS_IMAGEBURN_PROGRESS_TIME_UNKNOWN);
158    SendProgressSignal(progress_type, amount_finished, amount_total,
159                       time_remaining_text);
160  }
161
162  // BurnController::Delegate override.
163  virtual void OnProgressWithRemainingTime(
164      ProgressType progress_type,
165      int64 amount_finished,
166      int64 amount_total,
167      const base::TimeDelta& time_remaining) OVERRIDE {
168    const string16 time_remaining_text = l10n_util::GetStringFUTF16(
169        IDS_IMAGEBURN_DOWNLOAD_TIME_REMAINING,
170        ui::TimeFormat::TimeRemaining(time_remaining));
171    SendProgressSignal(progress_type, amount_finished, amount_total,
172                       time_remaining_text);
173  }
174
175  // BurnController::Delegate override.
176  virtual void OnNetworkDetected() OVERRIDE {
177    web_ui()->CallJavascriptFunction("browserBridge.reportNetworkDetected");
178  }
179
180  // BurnController::Delegate override.
181  virtual void OnNoNetwork() OVERRIDE {
182    web_ui()->CallJavascriptFunction("browserBridge.reportNoNetwork");
183  }
184
185 private:
186  void CreateDiskValue(const disks::DiskMountManager::Disk& disk,
187                       DictionaryValue* disk_value) {
188    string16 label = ASCIIToUTF16(disk.drive_label());
189    base::i18n::AdjustStringForLocaleDirection(&label);
190    disk_value->SetString(std::string(kPropertyLabel), label);
191    disk_value->SetString(std::string(kPropertyFilePath), disk.file_path());
192    disk_value->SetString(std::string(kPropertyDevicePath), disk.device_path());
193    disk_value->SetString(std::string(kPropertyDeviceType),
194        disks::DiskMountManager::DeviceTypeToString(disk.device_type()));
195  }
196
197  // Callback for the "getDevices" message.
198  void HandleGetDevices(const ListValue* args) {
199    const std::vector<disks::DiskMountManager::Disk> disks
200        = burn_controller_->GetBurnableDevices();
201    ListValue results_value;
202    for (size_t i = 0; i != disks.size(); ++i) {
203      DictionaryValue* disk_value = new DictionaryValue();
204      CreateDiskValue(disks[i], disk_value);
205      results_value.Append(disk_value);
206    }
207    web_ui()->CallJavascriptFunction("browserBridge.getDevicesCallback",
208                                     results_value);
209  }
210
211  // Callback for the webuiInitialized message.
212  void HandleWebUIInitialized(const ListValue* args) {
213    burn_controller_->Init();
214  }
215
216  // Callback for the "cancelBurnImage" message.
217  void HandleCancelBurnImage(const ListValue* args) {
218    burn_controller_->CancelBurnImage();
219  }
220
221  // Callback for the "burnImage" message.
222  // It may be called with NULL if there is a handler that has started burning,
223  // and thus set the target paths.
224  void HandleBurnImage(const ListValue* args) {
225    base::FilePath target_device_path;
226    ExtractTargetedDevicePath(*args, 0, &target_device_path);
227
228    base::FilePath target_file_path;
229    ExtractTargetedDevicePath(*args, 1, &target_file_path);
230
231    burn_controller_->StartBurnImage(target_device_path, target_file_path);
232  }
233
234  // Reports update to UI
235  void SendProgressSignal(ProgressType progress_type,
236                          int64 amount_finished,
237                          int64 amount_total,
238                          const string16& time_remaining_text) {
239    DictionaryValue progress;
240    int progress_message_id = 0;
241    switch (progress_type) {
242      case DOWNLOADING:
243        progress.SetString("progressType", "download");
244        progress_message_id = IDS_IMAGEBURN_DOWNLOAD_PROGRESS_TEXT;
245        break;
246      case UNZIPPING:
247        progress.SetString("progressType", "unzip");
248        break;
249      case BURNING:
250        progress.SetString("progressType", "burn");
251        progress_message_id = IDS_IMAGEBURN_BURN_PROGRESS_TEXT;
252        break;
253      default:
254        return;
255    }
256
257    progress.SetInteger("amountFinished", amount_finished);
258    progress.SetInteger("amountTotal", amount_total);
259    if (amount_total != 0) {
260      string16 progress_text;
261      GetProgressText(progress_message_id, amount_finished, amount_total,
262                      &progress_text);
263      progress.SetString("progressText", progress_text);
264    } else {
265      progress.SetString("progressText", "");
266    }
267    progress.SetString("timeLeftText", time_remaining_text);
268
269    web_ui()->CallJavascriptFunction("browserBridge.updateProgress", progress);
270  }
271
272  // size_text should be previously created.
273  void GetDataSizeText(int64 size, string16* size_text) {
274    *size_text = ui::FormatBytes(size);
275    base::i18n::AdjustStringForLocaleDirection(size_text);
276  }
277
278  // progress_text should be previously created.
279  void GetProgressText(int message_id,
280                       int64 amount_finished,
281                       int64 amount_total,
282                       string16* progress_text) {
283    string16 finished;
284    GetDataSizeText(amount_finished, &finished);
285    string16 total;
286    GetDataSizeText(amount_total, &total);
287    *progress_text = l10n_util::GetStringFUTF16(message_id, finished, total);
288  }
289
290  // device_path has to be previously created.
291  void ExtractTargetedDevicePath(const ListValue& list_value,
292                                 int index,
293                                 base::FilePath* device_path) {
294    const Value* list_member;
295    std::string image_dest;
296    if (list_value.Get(index, &list_member) &&
297        list_member->GetType() == Value::TYPE_STRING &&
298        list_member->GetAsString(&image_dest)) {
299      *device_path = base::FilePath(image_dest);
300    } else {
301      LOG(ERROR) << "Unable to get path string";
302      device_path->clear();
303    }
304  }
305
306  scoped_ptr<BurnController> burn_controller_;
307
308  DISALLOW_COPY_AND_ASSIGN(WebUIHandler);
309};
310
311}  // namespace
312
313}  // namespace imageburner
314}  // namespace chromeos
315
316////////////////////////////////////////////////////////////////////////////////
317//
318// ImageBurnUI
319//
320////////////////////////////////////////////////////////////////////////////////
321
322ImageBurnUI::ImageBurnUI(content::WebUI* web_ui) : WebUIController(web_ui) {
323  chromeos::imageburner::WebUIHandler* handler =
324      new chromeos::imageburner::WebUIHandler(web_ui->GetWebContents());
325  web_ui->AddMessageHandler(handler);
326
327  Profile* profile = Profile::FromWebUI(web_ui);
328  content::WebUIDataSource::Add(
329      profile, chromeos::imageburner::CreateImageburnerUIHTMLSource());
330}
331