// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/webui/chromeos/imageburner_ui.h" #include <algorithm> #include "base/i18n/rtl.h" #include "base/memory/singleton.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/string_util.h" #include "base/task.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/download/download_types.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/url_constants.h" #include "chrome/common/zip.h" #include "content/browser/browser_thread.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/browser_resources.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" static const char kPropertyPath[] = "path"; static const char kPropertyTitle[] = "title"; static const char kPropertyDirectory[] = "isDirectory"; static const char kImageBaseURL[] = "http://chrome-master.mtv.corp.google.com/chromeos/dev-channel/"; static const char kImageFetcherName[] = "LATEST-x86-generic"; static const char kImageDownloadURL[] = "https://dl.google.com/dl/chromeos/recovery/latest_mario_beta_channel"; static const char kImageFileName[] = "chromeos_image.bin.zip"; static const char kTempImageFolderName[] = "chromeos_image"; //////////////////////////////////////////////////////////////////////////////// // // ImageBurnUIHTMLSource // //////////////////////////////////////////////////////////////////////////////// class ImageBurnUIHTMLSource : public ChromeURLDataManager::DataSource { public: ImageBurnUIHTMLSource() : DataSource(chrome::kChromeUIImageBurnerHost, MessageLoop::current()) { } // Called when the network layer has requested a resource underneath // the path we registered. virtual void StartDataRequest(const std::string& path, bool is_incognito, int request_id) { DictionaryValue localized_strings; localized_strings.SetString("burnConfirmText1", l10n_util::GetStringUTF16(IDS_IMAGEBURN_CONFIM_BURN1)); localized_strings.SetString("burnConfirmText2", l10n_util::GetStringUTF16(IDS_IMAGEBURN_CONFIM_BURN2)); localized_strings.SetString("burnUnsuccessfulMessage", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_UNSUCCESSFUL)); localized_strings.SetString("burnSuccessfulMessage", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_SUCCESSFUL)); localized_strings.SetString("downloadAbortedMessage", l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_UNSUCCESSFUL)); localized_strings.SetString("title", l10n_util::GetStringUTF16(IDS_IMAGEBURN_TITLE)); localized_strings.SetString("listTitle", l10n_util::GetStringUTF16(IDS_IMAGEBURN_ROOT_LIST_TITLE)); localized_strings.SetString("downloadStatusStart", l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_STARTING_STATUS)); localized_strings.SetString("downloadStatusInProgress", l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_IN_PROGRESS_STATUS)); localized_strings.SetString("downloadStatusComplete", l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_COMPLETE_STATUS)); localized_strings.SetString("downloadStatusCanceled", l10n_util::GetStringUTF16(IDS_IMAGEBURN_DOWNLOAD_CANCELED_STATUS)); localized_strings.SetString("burnStatusStart", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_STARTING_STATUS)); localized_strings.SetString("burnStatusInProgress", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_IN_PROGRESS_STATUS)); localized_strings.SetString("burnStatusComplete", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_COMPLETE_STATUS)); localized_strings.SetString("burnStatusCanceled", l10n_util::GetStringUTF16(IDS_IMAGEBURN_BURN_CANCELED_STATUS)); SetFontAndTextDirection(&localized_strings); static const base::StringPiece imageburn_html( ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_IMAGEBURNER_HTML)); const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( imageburn_html, &localized_strings); scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); html_bytes->data.resize(full_html.size()); std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin()); SendResponse(request_id, html_bytes); } virtual std::string GetMimeType(const std::string&) const { return "text/html"; } private: virtual ~ImageBurnUIHTMLSource() {} DISALLOW_COPY_AND_ASSIGN(ImageBurnUIHTMLSource); }; //////////////////////////////////////////////////////////////////////////////// // // ImageBurnTaskProxy // //////////////////////////////////////////////////////////////////////////////// class ImageBurnTaskProxy : public base::RefCountedThreadSafe<ImageBurnTaskProxy> { public: explicit ImageBurnTaskProxy(const base::WeakPtr<ImageBurnHandler>& handler) : handler_(handler) {} void CreateImageDir() { if (handler_) handler_->CreateImageDirOnFileThread(this); } void OnImageDirCreated(bool success) { if (handler_) handler_->OnImageDirCreatedOnUIThread(success); } void BurnImage() { if (handler_) handler_->BurnImageOnFileThread(); DeleteOnUIThread(); } void UnzipImage() { if (handler_) handler_->UnzipImageOnFileThread(this); } void UnzipComplete(bool success) { if (handler_) handler_->UnzipComplete(success); } // ImageBurnTaskProxy is created on the UI thread, so in some cases, // we need to post back to the UI thread for destruction. void DeleteOnUIThread() { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &ImageBurnTaskProxy::DoNothing)); } void DoNothing() {} private: base::WeakPtr<ImageBurnHandler> handler_; friend class base::RefCountedThreadSafe<ImageBurnTaskProxy>; ~ImageBurnTaskProxy() {} DISALLOW_COPY_AND_ASSIGN(ImageBurnTaskProxy); }; //////////////////////////////////////////////////////////////////////////////// // // ImageBurnHandler // //////////////////////////////////////////////////////////////////////////////// ImageBurnHandler::ImageBurnHandler(TabContents* contents) :tab_contents_(contents), download_manager_(NULL), download_item_observer_added_(false), active_download_item_(NULL), resource_manager_(NULL) { chromeos::CrosLibrary::Get()->GetMountLibrary()->AddObserver(this); chromeos::CrosLibrary::Get()->GetBurnLibrary()->AddObserver(this); resource_manager_ = ImageBurnResourceManager::GetInstance(); zip_image_file_path_.clear(); image_file_path_.clear(); image_target_.clear(); } ImageBurnHandler::~ImageBurnHandler() { chromeos::CrosLibrary::Get()->GetMountLibrary()->RemoveObserver(this); chromeos::CrosLibrary::Get()->GetBurnLibrary()->RemoveObserver(this); if (active_download_item_) active_download_item_->RemoveObserver(this); if (download_manager_) download_manager_->RemoveObserver(this); } WebUIMessageHandler* ImageBurnHandler::Attach(WebUI* web_ui) { return WebUIMessageHandler::Attach(web_ui); } void ImageBurnHandler::RegisterMessages() { web_ui_->RegisterMessageCallback("getRoots", NewCallback(this, &ImageBurnHandler::HandleGetRoots)); web_ui_->RegisterMessageCallback("downloadImage", NewCallback(this, &ImageBurnHandler::HandleDownloadImage)); web_ui_->RegisterMessageCallback("burnImage", NewCallback(this, &ImageBurnHandler::HandleBurnImage)); web_ui_->RegisterMessageCallback("cancelBurnImage", NewCallback(this, &ImageBurnHandler::HandleCancelBurnImage)); } void ImageBurnHandler::DiskChanged(chromeos::MountLibraryEventType event, const chromeos::MountLibrary::Disk* disk) { if (event == chromeos::MOUNT_DISK_REMOVED || event == chromeos::MOUNT_DISK_CHANGED || event == chromeos::MOUNT_DISK_UNMOUNTED) { web_ui_->CallJavascriptFunction("rootsChanged"); } } void ImageBurnHandler::DeviceChanged(chromeos::MountLibraryEventType event, const std::string& device_path) { if (event == chromeos::MOUNT_DEVICE_REMOVED) web_ui_->CallJavascriptFunction("rootsChanged"); } void ImageBurnHandler::ProgressUpdated(chromeos::BurnLibrary* object, chromeos::BurnEventType evt, const ImageBurnStatus& status) { UpdateBurnProgress(status.amount_burnt, status.total_size, status.target_path, evt); if (evt == chromeos::BURN_COMPLETE) { FinalizeBurn(true); } else if (evt == chromeos::BURN_CANCELED) { FinalizeBurn(false); } } void ImageBurnHandler::OnDownloadUpdated(DownloadItem* download) { if (download->IsCancelled()) { DownloadCompleted(false); // Should stop observation. DCHECK(!download_item_observer_added_); } else if (download->IsComplete()) { zip_image_file_path_ = download->full_path(); DownloadCompleted(true); // Should stop observation. DCHECK(!download_item_observer_added_); } else if (download->IsPartialDownload()) { scoped_ptr<DictionaryValue> result_value( download_util::CreateDownloadItemValue(download, 0)); web_ui_->CallJavascriptFunction("downloadUpdated", *result_value); } } void ImageBurnHandler::OnDownloadOpened(DownloadItem* download) { if (download->safety_state() == DownloadItem::DANGEROUS) download->DangerousDownloadValidated(); } void ImageBurnHandler::ModelChanged() { // Find our item and observe it. std::vector<DownloadItem*> downloads; download_manager_->GetTemporaryDownloads( resource_manager_->GetImageDir(), &downloads); if (download_item_observer_added_) return; for (std::vector<DownloadItem*>::const_iterator it = downloads.begin(); it != downloads.end(); ++it) { if ((*it)->original_url() == *image_download_url_) { download_item_observer_added_ = true; (*it)->AddObserver(this); active_download_item_ = *it; break; } } } void ImageBurnHandler::OnImageDirCreated(bool success, ImageBurnTaskProxy* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); // Transfer to UI thread. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(task, &ImageBurnTaskProxy::OnImageDirCreated, success)); } void ImageBurnHandler::OnDownloadStarted(bool success) { if (success) resource_manager_->set_download_started(true); else DownloadCompleted(false); } void ImageBurnHandler::HandleGetRoots(const ListValue* args) { ListValue results_value; DictionaryValue info_value; chromeos::MountLibrary* mount_lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); const chromeos::MountLibrary::DiskMap& disks = mount_lib->disks(); if (!resource_manager_->burn_in_progress()) { for (chromeos::MountLibrary::DiskMap::const_iterator iter = disks.begin(); iter != disks.end(); ++iter) { if (iter->second->is_parent()) { FilePath disk_path = FilePath(iter->second->system_path()).DirName(); std::string title = "/dev/" + disk_path.BaseName().value(); if (!iter->second->on_boot_device()) { DictionaryValue* page_value = new DictionaryValue(); page_value->SetString(std::string(kPropertyTitle), title); page_value->SetString(std::string(kPropertyPath), title); page_value->SetBoolean(std::string(kPropertyDirectory), true); results_value.Append(page_value); } } } } info_value.SetString("functionCall", "getRoots"); info_value.SetString(std::string(kPropertyPath), ""); web_ui_->CallJavascriptFunction("browseFileResult", info_value, results_value); } void ImageBurnHandler::HandleDownloadImage(const ListValue* args) { ExtractTargetedDeviceSystemPath(args); if (resource_manager_->GetImageDir().empty()) { // Create image dir on File thread. scoped_refptr<ImageBurnTaskProxy> task = new ImageBurnTaskProxy(AsWeakPtr()); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(task.get(), &ImageBurnTaskProxy::CreateImageDir)); } else { OnImageDirCreatedOnUIThread(true); } } void ImageBurnHandler::DownloadCompleted(bool success) { resource_manager_->SetDownloadFinished(success); if (active_download_item_) { active_download_item_->RemoveObserver(this); active_download_item_ = NULL; } download_item_observer_added_ = false; if (download_manager_) download_manager_->RemoveObserver(this); if (success) { UnzipImage(); } else { UnzipComplete(success); } } void ImageBurnHandler::UnzipComplete(bool success) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DictionaryValue signal_value; if (success) { signal_value.SetString("state", "COMPLETE"); web_ui_->CallJavascriptFunction("downloadUpdated", signal_value); web_ui_->CallJavascriptFunction("promptUserDownloadFinished"); } else { signal_value.SetString("state", "CANCELLED"); web_ui_->CallJavascriptFunction("downloadUpdated", signal_value); web_ui_->CallJavascriptFunction("alertUserDownloadAborted"); } } void ImageBurnHandler::HandleBurnImage(const ListValue* args) { scoped_refptr<ImageBurnTaskProxy> task = new ImageBurnTaskProxy(AsWeakPtr()); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(task.get(), &ImageBurnTaskProxy::BurnImage)); } void ImageBurnHandler::HandleCancelBurnImage(const ListValue* args) { image_target_.clear(); } void ImageBurnHandler::CreateImageDirOnFileThread(ImageBurnTaskProxy* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); resource_manager_->CreateImageDir(this, task); } void ImageBurnHandler::OnImageDirCreatedOnUIThread(bool success) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (success) { zip_image_file_path_ = resource_manager_->GetImageDir().Append(kImageFileName); resource_manager_->CreateImageUrl(tab_contents_, this); } else { DownloadCompleted(success); } } void ImageBurnHandler::BurnImageOnFileThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); if (resource_manager_->burn_in_progress()) return; resource_manager_->set_burn_in_progress(true); if (chromeos::CrosLibrary::Get()->GetBurnLibrary()-> DoBurn(image_file_path_, image_target_)) { DictionaryValue signal_value; signal_value.SetString("state", "IN_PROGRESS"); signal_value.SetString("path", image_target_.value()); signal_value.SetInteger("received", 0); signal_value.SetString("progress_status_text", ""); web_ui_->CallJavascriptFunction("burnProgressUpdated", signal_value); } } void ImageBurnHandler::FinalizeBurn(bool successful) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); web_ui_->CallJavascriptFunction(successful ? "burnSuccessful" : "burnUnsuccessful"); resource_manager_->set_burn_in_progress(false); } void ImageBurnHandler::UpdateBurnProgress(int64 total_burnt, int64 image_size, const std::string& path, chromeos::BurnEventType event) { DictionaryValue progress_value; progress_value.SetString("progress_status_text", GetBurnProgressText(total_burnt, image_size)); if (event == chromeos::BURN_UPDATED) progress_value.SetString("state", "IN_PROGRESS"); else if (event == chromeos::BURN_CANCELED) progress_value.SetString("state", "CANCELLED"); else if (event == chromeos::BURN_COMPLETE) progress_value.SetString("state", "COMPLETE"); progress_value.SetInteger("received", total_burnt); progress_value.SetInteger("total", image_size); progress_value.SetString("path", path); web_ui_->CallJavascriptFunction("burnProgressUpdated", progress_value); } string16 ImageBurnHandler::GetBurnProgressText(int64 total_burnt, int64 image_size) { DataUnits amount_units = GetByteDisplayUnits(total_burnt); string16 burnt_size = FormatBytes(total_burnt, amount_units, true); base::i18n::AdjustStringForLocaleDirection(&burnt_size); if (image_size) { amount_units = GetByteDisplayUnits(image_size); string16 total_text = FormatBytes(image_size, amount_units, true); base::i18n::AdjustStringForLocaleDirection(&total_text); return l10n_util::GetStringFUTF16(IDS_IMAGEBURN_BURN_PROGRESS, burnt_size, total_text); } else { return l10n_util::GetStringFUTF16(IDS_IMAGEBURN_BURN_PROGRESS_SIZE_UNKNOWN, burnt_size); } } void ImageBurnHandler::OnImageUrlCreated(GURL* image_url, bool success) { if (!success) { DownloadCompleted(false); return; } image_download_url_ = image_url; download_manager_ = tab_contents_->profile()->GetDownloadManager(); download_manager_->AddObserver(this); if (!resource_manager_->download_started()) { resource_manager_->set_download_started(true); if (!resource_manager_->image_download_requested()) { resource_manager_->set_image_download_requested(true); ImageBurnDownloader::GetInstance()->AddListener(this, *image_download_url_); ImageBurnDownloader::GetInstance()->DownloadFile(*image_download_url_, zip_image_file_path_, tab_contents_); } } else if (resource_manager_->download_finished()) { DownloadCompleted(true); } } void ImageBurnHandler::ExtractTargetedDeviceSystemPath( const ListValue* list_value) { Value* list_member; if (list_value->Get(0, &list_member) && list_member->GetType() == Value::TYPE_STRING) { const StringValue* string_value = static_cast<const StringValue*>(list_member); std::string image_dest; string_value->GetAsString(&image_dest); image_target_ = FilePath(image_dest); } else { LOG(ERROR) << "Unable to get path string"; } } void ImageBurnHandler::UnzipImage() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); scoped_refptr<ImageBurnTaskProxy> task = new ImageBurnTaskProxy(AsWeakPtr()); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(task.get(), &ImageBurnTaskProxy::UnzipImage)); } void ImageBurnHandler::UnzipImageOnFileThread(ImageBurnTaskProxy* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); bool success = UnzipImageImpl(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(task, &ImageBurnTaskProxy::UnzipComplete, success)); } bool ImageBurnHandler::UnzipImageImpl() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); const FilePath& img_dir = resource_manager_->GetImageDir(); if (!Unzip(zip_image_file_path_, img_dir)) return false; image_file_path_.clear(); file_util::FileEnumerator file_enumerator( img_dir, false, // recursive file_util::FileEnumerator::FILES); for (FilePath path = file_enumerator.Next(); !path.empty(); path = file_enumerator.Next()) { if (path != zip_image_file_path_) { image_file_path_ = path; break; } } return !image_file_path_.empty(); } //////////////////////////////////////////////////////////////////////////////// // // ImageBurnResourceManager // //////////////////////////////////////////////////////////////////////////////// ImageBurnResourceManager::ImageBurnResourceManager() : image_download_requested_(false), download_started_(false), download_finished_(false), burn_in_progress_(false), download_manager_(NULL), download_item_observer_added_(false), active_download_item_(NULL), image_url_(new GURL(kImageDownloadURL)), config_file_url_(std::string(kImageBaseURL) + kImageFetcherName), config_file_requested_(false), config_file_fetched_(true) { image_dir_.clear(); } ImageBurnResourceManager::~ImageBurnResourceManager() { if (!image_dir_.empty()) { file_util::Delete(image_dir_, true); } if (active_download_item_) active_download_item_->RemoveObserver(this); if (download_manager_) download_manager_->RemoveObserver(this); } // static ImageBurnResourceManager* ImageBurnResourceManager::GetInstance() { return Singleton<ImageBurnResourceManager>::get(); } void ImageBurnResourceManager::OnDownloadUpdated(DownloadItem* download) { if (download->IsCancelled()) { image_url_.reset(); ConfigFileFetched(false); // ConfigFileFetched should remove observer. DCHECK(!download_item_observer_added_); DCHECK(active_download_item_ == NULL); } else if (download->IsComplete()) { std::string image_url; if (file_util::ReadFileToString(config_file_path_, &image_url)) { image_url_.reset(new GURL(std::string(kImageBaseURL) + image_url)); ConfigFileFetched(true); } else { image_url_.reset(); ConfigFileFetched(false); } } } void ImageBurnResourceManager::ModelChanged() { std::vector<DownloadItem*> downloads; download_manager_->GetTemporaryDownloads(GetImageDir(), &downloads); if (download_item_observer_added_) return; for (std::vector<DownloadItem*>::const_iterator it = downloads.begin(); it != downloads.end(); ++it) { if ((*it)->url() == config_file_url_) { download_item_observer_added_ = true; (*it)->AddObserver(this); active_download_item_ = *it; break; } } } void ImageBurnResourceManager::OnDownloadStarted(bool success) { if (!success) ConfigFileFetched(false); } void ImageBurnResourceManager::CreateImageDir( Delegate* delegate, ImageBurnTaskProxy* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); bool success = true; if (image_dir_.empty()) { CHECK(PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &image_dir_)); image_dir_ = image_dir_.Append(kTempImageFolderName); success = file_util::CreateDirectory(image_dir_); } delegate->OnImageDirCreated(success, task); } const FilePath& ImageBurnResourceManager::GetImageDir() { return image_dir_; } void ImageBurnResourceManager::SetDownloadFinished(bool finished) { if (!download_started_) return; if (!finished) download_started_ = false; download_finished_ = finished; } void ImageBurnResourceManager::CreateImageUrl(TabContents* tab_contents, Delegate* delegate) { if (config_file_fetched_) { delegate->OnImageUrlCreated(image_url_.get(), true); return; } downloaders_.push_back(delegate); if (config_file_requested_) return; config_file_requested_ = true; config_file_path_ = GetImageDir().Append(kImageFetcherName); download_manager_ = tab_contents->profile()->GetDownloadManager(); download_manager_->AddObserver(this); ImageBurnDownloader* downloader = ImageBurnDownloader::GetInstance(); downloader->AddListener(this, config_file_url_); downloader->DownloadFile(config_file_url_, config_file_path_, tab_contents); } void ImageBurnResourceManager::ConfigFileFetched(bool fetched) { if (active_download_item_) { active_download_item_->RemoveObserver(this); active_download_item_ = NULL; } download_item_observer_added_ = false; if (download_manager_) download_manager_->RemoveObserver(this); if (!fetched) config_file_requested_ = false; config_file_fetched_ = fetched; for (size_t i = 0; i < downloaders_.size(); ++i) downloaders_[i]->OnImageUrlCreated(image_url_.get(), fetched); downloaders_.clear(); } //////////////////////////////////////////////////////////////////////////////// // // ImageBurnDownloaderTaskProxy // //////////////////////////////////////////////////////////////////////////////// class ImageBurnDownloaderTaskProxy : public base::RefCountedThreadSafe<ImageBurnDownloaderTaskProxy> { public: explicit ImageBurnDownloaderTaskProxy() {} void CreateFileStream(const GURL& url, const FilePath& target_path, TabContents* tab_contents) { ImageBurnDownloader::GetInstance()->CreateFileStreamOnFileThread(url, target_path, tab_contents, this); } void OnFileStreamCreated(const GURL& url, const FilePath& file_path, TabContents* tab_contents, net::FileStream* created_file_stream) { ImageBurnDownloader::GetInstance()->OnFileStreamCreatedOnUIThread(url, file_path, tab_contents, created_file_stream); } private: ~ImageBurnDownloaderTaskProxy() {} friend class base::RefCountedThreadSafe<ImageBurnDownloaderTaskProxy>; DISALLOW_COPY_AND_ASSIGN(ImageBurnDownloaderTaskProxy); }; //////////////////////////////////////////////////////////////////////////////// // // ImageBurnDownloader // //////////////////////////////////////////////////////////////////////////////// // static ImageBurnDownloader* ImageBurnDownloader::GetInstance() { return Singleton<ImageBurnDownloader>::get(); } void ImageBurnDownloader::DownloadFile(const GURL& url, const FilePath& file_path, TabContents* tab_contents) { // First we have to create file stream we will download file to. // That has to be done on File thread. scoped_refptr<ImageBurnDownloaderTaskProxy> task = new ImageBurnDownloaderTaskProxy(); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableMethod(task.get(), &ImageBurnDownloaderTaskProxy::CreateFileStream, url, file_path, tab_contents)); } void ImageBurnDownloader::CreateFileStreamOnFileThread( const GURL& url, const FilePath& file_path, TabContents* tab_contents, ImageBurnDownloaderTaskProxy* task) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(!file_path.empty()); scoped_ptr<net::FileStream> file_stream(new net::FileStream); if (file_stream->Open(file_path, base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE)) file_stream.reset(NULL); // Call callback method on UI thread. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(task, &ImageBurnDownloaderTaskProxy::OnFileStreamCreated, url, file_path, tab_contents, file_stream.release())); } void ImageBurnDownloader::OnFileStreamCreatedOnUIThread(const GURL& url, const FilePath& file_path, TabContents* tab_contents, net::FileStream* created_file_stream) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (created_file_stream) { DownloadManager* download_manager = tab_contents->profile()->GetDownloadManager(); DownloadSaveInfo save_info; save_info.file_path = file_path; save_info.file_stream = linked_ptr<net::FileStream>(created_file_stream); DownloadStarted(true, url); download_manager->DownloadUrlToFile(url, tab_contents->GetURL(), tab_contents->encoding(), save_info, tab_contents); } else { DownloadStarted(false, url); } } void ImageBurnDownloader::AddListener(Listener* listener, const GURL& url) { listeners_.insert(std::make_pair(url, listener)); } void ImageBurnDownloader::DownloadStarted(bool success, const GURL& url) { std::pair<ListenerMap::iterator, ListenerMap::iterator> listener_range = listeners_.equal_range(url); for (ListenerMap::iterator current_listener = listener_range.first; current_listener != listener_range.second; ++current_listener) { current_listener->second->OnDownloadStarted(success); } listeners_.erase(listener_range.first, listener_range.second); } //////////////////////////////////////////////////////////////////////////////// // // ImageBurnUI // //////////////////////////////////////////////////////////////////////////////// ImageBurnUI::ImageBurnUI(TabContents* contents) : WebUI(contents) { ImageBurnHandler* handler = new ImageBurnHandler(contents); AddMessageHandler((handler)->Attach(this)); ImageBurnUIHTMLSource* html_source = new ImageBurnUIHTMLSource(); contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); }