// 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);
}