summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.vcproj16
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/dom_ui/dom_ui.cc46
-rw-r--r--chrome/browser/dom_ui/dom_ui.h7
-rw-r--r--chrome/browser/dom_ui/dom_ui_contents.cc5
-rw-r--r--chrome/browser/dom_ui/downloads_ui.cc369
-rw-r--r--chrome/browser/dom_ui/downloads_ui.h123
-rw-r--r--chrome/browser/dom_ui/fileicon_source.cc63
-rw-r--r--chrome/browser/dom_ui/fileicon_source.h45
-rw-r--r--chrome/browser/resources/downloads.html551
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