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