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