diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/browser.vcproj | 16 | ||||
-rw-r--r-- | chrome/browser/browser_resources.grd | 1 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui.cc | 46 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui.h | 7 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui_contents.cc | 5 | ||||
-rw-r--r-- | chrome/browser/dom_ui/downloads_ui.cc | 369 | ||||
-rw-r--r-- | chrome/browser/dom_ui/downloads_ui.h | 123 | ||||
-rw-r--r-- | chrome/browser/dom_ui/fileicon_source.cc | 63 | ||||
-rw-r--r-- | chrome/browser/dom_ui/fileicon_source.h | 45 | ||||
-rw-r--r-- | chrome/browser/resources/downloads.html | 551 |
10 files changed, 1224 insertions, 2 deletions
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index ff3a63e..11070f7 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -1522,6 +1522,22 @@ > </File> <File + RelativePath=".\dom_ui\downloads_ui.cc" + > + </File> + <File + RelativePath=".\dom_ui\downloads_ui.h" + > + </File> + <File + RelativePath=".\dom_ui\fileicon_source.cc" + > + </File> + <File + RelativePath=".\dom_ui\fileicon_source.h" + > + </File> + <File RelativePath=".\dom_ui\history_ui.cc" > </File> diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index f41a269..232b88d 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -22,6 +22,7 @@ <include name="IDR_INCOGNITO_TAB_HTML" file="resources\incognito_tab.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_CREDITS_HTML" file="resources\about_credits.html" flattenhtml="true" type="BINDATA" /> <include name="IDR_HISTORY_HTML" file="resources\history.html" flattenhtml="true" type="BINDATA" /> + <include name="IDR_DOWNLOADS_HTML" file="resources\downloads.html" flattenhtml="true" type="BINDATA" /> </includes> </release> </grit> diff --git a/chrome/browser/dom_ui/dom_ui.cc b/chrome/browser/dom_ui/dom_ui.cc index 1fe8d99..94116b3 100644 --- a/chrome/browser/dom_ui/dom_ui.cc +++ b/chrome/browser/dom_ui/dom_ui.cc @@ -48,6 +48,11 @@ void DOMUI::ProcessDOMUIMessage(const std::string& message, callback->second->Run(value.get()); } +void DOMUI::CallJavascriptFunction(const std::wstring& function_name) { + std::wstring javascript = function_name + L"();"; + ExecuteJavascript(javascript); +} + void DOMUI::CallJavascriptFunction(const std::wstring& function_name, const Value& arg) { std::string json; @@ -123,4 +128,43 @@ void DOMMessageHandler::SetURLAndTitle(DictionaryValue* dictionary, } } dictionary->SetString(L"title", title_to_set); -}
\ No newline at end of file +} + +bool DOMMessageHandler::ExtractIntegerValue(const Value* value, int* out_int) { + if (value && value->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(value); + Value* list_member; + + // Get id. + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + string_value->GetAsString(&wstring_value); + *out_int = StringToInt(wstring_value); + return true; + } + } + + return false; +} + +std::wstring DOMMessageHandler::ExtractStringValue(const Value* value) { + if (value && value->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(value); + Value* list_member; + + // Get id. + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + string_value->GetAsString(&wstring_value); + return wstring_value; + } + } + return std::wstring(); +} + diff --git a/chrome/browser/dom_ui/dom_ui.h b/chrome/browser/dom_ui/dom_ui.h index 8b0286b..ff0f96f 100644 --- a/chrome/browser/dom_ui/dom_ui.h +++ b/chrome/browser/dom_ui/dom_ui.h @@ -35,6 +35,7 @@ class DOMUI { // of the call, and should be thought of more like sending a message to // the page. // There are two function variants for one-arg and two-arg calls. + void CallJavascriptFunction(const std::wstring& function_name); void CallJavascriptFunction(const std::wstring& function_name, const Value& arg); void CallJavascriptFunction(const std::wstring& function_name, @@ -77,6 +78,12 @@ class DOMMessageHandler { std::wstring title, const GURL& gurl); + // Extract an integer value from a Value. + bool ExtractIntegerValue(const Value* value, int* out_int); + + // Extract a string value from a Value. + std::wstring ExtractStringValue(const Value* value); + DOMUI* const dom_ui_; private: diff --git a/chrome/browser/dom_ui/dom_ui_contents.cc b/chrome/browser/dom_ui/dom_ui_contents.cc index 4e0a3f2..89c35f2 100644 --- a/chrome/browser/dom_ui/dom_ui_contents.cc +++ b/chrome/browser/dom_ui/dom_ui_contents.cc @@ -6,6 +6,7 @@ #include "chrome/browser/dom_ui/dom_ui.h" #include "chrome/browser/dom_ui/history_ui.h" +#include "chrome/browser/dom_ui/downloads_ui.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/common/resource_bundle.h" @@ -192,7 +193,9 @@ bool DOMUIContents::NavigateToPendingEntry(bool reload) { DOMUI* DOMUIContents::GetDOMUIForURL(const GURL &url) { if (url.host() == HistoryUI::GetBaseURL().host()) return new HistoryUI(this); - + else if (url.host() == DownloadsUI::GetBaseURL().host()) + return new DownloadsUI(this); + return NULL; } diff --git a/chrome/browser/dom_ui/downloads_ui.cc b/chrome/browser/dom_ui/downloads_ui.cc new file mode 100644 index 0000000..49a6726 --- /dev/null +++ b/chrome/browser/dom_ui/downloads_ui.cc @@ -0,0 +1,369 @@ +// Copyright (c) 2006-2008 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/dom_ui/downloads_ui.h" + +#include "base/gfx/png_encoder.h" +#include "base/string_piece.h" +#include "base/thread.h" +#include "base/time.h" +#include "base/time_format.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/dom_ui/fileicon_source.h" +#include "chrome/browser/download/download_util.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profile.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/time_format.h" + +// Generated by GRIT +#include "browser_resources.h" +#include "generated_resources.h" + +using base::Time; + +// DownloadsUI is accessible from chrome-ui://downloads. +static const char kDownloadsHost[] = "downloads"; + +/////////////////////////////////////////////////////////////////////////////// +// +// DownloadsHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +DownloadsUIHTMLSource::DownloadsUIHTMLSource() + : DataSource(kDownloadsHost, MessageLoop::current()) { +} + +void DownloadsUIHTMLSource::StartDataRequest(const std::string& path, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_DOWNLOAD_TITLE)); + localized_strings.SetString(L"searchbutton", + l10n_util::GetString(IDS_DOWNLOAD_SEARCH_BUTTON)); + localized_strings.SetString(L"no_results", + l10n_util::GetString(IDS_DOWNLOAD_SEARCH_BUTTON)); + localized_strings.SetString(L"searchresultsfor", + l10n_util::GetString(IDS_DOWNLOAD_SEARCHRESULTSFOR)); + localized_strings.SetString(L"downloads", + l10n_util::GetString(IDS_DOWNLOAD_TITLE)); + + // Status. + localized_strings.SetString(L"status_cancelled", + l10n_util::GetString(IDS_DOWNLOAD_TAB_CANCELLED)); + localized_strings.SetString(L"status_paused", + l10n_util::GetString(IDS_DOWNLOAD_PROGRESS_PAUSED)); + + // Dangerous file. + localized_strings.SetString(L"danger_desc", + l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, L"%s")); + localized_strings.SetString(L"danger_save", + l10n_util::GetString(IDS_SAVE_DOWNLOAD)); + localized_strings.SetString(L"danger_discard", + l10n_util::GetString(IDS_DISCARD_DOWNLOAD)); + + // Controls. + localized_strings.SetString(L"control_pause", + l10n_util::GetString(IDS_DOWNLOAD_LINK_PAUSE)); + localized_strings.SetString(L"control_showinfolder", + l10n_util::GetString(IDS_DOWNLOAD_LINK_SHOW)); + localized_strings.SetString(L"control_cancel", + l10n_util::GetString(IDS_DOWNLOAD_LINK_CANCEL)); + localized_strings.SetString(L"control_resume", + l10n_util::GetString(IDS_DOWNLOAD_LINK_RESUME)); + + static const StringPiece downloads_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_DOWNLOADS_HTML)); + const std::string full_html = jstemplate_builder::GetTemplateHtml( + downloads_html, &localized_strings, "t"); + + 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); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// DownloadsDOMHandler +// +/////////////////////////////////////////////////////////////////////////////// + +// Sort DownloadItems into descending order by their start time. +class DownloadItemSorter : public std::binary_function<DownloadItem*, + DownloadItem*, + bool> { + public: + bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) { + return lhs->start_time() > rhs->start_time(); + } +}; + +DownloadsDOMHandler::DownloadsDOMHandler(DOMUI* dom_ui, DownloadManager* dlm) + : DOMMessageHandler(dom_ui), + download_manager_(dlm), + search_text_() { + dom_ui_->RegisterMessageCallback("getDownloads", + NewCallback(this, &DownloadsDOMHandler::HandleGetDownloads)); + dom_ui_->RegisterMessageCallback("openFile", + NewCallback(this, &DownloadsDOMHandler::HandleOpenFile)); + + dom_ui_->RegisterMessageCallback("drag", + NewCallback(this, &DownloadsDOMHandler::HandleDrag)); + + dom_ui_->RegisterMessageCallback("saveDangerous", + NewCallback(this, &DownloadsDOMHandler::HandleSaveDangerous)); + dom_ui_->RegisterMessageCallback("discardDangerous", + NewCallback(this, &DownloadsDOMHandler::HandleDiscardDangerous)); + dom_ui_->RegisterMessageCallback("show", + NewCallback(this, &DownloadsDOMHandler::HandleShow)); + dom_ui_->RegisterMessageCallback("pause", + NewCallback(this, &DownloadsDOMHandler::HandlePause)); + dom_ui_->RegisterMessageCallback("cancel", + NewCallback(this, &DownloadsDOMHandler::HandleCancel)); + + // Create our fileicon data source. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + new FileIconSource())); +} + +DownloadsDOMHandler::~DownloadsDOMHandler() { + ClearDownloadItems(); + download_manager_->RemoveObserver(this); +} + +// DownloadsDOMHandler, public: ----------------------------------------------- + +void DownloadsDOMHandler::Init() { + download_manager_->AddObserver(this); +} + +void DownloadsDOMHandler::OnDownloadUpdated(DownloadItem* download) { + // Get the id for the download. Our downloads are sorted latest to first, + // and the id is the index into that list. We should be careful of sync + // errors between the UI and the download_items_ list (we may wish to use + // something other than 'id'). + OrderedDownloads::iterator it = find(download_items_.begin(), + download_items_.end(), + download); + if (it == download_items_.end()) + return; + const int id = static_cast<int>(it - download_items_.begin()); + + ListValue results_value; + results_value.Append(CreateDownloadItemValue(download, id)); + dom_ui_->CallJavascriptFunction(L"downloadUpdated", results_value); +} + +// A download has started or been deleted. Query our DownloadManager for the +// current set of downloads, which will call us back in SetDownloads once it +// has retrieved them. +void DownloadsDOMHandler::ModelChanged() { + ClearDownloadItems(); + download_manager_->GetDownloads(this, search_text_); +} + +void DownloadsDOMHandler::SetDownloads( + std::vector<DownloadItem*>& downloads) { + ClearDownloadItems(); + + // Swap new downloads in. + download_items_.swap(downloads); + sort(download_items_.begin(), download_items_.end(), DownloadItemSorter()); + + // Scan for any in progress downloads and add ourself to them as an observer. + for (OrderedDownloads::iterator it = download_items_.begin(); + it != download_items_.end(); ++it) { + DownloadItem* download = *it; + if (download->state() == DownloadItem::IN_PROGRESS) { + // We want to know what happens as the download progresses. + download->AddObserver(this); + } else if (download->safety_state() == DownloadItem::DANGEROUS) { + // We need to be notified when the user validates the dangerous download. + download->AddObserver(this); + } + } + + SendCurrentDownloads(); +} + +void DownloadsDOMHandler::HandleGetDownloads(const Value* value) { + std::wstring new_search = ExtractStringValue(value); + if (search_text_.compare(new_search) != 0) { + search_text_ = new_search; + ClearDownloadItems(); + download_manager_->GetDownloads(this, search_text_); + } else { + SendCurrentDownloads(); + } +} + +void DownloadsDOMHandler::HandleOpenFile(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + download_manager_->OpenDownloadInShell(file, NULL); +} + +void DownloadsDOMHandler::HandleDrag(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) { + IconManager* im = g_browser_process->icon_manager(); + SkBitmap* icon = im->LookupIcon(file->full_path().ToWStringHack(), + IconLoader::NORMAL); + download_util::DragDownload(file, icon); + } +} + +void DownloadsDOMHandler::HandleSaveDangerous(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + download_manager_->DangerousDownloadValidated(file); +} + +void DownloadsDOMHandler::HandleDiscardDangerous(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + file->Remove(true); +} + +void DownloadsDOMHandler::HandleShow(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + download_manager_->ShowDownloadInShell(file); +} + +void DownloadsDOMHandler::HandlePause(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + file->TogglePause(); +} + +void DownloadsDOMHandler::HandleCancel(const Value* value) { + DownloadItem* file = GetDownloadByValue(value); + if (file) + file->Cancel(true); +} + +// DownloadsDOMHandler, private: ---------------------------------------------- + +void DownloadsDOMHandler::SendCurrentDownloads() { + ListValue results_value; + + for (OrderedDownloads::iterator it = download_items_.begin(); + it != download_items_.end(); ++it) { + results_value.Append(CreateDownloadItemValue(*it, + static_cast<int>(it - download_items_.begin()))); + } + + dom_ui_->CallJavascriptFunction(L"downloadsList", results_value); +} + +DictionaryValue* DownloadsDOMHandler::CreateDownloadItemValue( + DownloadItem* download, int id) { + DictionaryValue* file_value = new DictionaryValue(); + + file_value->SetInteger(L"time", + static_cast<int>(download->start_time().ToTimeT())); + file_value->SetInteger(L"id", id); + file_value->SetString(L"file_path", download->full_path().ToWStringHack()); + file_value->SetString(L"file_name", download->GetFileName().ToWStringHack()); + file_value->SetString(L"url", download->url().spec()); + + if (download->state() == DownloadItem::IN_PROGRESS) { + if (download->safety_state() == DownloadItem::DANGEROUS) { + file_value->SetString(L"state", L"DANGEROUS"); + } else if (download->is_paused()) { + file_value->SetString(L"state", L"PAUSED"); + } else { + file_value->SetString(L"state", L"IN_PROGRESS"); + } + file_value->SetInteger(L"speed", + static_cast<int>(download->CurrentSpeed())); + file_value->SetInteger(L"percent", + static_cast<int>(download->PercentComplete())); + file_value->SetInteger(L"received", + static_cast<int>(download->received_bytes())); + } else if (download->state() == DownloadItem::CANCELLED) { + file_value->SetString(L"state", L"CANCELLED"); + } else if (download->state() == DownloadItem::COMPLETE) { + if (download->safety_state() == DownloadItem::DANGEROUS) { + file_value->SetString(L"state", L"DANGEROUS"); + } else { + file_value->SetString(L"state", L"COMPLETE"); + } + } + + file_value->SetInteger(L"total", + static_cast<int>(download->total_bytes())); + + return file_value; +} + +void DownloadsDOMHandler::ClearDownloadItems() { + // Clear out old state and remove self as observer for each download. + for (OrderedDownloads::iterator it = download_items_.begin(); + it != download_items_.end(); ++it) { + (*it)->RemoveObserver(this); + } + download_items_.clear(); +} + +DownloadItem* DownloadsDOMHandler::GetDownloadById(int id) { + for (OrderedDownloads::iterator it = download_items_.begin(); + it != download_items_.end(); ++it) { + if (static_cast<int>(it - download_items_.begin() == id)) { + return (*it); + } + } + + return NULL; +} + +DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const Value* value) { + int id; + if (ExtractIntegerValue(value, &id)) { + return GetDownloadById(id); + } + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// DownloadsUI +// +/////////////////////////////////////////////////////////////////////////////// + +DownloadsUI::DownloadsUI(DOMUIContents* contents) : DOMUI(contents) { +} + +void DownloadsUI::Init() { + DownloadManager* dlm = get_profile()->GetDownloadManager(); + + DownloadsDOMHandler* handler = new DownloadsDOMHandler(this, dlm); + AddMessageHandler(handler); + handler->Init(); + + DownloadsUIHTMLSource* html_source = new DownloadsUIHTMLSource(); + + // Set up the chrome-ui://downloads/ source. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + html_source)); +} + +// static +GURL DownloadsUI::GetBaseURL() { + std::string url = DOMUIContents::GetScheme(); + url += "://"; + url += kDownloadsHost; + return GURL(url); +} + diff --git a/chrome/browser/dom_ui/downloads_ui.h b/chrome/browser/dom_ui/downloads_ui.h new file mode 100644 index 0000000..9209000 --- /dev/null +++ b/chrome/browser/dom_ui/downloads_ui.h @@ -0,0 +1,123 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_DOM_UI_DOWNLOADS_UI_H_ +#define CHROME_BROWSER_DOM_UI_DOWNLOADS_UI_H_ + +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/dom_ui/dom_ui.h" +#include "chrome/browser/dom_ui/dom_ui_contents.h" +#include "chrome/browser/download/download_manager.h" + +class GURL; + +class DownloadsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + DownloadsUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + DISALLOW_COPY_AND_ASSIGN(DownloadsUIHTMLSource); +}; + +// The handler for Javascript messages related to the "downloads" view, +// also observes changes to the download manager. +class DownloadsDOMHandler : public DOMMessageHandler, + public DownloadManager::Observer, + public DownloadItem::Observer { + public: + explicit DownloadsDOMHandler(DOMUI* dom_ui, DownloadManager* dlm); + virtual ~DownloadsDOMHandler(); + + void Init(); + + // DownloadItem::Observer interface + virtual void OnDownloadUpdated(DownloadItem* download); + + // DownloadManager::Observer interface + virtual void ModelChanged(); + virtual void SetDownloads(std::vector<DownloadItem*>& downloads); + + // Callback for the "getDownloads" message. + void HandleGetDownloads(const Value* value); + + // Callback for the "openFile" message - opens the file in the shell. + void HandleOpenFile(const Value* value); + + // Callback for the "drag" message - initiates a file object drag. + void HandleDrag(const Value* value); + + // Callback for the "saveDangerous" message - specifies that the user + // wishes to save a dangerous file. + void HandleSaveDangerous(const Value* value); + + // Callback for the "discardDangerous" message - specifies that the user + // wishes to discard (remove) a dangerous file. + void HandleDiscardDangerous(const Value* value); + + // Callback for the "show" message - shows the file in explorer. + void HandleShow(const Value* value); + + // Callback for the "pause" message - pauses the file download. + void HandlePause(const Value* value); + + // Callback for the "cancel" message - cancels the download. + void HandleCancel(const Value* value); + + private: + // Send the current list of downloads to the page. + void SendCurrentDownloads(); + + // Creates a representation of a download in a format that the downloads + // HTML page can understand. + DictionaryValue* CreateDownloadItemValue(DownloadItem* download, int id); + + // Clear all download items and their observers. + void ClearDownloadItems(); + + // Return the download that corresponds to a given id. + DownloadItem* GetDownloadById(int id); + + // Return the download that is referred to in a given value. + DownloadItem* GetDownloadByValue(const Value* value); + + // Current search text. + std::wstring search_text_; + + // Our model + DownloadManager* download_manager_; + + // The current set of visible DownloadItems for this view received from the + // DownloadManager. DownloadManager owns the DownloadItems. The vector is + // kept in order, sorted by ascending start time. + typedef std::vector<DownloadItem*> OrderedDownloads; + OrderedDownloads download_items_; + + DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandler); +}; + +class DownloadsUI : public DOMUI { + public: + explicit DownloadsUI(DOMUIContents* contents); + + // Return the URL for the front page of this UI. + static GURL GetBaseURL(); + + // DOMUI Implementation + virtual void Init(); + + private: + DOMUIContents* contents_; + + DISALLOW_COPY_AND_ASSIGN(DownloadsUI); +}; + +#endif // CHROME_BROWSER_DOM_UI_DOWNLOADS_UI_H_ + diff --git a/chrome/browser/dom_ui/fileicon_source.cc b/chrome/browser/dom_ui/fileicon_source.cc new file mode 100644 index 0000000..36fb30f --- /dev/null +++ b/chrome/browser/dom_ui/fileicon_source.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 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/dom_ui/fileicon_source.h" + +#include "base/gfx/png_encoder.h" +#include "base/string_util.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/time_format.h" + +#include "generated_resources.h" + +// The path used in internal URLs to file icon data. +static const char kFileIconPath[] = "fileicon"; + +FileIconSource::FileIconSource() + : DataSource(kFileIconPath, MessageLoop::current()) {} + +FileIconSource::~FileIconSource() { + cancelable_consumer_.CancelAllRequests(); +} + +void FileIconSource::StartDataRequest(const std::string& path, + int request_id) { + IconManager* im = g_browser_process->icon_manager(); + + // Fast look up. + SkBitmap* icon = im->LookupIcon(UTF8ToWide(path), IconLoader::NORMAL); + + if (icon) { + std::vector<unsigned char> png_bytes; + PNGEncoder::EncodeBGRASkBitmap(*icon, false, &png_bytes); + + scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes(png_bytes); + SendResponse(request_id, icon_data); + } else { + // Icon was not in cache, go fetch it slowly. + IconManager::Handle h = im->LoadIcon(UTF8ToWide(path), IconLoader::NORMAL, + &cancelable_consumer_, + NewCallback(this, &FileIconSource::OnFileIconDataAvailable)); + + // Attach the ChromeURLDataManager request ID to the history request. + cancelable_consumer_.SetClientData(im, h, request_id); + } +} + +void FileIconSource::OnFileIconDataAvailable(IconManager::Handle handle, + SkBitmap* icon) { + IconManager* im = g_browser_process->icon_manager(); + int request_id = cancelable_consumer_.GetClientData(im, handle); + + if (icon) { + std::vector<unsigned char> png_bytes; + PNGEncoder::EncodeBGRASkBitmap(*icon, false, &png_bytes); + + scoped_refptr<RefCountedBytes> icon_data = new RefCountedBytes(png_bytes); + SendResponse(request_id, icon_data); + } else { + // TODO(glen): send a dummy icon. + SendResponse(request_id, NULL); + } +}
\ No newline at end of file diff --git a/chrome/browser/dom_ui/fileicon_source.h b/chrome/browser/dom_ui/fileicon_source.h new file mode 100644 index 0000000..e56996c --- /dev/null +++ b/chrome/browser/dom_ui/fileicon_source.h @@ -0,0 +1,45 @@ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_BROWSER_DOM_UI_FILEICON_SOURCE_H_ +#define CHROME_BROWSER_DOM_UI_FILEICON_SOURCE_H_ + +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/icon_manager.h" +#include "chrome/common/resource_bundle.h" + +class GURL; + +// FileIconSource is the gateway between network-level chrome: +// requests for favicons and the history backend that serves these. +class FileIconSource : public ChromeURLDataManager::DataSource { + public: + explicit FileIconSource(); + virtual ~FileIconSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, int request_id); + + virtual std::string GetMimeType(const std::string&) const { + // Rely on image decoder inferring the correct type. + return std::string(); + } + + // Called when favicon data is available from the history backend. + void OnFileIconDataAvailable( + IconManager::Handle request_handle, + SkBitmap* icon); + + private: + CancelableRequestConsumerT<int, 0> cancelable_consumer_; + + // Raw PNG representation of the favicon to show when the favicon + // database doesn't have a favicon for a webpage. + scoped_refptr<RefCountedBytes> default_favicon_; + + DISALLOW_COPY_AND_ASSIGN(FileIconSource); +}; +#endif // CHROME_BROWSER_DOM_UI_FILEICON_SOURCE_H_ + diff --git a/chrome/browser/resources/downloads.html b/chrome/browser/resources/downloads.html new file mode 100644 index 0000000..5c38db8 --- /dev/null +++ b/chrome/browser/resources/downloads.html @@ -0,0 +1,551 @@ +<!DOCTYPE HTML> +<html id="t"> +<head> +<meta charset="utf-8"> +<title jscontent="title"></title> +<style type="text/css"> +body { + font-family:arial; + background-color:white; + color:black; + font-size:84%; + margin:10px; +} +.header { + overflow:auto; + clear:both; +} +.header .logo { + float:left; +} +.header .form { + float:left; + margin-top:22px; + margin-left:12px; +} +#downloads-summary { + margin-top:12px; + border-top:1px solid #9cc2ef; + background-color:#ebeff9; + font-weight:bold; + padding:3px; + margin-bottom:6px; +} +#downloads-display { + max-width:740px; +} +.download { + position:relative; + padding-left:45px; + margin-bottom:16px; +} +.download .icon { + position:absolute; + top:2px; + left:2px; + width:32px; + height:32px; +} +.name { + display:none; + padding-right:16px; + max-width:450px; + text-overflow:ellipsis; +} +.download .status { + display:inline; + color:#999; +} +.download .url { + color:#080; + width:500px; + white-space:nowrap; + overflow:hidden; + text-overflow:ellipsis; +} +.controls a { + color:#777; +} +#downloads-pagination { + padding-top:24px; + margin-left:18px; +} +.page-navigation { + padding:8px; + background-color:#ebeff9; + margin-right:4px; +} +.footer { + height:24px; +} +</style> +<script type="text/javascript"> +/////////////////////////////////////////////////////////////////////////////// +// localStrings: +/** + * We get strings into the page by using JSTemplate to populate some elements + * with localized content, then reading the content of those elements into + * this global strings object. + * @param {Node} node The DOM node containing all our strings. + */ +function LocalStrings(node) { + this.strings_ = {}; + + var children = node.childNodes; + for (var i = 0, child; child = children[i]; i++) { + var id = child.id; + if (id) { + this.strings_[id] = child.innerHTML; + } + } +} + +/** + * Gets a localized string by its id. + * @param {string} s The id of the string we want + * @return {string} The localized string + */ +LocalStrings.prototype.getString = function(s) { + return (s in this.strings_) ? this.strings_[s] : ''; +} + +/** + * Returns a formatted localized string (where all %s contents are replaced + * by the second argument). + * @param {string} s The id of the string we want + * @param {string} d The string to include in the formatted string + * @return {string} The formatted string. + */ +LocalStrings.prototype.formatString = function(s, d) { + return (s in this.strings_) ? this.strings_[s].replace(/\%s/, d) : ''; +} + +/////////////////////////////////////////////////////////////////////////////// +// Helper functions +function $(o) {return document.getElementById(o);} + +function bind(fn, selfObj, var_args) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + var args = Array.prototype.slice.call(arguments); + args.unshift.apply(args, boundArgs); + return fn.apply(selfObj, args); + } +} + +/** + * Sets the display style of a node. + */ +function showInline(node, isShow) { + node.style.display = isShow ? 'inline' : 'none'; +} + +/** + * Creates an element of a specified type with a specified class name. + * @param {String} type The node type. + * @param {String} className The class name to use. + */ +function createElementWithClassName(type, className) { + var elm = document.createElement(type); + elm.className = className; + return elm; +} + +/** + * Creates a link with a specified onclick handler and content + * @param {String} onclick The onclick handler + * @param {String} value The link text + */ +function createLink(onclick, value) { + var link = document.createElement("a"); + link.onclick = onclick; + link.href = '#'; + link.innerHTML = value; + return link; +} + +/** + * Creates a button with a specified onclick handler and content + * @param {String} onclick The onclick handler + * @param {String} value The button text + */ +function createButton(onclick, value) { + var button = document.createElement("input"); + button.type = 'button'; + button.value = value; + button.onclick = onclick; + return button; +} + +/////////////////////////////////////////////////////////////////////////////// +// Downloads +/** + * Class to hold all the information about the visible downloads. + */ +function Downloads() { + this.downloads_ = {}; + this.node_ = $('downloads-display'); + this.summary_ = $('downloads-summary'); + this.searchText_ = ""; +} + +/** + * Called when a download has been updated or added. + * @param {Object} download A backend download object (see downloads_ui.cc) + */ +Downloads.prototype.updated = function(download) { + var id = download.id; + if (!!this.downloads_[id]) { + this.downloads_[id].update(download); + } else { + this.downloads_[id] = new Download(download); + // We get downloads in display order, so we don't have to worry about + // maintaining correct order for now. + this.node_.insertBefore(this.downloads_[id].node, + this.node_.firstChild); + } +} + +/** + * Set our display search text. + * @param {String} searchText The string we're searching for. + */ +Downloads.prototype.setSearchText = function(searchText) { + this.searchText_ = searchText; +} + +/** + * Update the summary block above the results + */ +Downloads.prototype.updateSummary = function() { + if (this.searchText_) { + this.summary_.innerHTML = localStrings.formatString( + 'searchresultsfor', this.searchText_); + } else { + this.summary_.innerHTML = localStrings.getString('downloads'); + } + + var hasDownloads = false; + for (var i in this.downloads_) { + hasDownloads = true; + break; + } + + if (!hasDownloads) { + this.node_.innerHTML = localStrings.getString('noresults'); + } +} + +/** + * Remove a download. + * @param {Number} id The id of the download to remove. + */ +Downloads.prototype.remove = function(id) { + this.node_.removeChild(this.downloads_[id].node); + delete this.downloads_[id]; +} + +/** + * Clear all downloads and reset us back to a null state. + */ +Downloads.prototype.clear = function() { + for (var id in this.downloads_) { + this.downloads_[id].clear(); + this.remove(id); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Download +/** + * A download and the DOM representation for that download. + * @param {Object} download A backend download object (see downloads_ui.cc) + */ +function Download(download) { + // Create DOM + this.node = createElementWithClassName('div', 'download'); + + // Container for all 'safe download' UI. + this.safe_ = document.createElement("div"); + this.safe_.ondragstart = bind(this.drag_, this); + this.node.appendChild(this.safe_); + + this.nodeImg_ = createElementWithClassName('img', 'icon'); + this.safe_.appendChild(this.nodeImg_); + + // FileLink is used for completed downloads, otherwise we show FileName. + this.nodeTitleArea_ = createElementWithClassName('div', 'title-area'); + this.safe_.appendChild(this.nodeTitleArea_); + + this.nodeFileLink_ = createLink(bind(this.openFile_, this), ''); + this.nodeFileLink_.className = 'name'; + this.nodeFileLink_.style.display = 'none'; + this.nodeTitleArea_.appendChild(this.nodeFileLink_); + + this.nodeFileName_ = createElementWithClassName('span', 'name'); + this.nodeFileName_.style.display = 'none'; + this.nodeTitleArea_.appendChild(this.nodeFileName_); + + this.nodeStatus_ = createElementWithClassName('span', 'status'); + this.nodeTitleArea_.appendChild(this.nodeStatus_); + + this.nodeURL_ = createElementWithClassName('div', 'url'); + this.safe_.appendChild(this.nodeURL_); + + // Controls. + this.nodeControls_ = createElementWithClassName('div', 'controls'); + this.safe_.appendChild(this.nodeControls_); + + this.controlShow_ = createLink(bind(this.show_, this), + localStrings.getString('control_showinfolder')); + this.nodeControls_.appendChild(this.controlShow_); + + this.controlPause_ = createLink(bind(this.pause_, this), + localStrings.getString('control_pause')); + this.nodeControls_.appendChild(this.controlPause_); + + this.controlCancel_ = createLink(bind(this.cancel_, this), + localStrings.getString('control_cancel')); + this.nodeControls_.appendChild(this.controlCancel_); + + // Container for 'unsafe download' UI. + this.danger_ = createElementWithClassName('div', 'show-dangerous'); + this.node.appendChild(this.danger_); + + this.dangerDesc_ = document.createElement("div"); + this.danger_.appendChild(this.dangerDesc_); + + this.dangerSave_ = createButton(bind(this.saveDangerous_, this), + localStrings.getString("danger_save")); + this.danger_.appendChild(this.dangerSave_); + + this.dangerDiscard_ = createButton(bind(this.discardDangerous_, this), + localStrings.getString("danger_discard")); + this.danger_.appendChild(this.dangerDiscard_); + + // Update member vars. + this.update(download); +} + +/** + * The states a download can be in. These correspond to states defined in + * DownloadsDOMHandler::CreateDownloadItemValue + */ +Download.States = { + IN_PROGRESS : "IN_PROGRESS", + CANCELLED : "CANCELLED", + COMPLETE : "COMPLETE", + PAUSED : "PAUSED", + DANGEROUS : "DANGEROUS", +} + +/** + * Updates the download to reflect new data. + * @param {Object} download A backend download object (see downloads_ui.cc) + */ +Download.prototype.update = function(download) { + this.id_ = download.id; + this.filePath_ = download.file_path; + this.fileName_ = download.file_name; + this.url_ = download.url; + this.state_ = download.state; + this.percent_ = download.percent; + this.speed_ = download.speed; + this.received_ = download.received; + + if (this.state_ == Download.States.DANGEROUS) { + this.dangerDesc_.innerHTML = localStrings.formatString('danger_desc', + this.fileName_); + this.danger_.style.display = 'block'; + this.safe_.style.display = 'none'; + } else { + this.nodeImg_.src = 'chrome-ui://fileicon/' + this.filePath_; + + if (this.state_ == Download.States.COMPLETE) { + this.nodeFileLink_.innerHTML = this.fileName_; + this.nodeFileLink_.href = this.filePath_; + } else { + this.nodeFileName_.innerHTML = this.fileName_; + } + + showInline(this.nodeFileLink_, this.state_ == Download.States.COMPLETE); + showInline(this.nodeFileName_, this.state_ != Download.States.COMPLETE); + + showInline(this.controlShow_, this.state_ == Download.States.COMPLETE); + showInline(this.controlPause_, this.state_ == Download.States.IN_PROGRESS); + showInline(this.controlCancel_, this.state_ == Download.States.IN_PROGRESS); + + this.nodeURL_.innerHTML = this.url_; + this.nodeStatus_.innerHTML = this.getStatusText_(); + + this.danger_.style.display = 'none'; + this.safe_.style.display = 'block'; + } +} + +/** + * Removes applicable bits from the DOM in preparation for deletion. + */ +Download.prototype.clear = function() { + this.safe_.ondragstart = null; + this.nodeFileLink_.onclick = null; + this.controlShow_.onclick = null; + this.controlCancel_.onclick = null; + this.controlPause_.onclick = null; + this.dangerDiscard_.onclick = null; + + this.node.innerHTML = ''; +} + +/** + * @return {String} User-visible status update text. + */ +Download.prototype.getStatusText_ = function() { + switch (this.state_) { + case Download.States.IN_PROGRESS: + // TODO(glen): Make pretty, localize, etc. + return this.percent_ + '% at ' + this.speed_; + case Download.States.CANCELLED: + return localStrings.getString('status_cancelled'); + case Download.States.PAUSED: + return localStrings.getString('status_paused'); + case Download.States.DANGEROUS: + return localStrings.getString('danger_desc'); + case Download.States.COMPLETE: + return ""; + } +} + +/** + * Tells the backend to initiate a drag, allowing users to drag + * files from the download page and have them appear as native file + * drags. + */ +Download.prototype.drag_ = function() { + chrome.send("drag", [this.id_.toString()]); + return false; +} + +/** + * Tells the backend to open this file. + */ +Download.prototype.openFile_ = function() { + chrome.send("openFile", [this.id_.toString()]); + return false; +} + +/** + * Tells the backend that the user chose to save a dangerous file. + */ +Download.prototype.saveDangerous_ = function() { + chrome.send("saveDangerous", [this.id_.toString()]); + return false; +} + +/** + * Tells the backend that the user chose to discard a dangerous file. + */ +Download.prototype.discardDangerous_ = function() { + chrome.send("discardDangerous", [this.id_.toString()]); + downloads.remove(this.id_); + return false; +} + +/** + * Tells the backend to show the file in explorer. + */ +Download.prototype.show_ = function() { + chrome.send("show", [this.id_.toString()]); + return false; +} + +/** + * Tells the backend to pause this download. + */ +Download.prototype.pause_ = function() { + chrome.send("pause", [this.id_.toString()]); + return false; +} + +/** + * Tells the backend to cancel this download. + */ +Download.prototype.cancel_ = function() { + chrome.send("cancel", [this.id_.toString()]); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Page: +var downloads, localStrings; + +function load() { + localStrings = new LocalStrings($('l10n')); + downloads = new Downloads(); + setSearch(""); +} + +function setSearch(searchText) { + downloads.clear(); + downloads.setSearchText(searchText); + chrome.send("getDownloads", [searchText.toString()]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Chrome callbacks: +/** + * Our history system calls this function with results from searches or when + * downloads are added or removed. + */ +function downloadsList(results) { + downloadUpdated(results); + downloads.updateSummary(); +} + +/** + * When a download is updated (progress, state change), this is called. + */ +function downloadUpdated(results) { + for (var i = 0, thisResult; thisResult = results[i]; i++) { + downloads.updated(thisResult); + } +} +</script> +</head> +<body onload="load();"> +<div class="header"> + <a href="" onclick="setSearch(''); return false;"> + <img src="../../app/theme/downloads_section.png" + width="67" height="67" class="logo" border="0" /></a> + <form method="post" action="" + onsubmit="setSearch(this.term.value); return false;" + class="form"> + <input type="text" name="term" id="term" /> + <input type="submit" name="submit" jsvalues="value:searchbutton" /> + </form> +</div> +<div class="main"> + <div id="downloads-summary"></div> + <div id="downloads-display"></div> +</div> +<div class="footer"> +</div> +<div id="l10n" style="display:none;"> + <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '%s'</span> + <span id="no_results" jscontent="no_results">No results found.</span> + <span id="downloads" jscontent="downloads">Downloads</span> + + <span id="status_cancelled" jscontent="status_cancelled">Cancelled</span> + <span id="status_paused" jscontent="status_paused">Paused</span> + + <span id="danger_desc" jscontent="danger_desc">This is a dangerous file</span> + <span id="danger_save" jscontent="danger_save">Save</span> + <span id="danger_discard" jscontent="danger_discard">Discard</span> + + <span id="control_pause" jscontent="control_pause">Pause</span> + <span id="control_showinfolder" jscontent="control_showinfolder">Show in folder</span> + <span id="control_cancel" jscontent="control_cancel">Cancel</span> + <span id="control_resume" jscontent="control_resume">Resume</span> +</div> +</body> +</html>
\ No newline at end of file |