diff options
34 files changed, 1850 insertions, 230 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 3c9a45f..492ecea 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -178,32 +178,63 @@ each locale. --> <message name="IDS_VIEW_SOURCE_TITLE" desc="The ViewSource tab title."> Source of <ph name="PAGE_URL">%s</ph> </message> - <message name="IDS_HISTORY_SEARCH_BUTTON" desc="Title of the button in the history page that triggers a search"> - Search history + + + <message name="IDS_HISTORY_SEARCH_STRING" desc="Text showing what the user searched for"> + Search Results for '<ph name="SEARCH_STRING"> + $1<ex>pie</ex> + </ph>' </message> - <message name="IDS_HISTORY_NO_RESULTS" desc="Text shown when no history search results have been found"> - No search results found. + <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_LINK" desc="Title of the link that allows the user to delete visits prior to the specified point"> + Delete history for this day </message> - <message name="IDS_HISTORY_SEARCH_STRING" desc="Text showing what the user searched for"> - Search Results for '<ph name="SEARCH_STRING">$1<ex>pie</ex></ph>' + <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING" desc="Warning shown before deleting"> + Are you sure you want to delete these pages from your history? </message> - <message name="IDS_HISTORY_NO_ITEMS" desc="Text shown when the user has no history"> - All the pages you visit will appear here unless you open them in an incognito window. You can use the Search button on this page to search all the pages in your history. + <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING_TITLE" desc="Title of the warning dialog"> + Delete </message> + + <!-- History HTML UI --> <message name="IDS_HISTORY_LOADING" desc="Text shown when we're loading the user's history"> Loading... </message> <message name="IDS_HISTORY_DATE_WITH_RELATIVE_TIME" desc="In the history view, some dates are formatted as 'Today - Wednesday, Nov 7, 2007"> - <ph name="RELATIVE_DATE">$1<ex>Today</ex></ph> - <ph name="FULL_DATE">$2<ex>Wednesday, Nov 7, 2007</ex></ph> + <ph name="RELATIVE_DATE"> + $1<ex>Today</ex> + </ph> - <ph name="FULL_DATE"> + $2<ex>Wednesday, Nov 7, 2007</ex> + </ph> </message> - <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_LINK" desc="Title of the link that allows the user to delete visits prior to the specified point"> - Delete history for this day + <message name="IDS_HISTORY_NO_RESULTS" desc="Text shown when no history search results have been found"> + No search results found. </message> - <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING" desc="Warning shown before deleting"> - Are you sure you want to delete these pages from your history? + <message name="IDS_HISTORY_NO_ITEMS" desc="Text shown when the user has no history"> + All the pages you visit will appear here unless you open them in an incognito window. You can use the Search button on this page to search all the pages in your history. </message> - <message name="IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING_TITLE" desc="Title of the warning dialog"> - Delete + <message name="IDS_HISTORY_SEARCH_BUTTON" desc="Title of the button in the history page that triggers a search"> + Search history + </message> + <message name="IDS_HISTORY_NEWEST" desc="HTML text shown as page navigation tool to take the user to the top of their history"> + Newest + </message> + <message name="IDS_HISTORY_NEWER" desc="HTML text shown as page navigation tool to take the user back to the more recent page"> + Newer + </message> + <message name="IDS_HISTORY_OLDER" desc="HTML text shown as page navigation tool to take the user forward to their older history"> + Older + </message> + <message name="IDS_HISTORY_SEARCHRESULTSFOR" desc="Format string for search results"> + Search results for '<ph name="SEARCH_STRING">%s</ph>' + </message> + <message name="IDS_HISTORY_BROWSERESULTS" desc="Title of browsing results page"> + History + </message> + <message name="IDS_HISTORY_CONTINUED" desc="Shown after the date if the data is continued from the previous page"> + (Cont.) + </message> + <message name="IDS_HISTORY_DELETE" desc="Link shown after the date, allowing the user to delete that date"> + delete </message> <!-- Generic terms --> diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 7c91fe1..e853e3a 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -1866,6 +1866,22 @@ >
</File>
<File
+ RelativePath=".\dom_ui\dom_ui.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dom_ui\dom_ui.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dom_ui\dom_ui_contents.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dom_ui\dom_ui_contents.h"
+ >
+ </File>
+ <File
RelativePath=".\dom_ui\dom_ui_host.cc"
>
</File>
@@ -1874,6 +1890,14 @@ >
</File>
<File
+ RelativePath=".\dom_ui\history_ui.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dom_ui\history_ui.h"
+ >
+ </File>
+ <File
RelativePath=".\dom_ui\html_dialog_contents.cc"
>
</File>
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index 4922118..6b0e025 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -199,7 +199,7 @@ bool BrowserAboutHandler::MaybeHandle(GURL* url, return false; *result_type = TAB_CONTENTS_ABOUT_UI; - std::string about_url = "chrome-resource://about/"; + std::string about_url = "chrome://about/"; about_url.append(url->path()); *url = GURL(about_url); return true; diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 66c5f7f..9e9b258 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -536,7 +536,7 @@ int BrowserMain(CommandLine &parsed_command_line, // Config the network module so it has access to resources. net::NetModule::SetResourceProvider(NetResourceProvider); - // Register our global network handler for chrome-resource:// URLs. + // Register our global network handler for chrome:// URLs. RegisterURLRequestChromeJob(); browser_process->InitBrokerServices(broker_services); diff --git a/chrome/browser/browser_resources.h b/chrome/browser/browser_resources.h index e4a31af..dd1fdd1 100644 --- a/chrome/browser/browser_resources.h +++ b/chrome/browser/browser_resources.h @@ -10,4 +10,5 @@ #define IDR_SAFE_BROWSING_MALWARE_BLOCK 209 #define IDR_SAFE_BROWSING_PHISHING_BLOCK 210 #define IDR_INCOGNITO_TAB_HTML 211 -#define IDR_CREDITS_HTML 212 +#define IDR_CREDITS_HTML 212 +#define IDR_HISTORY_HTML 213 diff --git a/chrome/browser/browser_resources.rc b/chrome/browser/browser_resources.rc index 206bf4a..8ce4e17 100644 --- a/chrome/browser/browser_resources.rc +++ b/chrome/browser/browser_resources.rc @@ -28,4 +28,5 @@ IDR_NEW_TAB_HTML BINDATA "browser_resources\\new_tab_flat.html" IDR_SAFE_BROWSING_MALWARE_BLOCK BINDATA "browser_resources\\safe_browsing_malware_block_flat.html" IDR_SAFE_BROWSING_PHISHING_BLOCK BINDATA "browser_resources\\safe_browsing_phishing_block_flat.html" IDR_INCOGNITO_TAB_HTML BINDATA "browser_resources\\incognito_tab_flat.html" -IDR_CREDITS_HTML BINDATA "browser_resources\\about_credits_flat.html"
\ No newline at end of file +IDR_CREDITS_HTML BINDATA "browser_resources\\about_credits_flat.html" +IDR_HISTORY_HTML BINDATA "browser_resources\\history_flat.html"
\ No newline at end of file diff --git a/chrome/browser/browser_url_handler.cc b/chrome/browser/browser_url_handler.cc index 1b82105..555d065 100644 --- a/chrome/browser/browser_url_handler.cc +++ b/chrome/browser/browser_url_handler.cc @@ -6,6 +6,7 @@ #include "chrome/browser/browser_about_handler.h" #include "chrome/browser/dom_ui/new_tab_ui.h" +#include "chrome/browser/dom_ui/dom_ui_contents.h" std::vector<BrowserURLHandler::URLHandler> BrowserURLHandler::url_handlers_; @@ -17,8 +18,10 @@ void BrowserURLHandler::InitURLHandlers() { // Here is where we initialize the global list of handlers for special URLs. // about:* url_handlers_.push_back(&BrowserAboutHandler::MaybeHandle); - // chrome:* + // chrome-internal:* url_handlers_.push_back(&NewTabUIHandleURL); + // chrome:* + url_handlers_.push_back(&DOMUIContentsCanHandleURL); } // static diff --git a/chrome/browser/browsing_instance.cc b/chrome/browser/browsing_instance.cc index 88c4088..542b4c0 100644 --- a/chrome/browser/browsing_instance.cc +++ b/chrome/browser/browsing_instance.cc @@ -27,7 +27,7 @@ bool BrowsingInstance::ShouldUseProcessPerSite(const GURL& url) { // Note that --single-process may have been specified, but that affects the // process creation logic in RenderProcessHost, so we do not need to worry // about it here. - if (url.SchemeIs("chrome-resource")) + if (url.SchemeIs("chrome")) // Always consolidate instances of the new tab page (and instances of any // other internal resource urls). return true; diff --git a/chrome/browser/debugger/debugger_contents.cc b/chrome/browser/debugger/debugger_contents.cc index 37cb1d0..a6dd254 100644 --- a/chrome/browser/debugger/debugger_contents.cc +++ b/chrome/browser/debugger/debugger_contents.cc @@ -20,7 +20,7 @@ class DebuggerHTMLSource : public ChromeURLDataManager::DataSource { public: // Creates our datasource and sets our user message to a specific message // from our string bundle. - DebuggerHTMLSource() + DebuggerHTMLSource() : DataSource("debugger", MessageLoop::current()) { } // Called when the network layer has requested a resource underneath @@ -38,7 +38,7 @@ class DebuggerHTMLSource : public ChromeURLDataManager::DataSource { SendResponse(request_id, NULL); return; } - + std::wstring debugger_path = CommandLine().GetSwitchValue(switches::kJavaScriptDebuggerPath); std::string data_str; @@ -126,6 +126,6 @@ void DebuggerContents::AttachMessageHandlers() { // static bool DebuggerContents::IsDebuggerUrl(const GURL& url) { - return (url.SchemeIs("chrome-resource") && url.host() == "inspector"); + return (url.SchemeIs("chrome") && url.host() == "inspector"); } diff --git a/chrome/browser/debugger/debugger_view.cc b/chrome/browser/debugger/debugger_view.cc index 5fd62f1..530ffbf 100644 --- a/chrome/browser/debugger/debugger_view.cc +++ b/chrome/browser/debugger/debugger_view.cc @@ -105,8 +105,7 @@ void DebuggerView::OnInit() { web_container_->SetTabContents(web_contents_); web_contents_->render_view_host()->AllowDOMUIBindings(); - GURL contents("chrome-resource://inspector/debugger.html"); - + GURL contents("chrome://inspector/debugger.html"); web_contents_->controller()->LoadURL(contents, GURL(), PageTransition::START_PAGE); } @@ -130,7 +129,7 @@ void DebuggerView::OpenURLFromTab(TabContents* source, } -void DebuggerView::SendEventToPage(const std::wstring& name, +void DebuggerView::SendEventToPage(const std::wstring& name, Value* body) { DictionaryValue msg; msg.SetString(L"type", L"event"); @@ -150,7 +149,7 @@ void DebuggerView::SendEventToPage(const std::wstring& name, } void DebuggerView::ExecuteJavascript(const std::string& js) { - web_contents_->render_view_host()->ExecuteJavascriptInWebFrame(L"", + web_contents_->render_view_host()->ExecuteJavascriptInWebFrame(L"", UTF8ToWide(js)); } diff --git a/chrome/browser/dom_ui/chrome_url_data_manager.cc b/chrome/browser/dom_ui/chrome_url_data_manager.cc index b82506a..623ad8d 100644 --- a/chrome/browser/dom_ui/chrome_url_data_manager.cc +++ b/chrome/browser/dom_ui/chrome_url_data_manager.cc @@ -24,8 +24,8 @@ #endif // The URL scheme used for internal chrome resources. -// This URL scheme is never needed by code external to this module. -static const char kChromeURLScheme[] = "chrome-resource"; +// TODO(glen): Choose a better location for this. +static const char kChromeURLScheme[] = "chrome"; // The single global instance of ChromeURLDataManager. ChromeURLDataManager chrome_url_data_manager; @@ -123,7 +123,7 @@ void ChromeURLDataManager::URLToRequest(const GURL& url, return; } - // Our input looks like: chrome-resource://source_name/extra_bits?foo . + // Our input looks like: chrome://source_name/extra_bits?foo . // So the url's "host" is our source, and everything after the host is // the path. source_name->assign(url.host()); diff --git a/chrome/browser/dom_ui/chrome_url_data_manager.h b/chrome/browser/dom_ui/chrome_url_data_manager.h index 895ff46..102ab99 100644 --- a/chrome/browser/dom_ui/chrome_url_data_manager.h +++ b/chrome/browser/dom_ui/chrome_url_data_manager.h @@ -16,7 +16,7 @@ class MessageLoop; class URLRequest; class URLRequestJob; -// To serve dynamic data off of chrome-resource: URLs, implement the +// To serve dynamic data off of chrome: URLs, implement the // ChromeURLDataManager::DataSource interface and register your handler // with AddDataSource. diff --git a/chrome/browser/dom_ui/dom_ui.cc b/chrome/browser/dom_ui/dom_ui.cc new file mode 100644 index 0000000..4fe9070 --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui.cc @@ -0,0 +1,124 @@ +// 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/dom_ui.h" + +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "chrome/common/l10n_util.h" + +/////////////////////////////////////////////////////////////////////////////// +// DOMMessageHandler + +DOMUI::DOMUI(DOMUIContents* contents) : contents_(contents) { +} + +DOMUI::~DOMUI() { + STLDeleteContainerPairSecondPointers(message_callbacks_.begin(), + message_callbacks_.end()); + STLDeleteContainerPointers(handlers_.begin(), handlers_.end()); +} + +// DOMUI, public: ------------------------------------------------------------- + +void DOMUI::ProcessDOMUIMessage(const std::string& message, + const std::string& content) { + // Look up the callback for this message. + MessageCallbackMap::const_iterator callback = + message_callbacks_.find(message); + if (callback == message_callbacks_.end()) + return; + + // Convert the content JSON into a Value. + Value* value = NULL; + if (!content.empty()) { + if (!JSONReader::Read(content, &value, false)) { + // The page sent us something that we didn't understand. + // This probably indicates a programming error. + NOTREACHED(); + return; + } + } + + // Forward this message and content on. + callback->second->Run(value); + delete value; +} + +void DOMUI::CallJavascriptFunction(const std::wstring& function_name, + const Value& arg) { + std::string json; + JSONWriter::Write(&arg, false, &json); + std::wstring javascript = function_name + L"(" + UTF8ToWide(json) + L");"; + + ExecuteJavascript(javascript); +} + +void DOMUI::CallJavascriptFunction( + const std::wstring& function_name, + const Value& arg1, const Value& arg2) { + std::string json; + JSONWriter::Write(&arg1, false, &json); + std::wstring javascript = function_name + L"(" + UTF8ToWide(json); + JSONWriter::Write(&arg2, false, &json); + javascript += L"," + UTF8ToWide(json) + L");"; + + ExecuteJavascript(javascript); +} + +void DOMUI::RegisterMessageCallback(const std::string &message, + MessageCallback *callback) { + message_callbacks_.insert(std::make_pair(message, callback)); +} + +// DOMUI, protected: ---------------------------------------------------------- + +void DOMUI::AddMessageHandler(DOMMessageHandler* handler) { + handlers_.push_back(handler); +} + +// DOMUI, private: ------------------------------------------------------------ + +void DOMUI::ExecuteJavascript(const std::wstring& javascript) { + DCHECK(contents_); + contents_->render_view_host()->ExecuteJavascriptInWebFrame(std::wstring(), + javascript); +} + +/////////////////////////////////////////////////////////////////////////////// +// DOMMessageHandler + +DOMMessageHandler::DOMMessageHandler(DOMUI *dom_ui) : dom_ui_(dom_ui) { +} + +// DOMMessageHandler, protected: ---------------------------------------------- + +void DOMMessageHandler::SetURLAndTitle(DictionaryValue* dictionary, + std::wstring title, + const GURL& gurl) { + std::wstring wstring_url = UTF8ToWide(gurl.spec()); + dictionary->SetString(L"url", wstring_url); + + bool using_url_as_the_title = false; + if (title.empty()) { + using_url_as_the_title = true; + title = wstring_url; + } + + // Since the title can contain BiDi text, we need to mark the text as either + // RTL or LTR, depending on the characters in the string. If we use the URL + // as the title, we mark the title as LTR since URLs are always treated as + // left to right strings. + std::wstring title_to_set(title); + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) { + if (using_url_as_the_title) { + l10n_util::WrapStringWithLTRFormatting(&title_to_set); + } else { + bool success = + l10n_util::AdjustStringForLocaleDirection(title, &title_to_set); + DCHECK(success ? (title != title_to_set) : (title == title_to_set)); + } + } + dictionary->SetString(L"title", title_to_set); +}
\ No newline at end of file diff --git a/chrome/browser/dom_ui/dom_ui.h b/chrome/browser/dom_ui/dom_ui.h new file mode 100644 index 0000000..7b0b446 --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui.h @@ -0,0 +1,86 @@ +// 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_H__ +#define CHROME_BROWSER_DOM_UI_H__ + +#include "base/task.h" +#include "chrome/browser/dom_ui/dom_ui_contents.h" + +class Value; +class DOMMessageHandler; + +// A DOMUI sets up the datasources and message handlers for a given HTML-based +// UI. It is contained by a DOMUIContents. +class DOMUI { + public: + DOMUI(DOMUIContents* contents); + + virtual ~DOMUI(); + virtual void Init() = 0; + + // Called from DOMUIContents. + void ProcessDOMUIMessage(const std::string& message, + const std::string& content); + + // Used by DOMMessageHandlers. + typedef Callback1<const Value*>::Type MessageCallback; + void RegisterMessageCallback (const std::string& message, + MessageCallback* callback); + + // Call a Javascript function by sending its name and arguments down to + // the renderer. This is asynchronous; there's no way to get the result + // 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, + const Value& arg); + void CallJavascriptFunction(const std::wstring& function_name, + const Value& arg1, + const Value& arg2); + + Profile* get_profile() { return contents_->profile(); } + + protected: + void AddMessageHandler(DOMMessageHandler* handler); + + DOMUIContents* contents_; + + private: + // Execute a string of raw Javascript on the page. + void ExecuteJavascript(const std::wstring& javascript); + + // The DOMMessageHandlers we own. + std::vector<DOMMessageHandler*> handlers_; + + // A map of message name -> message handling callback. + typedef std::map<std::string, MessageCallback*> MessageCallbackMap; + MessageCallbackMap message_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(DOMUI); +}; + +// Messages sent from the DOM are forwarded via the DOMUIContents to handler +// classes. These objects are owned by DOMUIHost and destroyed when the +// host is destroyed. +class DOMMessageHandler { + public: + explicit DOMMessageHandler(DOMUI* dom_ui); + virtual ~DOMMessageHandler(); + + protected: + // Adds "url" and "title" keys on incoming dictionary, setting title + // as the url as a fallback on empty title. + static void SetURLAndTitle(DictionaryValue* dictionary, + std::wstring title, + const GURL& gurl); + + DOMUI* const dom_ui_; + + private: + DISALLOW_COPY_AND_ASSIGN(DOMMessageHandler); +}; + + +#endif // CHROME_BROWSER_DOM_UI_H__
\ No newline at end of file diff --git a/chrome/browser/dom_ui/dom_ui_contents.cc b/chrome/browser/dom_ui/dom_ui_contents.cc new file mode 100644 index 0000000..260c68f --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui_contents.cc @@ -0,0 +1,207 @@ +// 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/dom_ui_contents.h" + +#include "chrome/browser/dom_ui/dom_ui.h" +#include "chrome/browser/dom_ui/history_ui.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/common/resource_bundle.h" + +// The scheme used for DOMUIContentses +// TODO(glen): Merge this with the scheme in chrome_url_data_manager +static const char kURLScheme[] = "chrome"; + +// The path used in internal URLs to thumbnail data. +static const char kThumbnailPath[] = "thumb"; + +// The path used in internal URLs to favicon data. +static const char kFavIconPath[] = "favicon"; + +/////////////////////////////////////////////////////////////////////////////// +// FavIconSource + +FavIconSource::FavIconSource(Profile* profile) + : DataSource(kFavIconPath, MessageLoop::current()), profile_(profile) {} + +void FavIconSource::StartDataRequest(const std::string& path, int request_id) { + HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (hs) { + HistoryService::Handle handle; + if (path.size() > 8 && path.substr(0, 8) == "iconurl/") { + handle = hs->GetFavIcon( + GURL(path.substr(8)), + &cancelable_consumer_, + NewCallback(this, &FavIconSource::OnFavIconDataAvailable)); + } else { + handle = hs->GetFavIconForURL( + GURL(path), + &cancelable_consumer_, + NewCallback(this, &FavIconSource::OnFavIconDataAvailable)); + } + // Attach the ChromeURLDataManager request ID to the history request. + cancelable_consumer_.SetClientData(hs, handle, request_id); + } else { + SendResponse(request_id, NULL); + } +} + +void FavIconSource::OnFavIconDataAvailable( + HistoryService::Handle request_handle, + bool know_favicon, + scoped_refptr<RefCountedBytes> data, + bool expired, + GURL icon_url) { + HistoryService* hs = + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + int request_id = cancelable_consumer_.GetClientData(hs, request_handle); + + if (know_favicon && data.get() && !data->data.empty()) { + // Forward the data along to the networking system. + SendResponse(request_id, data); + } else { + if (!default_favicon_.get()) { + default_favicon_ = new RefCountedBytes; + ResourceBundle::GetSharedInstance().LoadImageResourceBytes( + IDR_DEFAULT_FAVICON, &default_favicon_->data); + } + + SendResponse(request_id, default_favicon_); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ThumbnailSource + +ThumbnailSource::ThumbnailSource(Profile* profile) + : DataSource(kThumbnailPath, MessageLoop::current()), profile_(profile) {} + +void ThumbnailSource::StartDataRequest(const std::string& path, + int request_id) { + HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (hs) { + HistoryService::Handle handle = hs->GetPageThumbnail( + GURL(path), + &cancelable_consumer_, + NewCallback(this, &ThumbnailSource::OnThumbnailDataAvailable)); + // Attach the ChromeURLDataManager request ID to the history request. + cancelable_consumer_.SetClientData(hs, handle, request_id); + } else { + // Tell the caller that no thumbnail is available. + SendResponse(request_id, NULL); + } +} + +void ThumbnailSource::OnThumbnailDataAvailable( + HistoryService::Handle request_handle, + scoped_refptr<RefCountedBytes> data) { + HistoryService* hs = + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); + int request_id = cancelable_consumer_.GetClientData(hs, request_handle); + // Forward the data along to the networking system. + if (data.get() && !data->data.empty()) { + SendResponse(request_id, data); + } else { + if (!default_thumbnail_.get()) { + default_thumbnail_ = new RefCountedBytes; + ResourceBundle::GetSharedInstance().LoadImageResourceBytes( + IDR_DEFAULT_THUMBNAIL, &default_thumbnail_->data); + } + + SendResponse(request_id, default_thumbnail_); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// DOMUIContents + +// This is the top-level URL handler for chrome: URLs, and exposed in +// our header file. The individual DOMUIs provide a chrome: +// HTML source at the same host/path. +bool DOMUIContentsCanHandleURL(GURL* url, + TabContentsType* result_type) { + if (!url->SchemeIs(kURLScheme)) + return false; + + // TODO: remove once the debugger is using DOMContentsUI + if (url->host().compare("debugger") == 0) + return false; + + *result_type = TAB_CONTENTS_DOM_UI; + return true; +} + +DOMUIContents::DOMUIContents(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory) + : WebContents(profile, + instance, + render_view_factory, + MSG_ROUTING_NONE, + NULL), + current_ui_(NULL) { + set_type(TAB_CONTENTS_DOM_UI); +} + +DOMUIContents::~DOMUIContents() { + if (current_ui_) + delete current_ui_; +} + +bool DOMUIContents::CreateRenderViewForRenderManager( + RenderViewHost* render_view_host) { + // Be sure to enable DOM UI bindings on the RenderViewHost before + // CreateRenderView is called. Since a cross-site transition may be + // involved, this may or may not be the same RenderViewHost that we had when + // we were created. + render_view_host->AllowDOMUIBindings(); + return WebContents::CreateRenderViewForRenderManager(render_view_host); +} + +WebPreferences DOMUIContents::GetWebkitPrefs() { + // Get the users preferences then force image loading to always be on. + WebPreferences web_prefs = WebContents::GetWebkitPrefs(); + web_prefs.loads_images_automatically = true; + web_prefs.javascript_enabled = true; + + return web_prefs; +} + +bool DOMUIContents::NavigateToPendingEntry(bool reload) { + if (current_ui_) { + // Shut down our existing DOMUI. + delete current_ui_; + current_ui_ = NULL; + } + + // Set up a new DOMUI. + NavigationEntry* pending_entry = controller()->GetPendingEntry(); + current_ui_ = GetDOMUIForURL(pending_entry->url()); + if (current_ui_) + current_ui_->Init(); + else + return false; + + // Let WebContents do whatever it's meant to do. + return WebContents::NavigateToPendingEntry(reload); +} + +DOMUI* DOMUIContents::GetDOMUIForURL(const GURL &url) { + if (url.host() == HistoryUI::GetBaseURL().host()) + return new HistoryUI(this); + + return NULL; +} + +void DOMUIContents::ProcessDOMUIMessage(const std::string& message, + const std::string& content) { + DCHECK(current_ui_); + current_ui_->ProcessDOMUIMessage(message, content); +} + +// static +const std::string DOMUIContents::GetScheme() { + return kURLScheme; +}
\ No newline at end of file diff --git a/chrome/browser/dom_ui/dom_ui_contents.h b/chrome/browser/dom_ui/dom_ui_contents.h new file mode 100644 index 0000000..c347c07 --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui_contents.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. + +// Contains code for managing local HTML UI at chrome:// URLs. + +#ifndef CHROME_BROWSER_DOM_UI_CONTENTS_H__ +#define CHROME_BROWSER_DOM_UI_CONTENTS_H__ + +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/web_contents.h" +#include "webkit/glue/webpreferences.h" + +class DOMUI; +class render_view_host; + +// FavIconSource is the gateway between network-level chrome: +// requests for favicons and the history backend that serves these. +class FavIconSource : public ChromeURLDataManager::DataSource { + public: + explicit FavIconSource(Profile* profile); + + // 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 OnFavIconDataAvailable( + HistoryService::Handle request_handle, + bool know_favicon, + scoped_refptr<RefCountedBytes> data, + bool expired, + GURL url); + + private: + Profile* const profile_; + 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(FavIconSource); +}; + +// ThumbnailSource is the gateway between network-level chrome: +// requests for thumbnails and the history backend that serves these. +class ThumbnailSource : public ChromeURLDataManager::DataSource { + public: + explicit ThumbnailSource(Profile* profile); + + // 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 thumbnail data is available from the history backend. + void OnThumbnailDataAvailable( + HistoryService::Handle request_handle, + scoped_refptr<RefCountedBytes> data); + + private: + Profile* const profile_; + CancelableRequestConsumerT<int, 0> cancelable_consumer_; + + // Raw PNG representation of the thumbnail to show when the thumbnail + // database doesn't have a thumbnail for a webpage. + scoped_refptr<RefCountedBytes> default_thumbnail_; + + DISALLOW_COPY_AND_ASSIGN(ThumbnailSource); +}; + +// Exposed for use by BrowserURLHandler. +bool DOMUIContentsCanHandleURL(GURL* url, TabContentsType* result_type); + +class DOMUIContents : public WebContents { + public: + DOMUIContents(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory); + ~DOMUIContents(); + + // + // WebContents overrides + // + virtual void ProcessDOMUIMessage(const std::string& message, + const std::string& content); + virtual bool CreateRenderViewForRenderManager( + RenderViewHost* render_view_host); + // Override this method so we can ensure that javascript and image loading + // are always on even for DOMUIHost tabs. + virtual WebPreferences GetWebkitPrefs(); + + // + // TabContents overrides + // + virtual void UpdateHistoryForNavigation(const GURL& url, + const ViewHostMsg_FrameNavigate_Params& params) { } + virtual bool NavigateToPendingEntry(bool reload); + + // Return the scheme used. We currently use chrome: + static const std::string GetScheme(); + + private: + // Return a DOM UI for the provided URL. + DOMUI* GetDOMUIForURL(const GURL& url); + + // The DOMUI we own and show. + DOMUI* current_ui_; + + DISALLOW_COPY_AND_ASSIGN(DOMUIContents); +}; + +#endif // CHROME_BROWSER_DOM_UI_CONTENTS_H__
\ No newline at end of file diff --git a/chrome/browser/dom_ui/history_ui.cc b/chrome/browser/dom_ui/history_ui.cc new file mode 100644 index 0000000..370417d --- /dev/null +++ b/chrome/browser/dom_ui/history_ui.cc @@ -0,0 +1,303 @@ +// 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/history_ui.h" + +#include "base/message_loop.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/browser_resources.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/user_metrics.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/time_format.h" + +#include "chromium_strings.h" +#include "generated_resources.h" + +using base::Time; + +// HistoryUI is accessible from chrome://history, and the raw HTML is +// accessed from chrome://history. +static const std::string kHistoryHost = "history"; + +// Maximum number of search results to return in a given search. We should +// eventually remove this. +static const int kMaxSearchResults = 100; + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUIHTMLSource::HistoryUIHTMLSource() + : DataSource(kHistoryHost, MessageLoop::current()) { +} + +void HistoryUIHTMLSource::StartDataRequest(const std::string& path, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_HISTORY_TITLE)); + localized_strings.SetString(L"loading", + l10n_util::GetString(IDS_HISTORY_LOADING)); + localized_strings.SetString(L"newest", + l10n_util::GetString(IDS_HISTORY_NEWEST)); + localized_strings.SetString(L"newer", + l10n_util::GetString(IDS_HISTORY_NEWER)); + localized_strings.SetString(L"older", + l10n_util::GetString(IDS_HISTORY_OLDER)); + localized_strings.SetString(L"searchresultsfor", + l10n_util::GetString(IDS_HISTORY_SEARCHRESULTSFOR)); + localized_strings.SetString(L"history", + l10n_util::GetString(IDS_HISTORY_BROWSERESULTS)); + localized_strings.SetString(L"cont", + l10n_util::GetString(IDS_HISTORY_CONTINUED)); + localized_strings.SetString(L"searchbutton", + l10n_util::GetString(IDS_HISTORY_SEARCH_BUTTON)); + localized_strings.SetString(L"noresults", + l10n_util::GetString(IDS_HISTORY_NO_RESULTS)); + localized_strings.SetString(L"noitems", + l10n_util::GetString(IDS_HISTORY_NO_ITEMS)); + localized_strings.SetString(L"delete", + l10n_util::GetString(IDS_HISTORY_DELETE)); + + static const StringPiece history_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_HISTORY_HTML)); + const std::string full_html = jstemplate_builder::GetTemplateHtml( + history_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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHandler +// +//////////////////////////////////////////////////////////////////////////////// +BrowsingHistoryHandler::BrowsingHistoryHandler(DOMUI* dom_ui) + : DOMMessageHandler(dom_ui), + search_text_() { + dom_ui_->RegisterMessageCallback("getHistory", + NewCallback(this, &BrowsingHistoryHandler::HandleGetHistory)); + + // Create our favicon data source. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + new FavIconSource(dom_ui_->get_profile()))); + + // Get notifications when history is cleared. + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_HISTORY_URLS_DELETED, + Source<Profile>(dom_ui_->get_profile())); +} + +BrowsingHistoryHandler::~BrowsingHistoryHandler() { + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_HISTORY_URLS_DELETED, + Source<Profile>(dom_ui_->get_profile())); +} + +void BrowsingHistoryHandler::HandleGetHistory(const Value* value) { + // Anything in-flight is invalid. + cancelable_consumer_.CancelAllRequests(); + + // Get arguments (if any). + int month; + std::wstring query; + ExtractGetHistoryArguments(value, &month, &query); + + // Set our query options. + history::QueryOptions options = CreateQueryOptions(month, query); + + // Need to remember the query string for our results. + search_text_ = query; + HistoryService* hs = + dom_ui_->get_profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QueryHistory(search_text_, + options, + &cancelable_consumer_, + NewCallback(this, &BrowsingHistoryHandler::QueryComplete)); +} + +void BrowsingHistoryHandler::QueryComplete( + HistoryService::Handle request_handle, + history::QueryResults* results) { + + ListValue results_value; + Time midnight_today = Time::Now().LocalMidnight(); + + for (size_t i = 0; i < results->size(); ++i) { + history::URLResult const &page = (*results)[i]; + DictionaryValue* page_value = new DictionaryValue(); + SetURLAndTitle(page_value, page.title(), page.url()); + + // Need to pass the time in epoch time (fastest JS conversion). + page_value->SetInteger(L"time", + static_cast<int>(page.visit_time().ToTimeT())); + + // Until we get some JS i18n infrastructure, we also need to + // pass the dates in as strings. This could use some + // optimization. + + // Only pass in the strings we need (search results need a shortdate + // and snippet, browse results need day and time information). + if (search_text_.empty()) { + // Figure out the relative date string. + std::wstring date_str = TimeFormat::RelativeDate(page.visit_time(), + &midnight_today); + if (date_str.empty()) { + date_str = base::TimeFormatFriendlyDate(page.visit_time()); + } else { + date_str = l10n_util::GetStringF( + IDS_HISTORY_DATE_WITH_RELATIVE_TIME, + date_str, base::TimeFormatFriendlyDate(page.visit_time())); + } + page_value->SetString(L"dateRelativeDay", date_str); + page_value->SetString(L"dateTimeOfDay", + base::TimeFormatTimeOfDay(page.visit_time())); + } else { + page_value->SetString(L"dateShort", + base::TimeFormatShortDate(page.visit_time())); + page_value->SetString(L"snippet", page.snippet().text()); + } + + results_value.Append(page_value); + } + + dom_ui_->CallJavascriptFunction(L"historyResult", + StringValue(search_text_), results_value); +} + +void BrowsingHistoryHandler::ExtractGetHistoryArguments(const Value* value, + int* month, std::wstring* query) { + *month = 0; + + if (value && value->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(value); + Value* list_member; + + // Get search string. + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + string_value->GetAsString(query); + } + + // Get search month. + if (list_value->Get(1, &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); + *month = _wtoi(wstring_value.c_str()); + } + } +} + +history::QueryOptions BrowsingHistoryHandler::CreateQueryOptions(int month, + const std::wstring& query) { + history::QueryOptions options; + + // Configure the begin point of the search to the start of the + // current month. + Time::Exploded exploded; + Time::Now().LocalMidnight().LocalExplode(&exploded); + exploded.day_of_month = 1; + + if (month == 0) { + options.begin_time = Time::FromLocalExploded(exploded); + + // Set the end time of this first search to null (which will + // show results from the future, should the user's clock have + // been set incorrectly). + options.end_time = Time(); + } else { + // Set the end-time of this search to the end of the month that is + // |depth| months before the search end point. The end time is not + // inclusive, so we should feel free to set it to midnight on the + // first day of the following month. + exploded.month -= month - 1; + while (exploded.month < 1) { + exploded.month += 12; + exploded.year--; + } + options.end_time = Time::FromLocalExploded(exploded); + + // Set the begin-time of the search to the start of the month + // that is |depth| months prior to search_start_. + if (exploded.month > 1) { + exploded.month--; + } else { + exploded.month = 12; + exploded.year--; + } + options.begin_time = Time::FromLocalExploded(exploded); + } + + // If searching, only show the most recent entry and limit the number of + // results returned. + if (!query.empty()) { + options.max_count = kMaxSearchResults; + options.most_recent_visit_only = true; + } + + return options; +} + +void BrowsingHistoryHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type != NOTIFY_HISTORY_URLS_DELETED) { + NOTREACHED(); + return; + } + + // Some URLs were deleted from history. Reload the most visited list. + HandleGetHistory(NULL); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryUIContents +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUI::HistoryUI(DOMUIContents* contents) : DOMUI(contents) { +} + +void HistoryUI::Init() { + AddMessageHandler(new BrowsingHistoryHandler(this)); + + HistoryUIHTMLSource* html_source = new HistoryUIHTMLSource(); + + // Set up the chrome://history/ source. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + html_source)); +} + +// static +GURL HistoryUI::GetBaseURL() { + std::string url = DOMUIContents::GetScheme(); + url += "://"; + url += kHistoryHost; + return GURL(url); +}
\ No newline at end of file diff --git a/chrome/browser/dom_ui/history_ui.h b/chrome/browser/dom_ui/history_ui.h new file mode 100644 index 0000000..742cf81 --- /dev/null +++ b/chrome/browser/dom_ui/history_ui.h @@ -0,0 +1,83 @@ +// 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_HISTORY_UI_H__ +#define CHROME_BROWSER_DOM_UI_HISTORY_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" + +class GURL; + +class HistoryUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + HistoryUIHTMLSource(); + + // 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(HistoryUIHTMLSource); +}; + +// The handler for Javascript messages related to the "history" view. +class BrowsingHistoryHandler : public DOMMessageHandler, + public NotificationObserver { + public: + explicit BrowsingHistoryHandler(DOMUI* dom_ui_); + virtual ~BrowsingHistoryHandler(); + + // Callback for the "getHistory" message. + void HandleGetHistory(const Value* value); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // Callback from the history system when the most visited list is available. + void QueryComplete(HistoryService::Handle request_handle, + history::QueryResults* results); + + // Extract the arguments from the call to HandleGetHistory. + void ExtractGetHistoryArguments(const Value* value, + int* month, + std::wstring* query); + + // Get the query options for a given month and query. + history::QueryOptions CreateQueryOptions(int month, + const std::wstring& query); + + // Current search text. + std::wstring search_text_; + + // Our consumer for the history service. + CancelableRequestConsumerT<PageUsageData*, NULL> cancelable_consumer_; + + DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryHandler); +}; + +class HistoryUI : public DOMUI { + public: + explicit HistoryUI(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(HistoryUI); +}; + +#endif // CHROME_BROWSER_DOM_UI_HISTORY_UI_H__
\ No newline at end of file diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc index 1775e1e..7d1aa82 100644 --- a/chrome/browser/dom_ui/new_tab_ui.cc +++ b/chrome/browser/dom_ui/new_tab_ui.cc @@ -10,6 +10,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_resources.h" +#include "chrome/browser/dom_ui/dom_ui_contents.h" #include "chrome/browser/history_tab_ui.h" #include "chrome/browser/history/page_usage_data.h" #include "chrome/browser/navigation_entry.h" @@ -36,12 +37,6 @@ using base::TimeTicks; // The URL scheme used for the new tab. static const char kNewTabUIScheme[] = "chrome-internal"; -// The path used in internal URLs to thumbnail data. -static const char kThumbnailPath[] = "thumb"; - -// The path used in internal URLs to favicon data. -static const char kFavIconPath[] = "favicon"; - // The number of most visited pages we show. const int kMostVisitedPages = 9; @@ -263,101 +258,6 @@ void IncognitoTabHTMLSource::StartDataRequest(const std::string& path, } /////////////////////////////////////////////////////////////////////////////// -// ThumbnailSource - -ThumbnailSource::ThumbnailSource(Profile* profile) - : DataSource(kThumbnailPath, MessageLoop::current()), profile_(profile) {} - -void ThumbnailSource::StartDataRequest(const std::string& path, - int request_id) { - HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (hs) { - HistoryService::Handle handle = hs->GetPageThumbnail( - GURL(path), - &cancelable_consumer_, - NewCallback(this, &ThumbnailSource::OnThumbnailDataAvailable)); - // Attach the ChromeURLDataManager request ID to the history request. - cancelable_consumer_.SetClientData(hs, handle, request_id); - } else { - // Tell the caller that no thumbnail is available. - SendResponse(request_id, NULL); - } -} - -void ThumbnailSource::OnThumbnailDataAvailable( - HistoryService::Handle request_handle, - scoped_refptr<RefCountedBytes> data) { - HistoryService* hs = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - int request_id = cancelable_consumer_.GetClientData(hs, request_handle); - // Forward the data along to the networking system. - if (data.get() && !data->data.empty()) { - SendResponse(request_id, data); - } else { - if (!default_thumbnail_.get()) { - default_thumbnail_ = new RefCountedBytes; - ResourceBundle::GetSharedInstance().LoadImageResourceBytes( - IDR_DEFAULT_THUMBNAIL, &default_thumbnail_->data); - } - - SendResponse(request_id, default_thumbnail_); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// FavIconSource - -FavIconSource::FavIconSource(Profile* profile) - : DataSource(kFavIconPath, MessageLoop::current()), profile_(profile) {} - -void FavIconSource::StartDataRequest(const std::string& path, int request_id) { - HistoryService* hs = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (hs) { - HistoryService::Handle handle; - if (path.size() > 8 && path.substr(0, 8) == "iconurl/") { - handle = hs->GetFavIcon( - GURL(path.substr(8)), - &cancelable_consumer_, - NewCallback(this, &FavIconSource::OnFavIconDataAvailable)); - } else { - handle = hs->GetFavIconForURL( - GURL(path), - &cancelable_consumer_, - NewCallback(this, &FavIconSource::OnFavIconDataAvailable)); - } - // Attach the ChromeURLDataManager request ID to the history request. - cancelable_consumer_.SetClientData(hs, handle, request_id); - } else { - SendResponse(request_id, NULL); - } -} - -void FavIconSource::OnFavIconDataAvailable( - HistoryService::Handle request_handle, - bool know_favicon, - scoped_refptr<RefCountedBytes> data, - bool expired, - GURL icon_url) { - HistoryService* hs = - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - int request_id = cancelable_consumer_.GetClientData(hs, request_handle); - - if (know_favicon && data.get() && !data->data.empty()) { - // Forward the data along to the networking system. - SendResponse(request_id, data); - } else { - if (!default_favicon_.get()) { - default_favicon_ = new RefCountedBytes; - ResourceBundle::GetSharedInstance().LoadImageResourceBytes( - IDR_DEFAULT_FAVICON, &default_favicon_->data); - } - - SendResponse(request_id, default_favicon_); - } -} - - -/////////////////////////////////////////////////////////////////////////////// // MostVisitedHandler MostVisitedHandler::MostVisitedHandler(DOMUIHost* dom_ui_host) @@ -847,7 +747,7 @@ bool NewTabUIHandleURL(GURL* url, return false; *result_type = TAB_CONTENTS_NEW_TAB_UI; - *url = GURL("chrome-resource://new-tab/"); + *url = GURL(DOMUIContents::GetScheme() + "://new-tab/"); return true; } diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h index 4995b4d..8f700b2 100644 --- a/chrome/browser/dom_ui/new_tab_ui.h +++ b/chrome/browser/dom_ui/new_tab_ui.h @@ -20,9 +20,9 @@ enum TabContentsType; // Return the URL for the new tab page. GURL NewTabUIURL(); -// If a |url| is a chrome: URL, this method sets up |url|, and |result_type| -// to the appropriate values for displaying the new tab page and returns true. -// Exposed for use by BrowserURLHandler. +// If a |url| is a chrome-internal: URL, this method sets up |url|, and +// |result_type| to the appropriate values for displaying the new tab page +// and returns true. Exposed for use by BrowserURLHandler. bool NewTabUIHandleURL(GURL* url, TabContentsType* result_type); // The following classes aren't used outside of new_tab_ui.cc but are @@ -69,71 +69,6 @@ class IncognitoTabHTMLSource : public ChromeURLDataManager::DataSource { DISALLOW_EVIL_CONSTRUCTORS(IncognitoTabHTMLSource); }; -// ThumbnailSource is the gateway between network-level chrome-resource: -// requests for thumbnails and the history backend that serves these. -class ThumbnailSource : public ChromeURLDataManager::DataSource { - public: - explicit ThumbnailSource(Profile* profile); - - // 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 thumbnail data is available from the history backend. - void OnThumbnailDataAvailable( - HistoryService::Handle request_handle, - scoped_refptr<RefCountedBytes> data); - - private: - Profile* profile_; - CancelableRequestConsumerT<int, 0> cancelable_consumer_; - - // Raw PNG representation of the thumbnail to show when the thumbnail - // database doesn't have a thumbnail for a webpage. - scoped_refptr<RefCountedBytes> default_thumbnail_; - - DISALLOW_EVIL_CONSTRUCTORS(ThumbnailSource); -}; - -// ThumbnailSource is the gateway between network-level chrome-resource: -// requests for favicons and the history backend that serves these. -class FavIconSource : public ChromeURLDataManager::DataSource { - public: - explicit FavIconSource(Profile* profile); - - // 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 OnFavIconDataAvailable( - HistoryService::Handle request_handle, - bool know_favicon, - scoped_refptr<RefCountedBytes> data, - bool expired, - GURL url); - - private: - Profile* profile_; - 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_EVIL_CONSTRUCTORS(FavIconSource); -}; - // The handler for Javascript messages related to the "most visited" view. class MostVisitedHandler : public DOMMessageHandler, public NotificationObserver { diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc index 4e64b55..99838a1 100644 --- a/chrome/browser/history/history.cc +++ b/chrome/browser/history/history.cc @@ -569,7 +569,7 @@ bool HistoryService::CanAddURL(const GURL& url) const { return false; if (url.SchemeIs("javascript") || - url.SchemeIs("chrome-resource") || + url.SchemeIs("chrome") || url.SchemeIs("view-source")) return false; diff --git a/chrome/browser/native_ui_contents.h b/chrome/browser/native_ui_contents.h index 54319b7..6f194f7 100644 --- a/chrome/browser/native_ui_contents.h +++ b/chrome/browser/native_ui_contents.h @@ -74,7 +74,7 @@ class NativeUIContents : public TabContents, views::View** focus_traversable_view); virtual views::RootView* GetContentsRootView() { return GetRootView(); } - // Return the scheme used. We currently use nativeui: + // Return the scheme used. We currently use chrome-nativeui: static std::string GetScheme(); // Register a NativeUIFactory for a given path. diff --git a/chrome/browser/render_view_context_menu_controller.cc b/chrome/browser/render_view_context_menu_controller.cc index 43836d2..e54868c 100644 --- a/chrome/browser/render_view_context_menu_controller.cc +++ b/chrome/browser/render_view_context_menu_controller.cc @@ -480,7 +480,7 @@ bool RenderViewContextMenuController::IsDevCommandEnabled(int id) const { return false; // Don't inspect inspector, new tab UI, etc. - if (active_entry->url().SchemeIs("chrome-resource")) + if (active_entry->url().SchemeIs("chrome")) return false; // Don't inspect about:network, about:memory, etc. diff --git a/chrome/browser/renderer_security_policy.cc b/chrome/browser/renderer_security_policy.cc index f72848a..32d29f8 100644 --- a/chrome/browser/renderer_security_policy.cc +++ b/chrome/browser/renderer_security_policy.cc @@ -195,9 +195,9 @@ void RendererSecurityPolicy::GrantInspectElement(int renderer_id) { if (state == security_state_.end()) return; - // The inspector is served from a chrome-resource: URL. In order to run the - // inspector, the renderer needs to be able to load chrome-resource URLs. - state->second->GrantScheme("chrome-resource"); + // The inspector is served from a chrome: URL. In order to run the + // inspector, the renderer needs to be able to load chrome URLs. + state->second->GrantScheme("chrome"); } void RendererSecurityPolicy::GrantDOMUIBindings(int renderer_id) { @@ -209,8 +209,8 @@ void RendererSecurityPolicy::GrantDOMUIBindings(int renderer_id) { state->second->GrantDOMUIBindings(); - // DOM UI bindings need the ability to request chrome-resource URLs. - state->second->GrantScheme("chrome-resource"); + // DOM UI bindings need the ability to request chrome URLs. + state->second->GrantScheme("chrome"); // DOM UI pages can contain links to file:// URLs. state->second->GrantScheme("file"); diff --git a/chrome/browser/renderer_security_policy_unittest.cc b/chrome/browser/renderer_security_policy_unittest.cc index 87e05cb..7d4318d 100644 --- a/chrome/browser/renderer_security_policy_unittest.cc +++ b/chrome/browser/renderer_security_policy_unittest.cc @@ -14,12 +14,12 @@ class RendererSecurityPolicyTest : public testing::Test { protected: // testing::Test virtual void SetUp() { - // In the real world, "chrome-resource" is a handled scheme. - URLRequest::RegisterProtocolFactory("chrome-resource", + // In the real world, "chrome" is a handled scheme. + URLRequest::RegisterProtocolFactory("chrome", &URLRequestTestJob::Factory); } virtual void TearDown() { - URLRequest::RegisterProtocolFactory("chrome-resource", NULL); + URLRequest::RegisterProtocolFactory("chrome", NULL); } }; @@ -70,7 +70,7 @@ TEST_F(RendererSecurityPolicyTest, StandardSchemesTest) { EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("view-cache:http://www.google.com/"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, - GURL("chrome-resource://foo/bar"))); + GURL("chrome://foo/bar"))); p->Remove(kRendererID); } @@ -203,7 +203,7 @@ TEST_F(RendererSecurityPolicyTest, CanUploadFiles) { TEST_F(RendererSecurityPolicyTest, CanServiceInspectElement) { RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); - GURL url("chrome-resource://inspector/inspector.html"); + GURL url("chrome://inspector/inspector.html"); p->Add(kRendererID); @@ -217,7 +217,7 @@ TEST_F(RendererSecurityPolicyTest, CanServiceInspectElement) { TEST_F(RendererSecurityPolicyTest, CanServiceDOMUIBindings) { RendererSecurityPolicy* p = RendererSecurityPolicy::GetInstance(); - GURL url("chrome-resource://thumb/http://www.google.com/"); + GURL url("chrome://thumb/http://www.google.com/"); p->Add(kRendererID); diff --git a/chrome/browser/resources/browser_resources.vcproj b/chrome/browser/resources/browser_resources.vcproj index d49ed50..0c5886b 100644 --- a/chrome/browser/resources/browser_resources.vcproj +++ b/chrome/browser/resources/browser_resources.vcproj @@ -80,6 +80,10 @@ >
</File>
<File
+ RelativePath=".\history.html"
+ >
+ </File>
+ <File
RelativePath=".\incognito_tab.html"
>
</File>
diff --git a/chrome/browser/resources/history.html b/chrome/browser/resources/history.html new file mode 100644 index 0000000..d86d6fe --- /dev/null +++ b/chrome/browser/resources/history.html @@ -0,0 +1,789 @@ +<!DOCTYPE HTML> +<html id="t"> +<head> +<meta charset="utf-8"> +<title jscontent="title"></title> +<script type="text/javascript"> +/////////////////////////////////////////////////////////////////////////////// +// Globals: +var RESULTS_PER_PAGE = 60; +var MAX_SEARCH_DEPTH = 18; + +// Amount of time between pageviews that we consider a 'break' in browsing, +// measured in milliseconds. +var BROWSING_GAP_TIME = 15 * 60 * 1000; + +function $(o) {return document.getElementById(o);} + +// TODO(glen): Get rid of these global references, replace with a controller +// or just make the classes own more of the page. +var historyModel; +var historyView; +var localStrings; +var pageState; + +/////////////////////////////////////////////////////////////////////////////// +// 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) : ''; +} + +/////////////////////////////////////////////////////////////////////////////// +// Page: +/** + * Class to hold all the information about an entry in our model. + * @param {Object} result An object containing the page's data. + * @param {boolean} continued Whether this page is on the same day as the + * page before it + */ +function Page(result, continued, model) { + this.model_ = model; + this.title_ = result.title; + this.url_ = result.url; + this.snippet_ = result.snippet || ""; + + // All the date information is public so that owners can compare properties of + // two items easily. + + // We get the time in seconds, but we want it in milliseconds. + this.time = new Date(result.time * 1000); + + // See comment in BrowsingHistoryHandler::QueryComplete - we won't always + // get all of these. + this.dateRelativeDay = result.dateRelativeDay || ""; + this.dateTimeOfDay = result.dateTimeOfDay || ""; + this.dateShort = result.dateShort || ""; + + // Whether this is the continuation of a previous day. + this.continued = continued; +} + +// Page, Public: -------------------------------------------------------------- +/** + * @return {string} Gets the HTML representation of the page + * for use in browse results. + */ +Page.prototype.getBrowseResultHTML = function() { + return '<div class="entry">' + + '<div class="time">' + + this.dateTimeOfDay + + '</div>' + + this.getTitleHTML_() + + '</div>'; +} + +/** + * @return {string} Gets the HTML representation of the page for + * use in search results. + */ +Page.prototype.getSearchResultHTML = function() { + return '<tr class="entry"><td valign="top">' + + '<div class="time">' + + this.dateShort + + '</div>' + + '</td><td valign="top">' + + this.getTitleHTML_() + + '<div class="snippet">' + + this.getHighlightedSnippet_() + + '</div>' + + '</td></tr>'; +} + +// Page, private: ------------------------------------------------------------- +/** + * @return {string} The page's snippet highlighted with the model's + * current search term. + */ +Page.prototype.getHighlightedSnippet_ = function() { + return Page.getHighlightedText_(this.snippet_, this.model_.getSearchText()); +} + +/** + * @return {string} Gets the page's title highlighted with the + * model's current search term. + */ +Page.prototype.getHighlightedTitle_ = function() { + return Page.getHighlightedText_(this.title_, this.model_.getSearchText()); +} + +/** + * @return {string} HTML for the title block. + */ +Page.prototype.getTitleHTML_ = function() { + return '<div class="title">' + + '<a ' + + 'href="' + this.url_ + '" ' + + 'style="background-image:url(chrome://favicon/' + + this.url_ + ')" ' + + '>' + + this.getHighlightedTitle_() + + '</a>' + + '</div>'; +} + +// Page, private, static: ----------------------------------------------------- +/** + * Case-insensitively highlights a string. + * @param {string} str The source string + * @param {string} opt_highlight The string to highlight with + * @return {string} The highlighted string + */ +Page.getHighlightedText_ = function(str, opt_highlight ) { + if (!opt_highlight) return str; + + var r = new RegExp(Page.pregQuote_(opt_highlight), 'gim'); + return str.replace(r, "<b>\$&</b>"); +} + +/** + * Quote a string so it can be used in a regular expression. + * @param {string} str The source string + * @return {string} The escaped string + */ +Page.pregQuote_ = function(str) {
+ return str.replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
+} + +/////////////////////////////////////////////////////////////////////////////// +// HistoryModel: +/** + * Global container for history data. Future optimizations might include + * allowing the creation of a HistoryModel for each search string, allowing + * quick flips back and forth between results. + * + * The history model is based around pages, and only fetching the data to + * fill the currently requested page. This is somewhat dependent on the view, + * and so future work may wish to change history model to operate on + * timeframe (day or week) based containers. + */ +function HistoryModel() { + this.clearModel_(); + this.view_; +} + +// HistoryModel, Public: ------------------------------------------------------ +/** + * Sets our current view that is called when the history model changes. + * @param {HistoryView} view The view to set our current view to. + */ +HistoryModel.prototype.setView = function(view) { + this.view_ = view; +} + +/** + * Start a new search - this will clear out our model. + * @param {String} searchText The text to search for + * @param {Number} opt_page The page to view - this is mostly used when setting + * up an initial view, use #requestPage otherwise. + */ +HistoryModel.prototype.setSearchText = function(searchText, opt_page) { + this.clearModel_(); + this.searchText_ = searchText; + this.requestedPage_ = opt_page ? opt_page : 0; + this.getSearchResults_(); +} + +/** + * @return {String} The current search text. + */ +HistoryModel.prototype.getSearchText = function() { + return this.searchText_; +} + +/** + * Tell the model that the view will want to see the current page. When + * the data becomes available, the model will call the view back. + * @page {Number} page The page we want to view. + */ +HistoryModel.prototype.requestPage = function(page) { + this.requestedPage_ = page; + this.updateSearch_(); +} + +/** + * Receiver for history query. + * @param {String} term The search term that the results are for. + * @param {Array} results A list of results + */ +HistoryModel.prototype.addResults = function(term, results) { + this.inFlight_ = false; + if (term != this.searchText_) { + // If our results aren't for our current search term, they're rubbish. + return; + } + + // Currently we assume we're getting things in date order. This needs to + // be updated if that ever changes. + if (results) { + var lastURL, lastDay; + var oldLength = this.pages_.length; + if (oldLength) { + var oldPage = this.pages_[oldLength - 1]; + lastURL = oldPage.url; + lastDay = oldPage.dateRelativeDay; + } + + for (var i = 0, thisResult; thisResult = results[i]; i++) { + var thisURL = thisResult.url; + var thisDay = thisResult.dateRelativeDay; + + // Remove adjacent duplicates. + if (!lastURL || lastURL != thisURL) { + // Figure out if this page is in the same day as the previous page, + // this is used to determine how day headers should be drawn. + this.pages_.push(new Page(thisResult, thisDay == lastDay, this)); + lastDay = thisDay; + lastURL = thisURL; + } + } + } + + this.updateSearch_(); +} + +/** + * @return {Number} The number of pages in the model. + */ +HistoryModel.prototype.getSize = function() { + return this.pages_.length; +} + +/** + * @return {boolean} Whether our history query has covered all of + * the user's history + */ +HistoryModel.prototype.isComplete = function() { + return this.complete_; +} + +/** + * Get a list of pages between specified index positions. + * @param {Number} start The start index + * @param {Number} end The end index + * @return {Array} A list of pages + */ +HistoryModel.prototype.getNumberedRange = function(start, end) { + if (start >= this.getSize()) + return []; + + var end = end > this.getSize() ? this.getSize() : end; + return this.pages_.slice(start, end); +} + +// HistoryModel, Private: ----------------------------------------------------- +HistoryModel.prototype.clearModel_ = function() { + this.inFlight_ = false; // Whether a query is inflight. + this.searchText_ = ''; + this.searchMonth_ = 0; + this.pages_ = []; // Date-sorted list of pages. + + // The page that the view wants to see - we only fetch slightly past this + // point. If the view requests a page that we don't have data for, we try + // to fetch it and call back when we're done. + this.requestedPage_ = 0; + + this.complete_ = false; +} + +/** + * Figure out if we need to do more searches to fill the currently requested + * page. If we think we can fill the page, call the view and let it know + * we're ready to show something. + */ +HistoryModel.prototype.updateSearch_ = function() { + if (this.searchMonth_ >= MAX_SEARCH_DEPTH) { + // We have maxed out. There will be no more data. + this.complete_ = true; + this.view_.onModelReady(); + } else { + // If we can't fill the requested page, ask for more data unless a request + // is still in-flight. + if (!this.canFillPage_(this.requestedPage_) && !this.inFlight_) { + this.getSearchResults_(this.searchMonth_ + 1); + } + + // If we have any data for the requested page, show it. + if (this.haveDataForPage_(this.requestedPage_)) { + this.view_.onModelReady(); + } + } +} + +/** + * Get search results for a selected month. Our history system is optimized + * for queries that don't cross month boundaries. + * + * TODO: Fix this for when the user's clock goes across month boundaries. + * @param {number} opt_month How many months back to do the search. + */ +HistoryModel.prototype.getSearchResults_ = function(opt_month) { + this.searchMonth_ = opt_month || 0; + chrome.send('getHistory', + [this.searchText_, String(this.searchMonth_)]); + this.inFlight_ = true; +} + +/** + * Check to see if we have data for a given page. + * @param {number} page The page number + * @return {boolean} Whether we have any data for the given page. + */ +HistoryModel.prototype.haveDataForPage_ = function(page) { + return (page * RESULTS_PER_PAGE < this.getSize()); +} + +/** + * Check to see if we have data to fill a page. + * @param {number} page The page number. + * @return {boolean} Whether we have data to fill the page. + */ +HistoryModel.prototype.canFillPage_ = function(page) { + return ((page + 1) * RESULTS_PER_PAGE <= this.getSize()); +} + +/////////////////////////////////////////////////////////////////////////////// +// HistoryView: +/** + * Functions and state for populating the page with HTML. This should one-day + * contain the view and use event handlers, rather than pushing HTML out and + * getting called externally. + * @param {HistoryModel} model The model backing this view. + */ +function HistoryView(model) { + this.summaryDiv_ = $('results-summary'); + this.summaryDiv_.innerHTML = localStrings.getString('loading'); + this.resultDiv_ = $('results-display'); + this.pageDiv_ = $('results-pagination'); + this.model_ = model + this.pageIndex_ = 0; + + this.model_.setView(this); +} + +// HistoryView, public: ------------------------------------------------------- +/** + * Do a search and optionally view a certain page. + * @param {string} term The string to search for. + * @param {number} opt_page The page we wish to view, only use this for + * setting up initial views, as this triggers a search. + */ +HistoryView.prototype.setSearch = function(term, opt_page) { + this.pageIndex_ = parseInt(opt_page || 0, 10); + window.scrollTo(0, 0); + this.model_.setSearchText(term, this.pageIndex_); + pageState.setUIState(term, this.pageIndex_); +} + +/** + * Switch to a specified page. + * @param {string} term The string to search for. + * @param {number} opt_page The page we wish to view. + */ +HistoryView.prototype.setPage = function(page) { + this.pageIndex_ = parseInt(page, 10); + window.scrollTo(0, 0); + this.model_.requestPage(page); + pageState.setUIState(this.model_.getSearchText(), this.pageIndex_); +} + +/** + * @return {number} The page number being viewed. + */ +HistoryView.prototype.getPage = function() { + return this.pageIndex_; +} + +/** + * Callback for the history model to let it know that it has data ready for us + * to view. + */ +HistoryView.prototype.onModelReady = function() { + this.displayResults_(); +} + +// HistoryView, private: ------------------------------------------------------ +/** + * Update the page with results. + */ +HistoryView.prototype.displayResults_ = function() { + var output = []; + var results = this.model_.getNumberedRange( + this.pageIndex_ * RESULTS_PER_PAGE, + this.pageIndex_ * RESULTS_PER_PAGE + RESULTS_PER_PAGE); + + if (this.model_.getSearchText()) { + output.push('<table class="results" cellspacing="0" ', + 'cellpadding="0" border="0">'); + for (var i = 0, page; page = results[i]; i++) { + output.push(page.getSearchResultHTML()); + } + output.push('</table>'); + } else { + var lastTime = Math.infinity; + for (var i = 0, page; page = results[i]; i++) { + // Break across day boundaries and insert gaps for browsing pauses. + var thisTime = page.time.getTime(); + if (page.continued && i == 0) { + output.push('<div class="day">' + page.dateRelativeDay + ' ' + + localStrings.getString('cont') + '</div>'); + } else if (!page.continued) { + output.push('<div class="day">' + page.dateRelativeDay + '</div>'); + } else if (lastTime - thisTime > BROWSING_GAP_TIME) { + output.push('<div class="gap"></div>'); + } + lastTime = thisTime; + + // Draw entry. + output.push(page.getBrowseResultHTML()); + } + } + this.resultDiv_.innerHTML = output.join(""); + + this.displaySummaryBar_(); + this.displayNavBar_(); +} + +/** + * Update the summary bar with descriptive text. + */ +HistoryView.prototype.displaySummaryBar_ = function() { + var searchText = this.model_.getSearchText(); + if (searchText != '') { + this.summaryDiv_.innerHTML = localStrings.formatString('searchresultsfor', + searchText); + } else { + this.summaryDiv_.innerHTML = localStrings.getString('history'); + } +} + +/** + * Update the pagination tools. + */ +HistoryView.prototype.displayNavBar_ = function() { + var navOutput = ''; + if (this.pageIndex_ > 0) { + navOutput += this.createPageNavHTML_(0, localStrings.getString('newest')); + navOutput += this.createPageNavHTML_(this.pageIndex_ - 1, + localStrings.getString('newer')); + } + if (this.model_.getSize() > (this.pageIndex_ + 1) * RESULTS_PER_PAGE) { + navOutput += this.createPageNavHTML_(this.pageIndex_ + 1, + localStrings.getString('older')); + } + this.pageDiv_.innerHTML = navOutput; +} + +/** + * Get the HTML representation of a page navigation link. + * @param {number} page The page index the navigation element should link to + * @param {string} name The text content of the link + * @return {string} HTML representation of the pagination link + */ +HistoryView.prototype.createPageNavHTML_ = function(page, name) { + var hashString = PageState.getHashString(this.model_.getSearchText(), page); + return '<a href="chrome://history/' + + (hashString ? '#' + hashString : '') + + '"' + + 'class="page-navigation"' + + 'onclick="setPage(' + page + '); return false;"' + + '>' + name + '</a>'; +} + +/////////////////////////////////////////////////////////////////////////////// +// State object: +/** + * An 'AJAX-history' implementation. + * @param {HistoryModel} model The model we're representing + * @param {HistoryView} view The view we're representing + */ +function PageState(model, view) { + // Enforce a singleton. + if (PageState.instance) { + return PageState.instance; + } + + this.model = model; + this.view = view; + + if (typeof this.checker_ != 'undefined' && this.checker_) { + clearInterval(this.checker_); + } + + // TODO(glen): Replace this with a bound method so we don't need + // public model and view. + this.checker_ = setInterval((function(state_obj) { + var hashData = state_obj.getHashData(); + + if (hashData.q != state_obj.model.getSearchText(term)) { + state_obj.view.setSearch(hashData.q, parseInt(hashData.p, 10)); + } else if (parseInt(hashData.p, 10) != state_obj.view.getPage()) { + state_obj.view.setPage(hashData.p); + } + }), 50, this); +} + +PageState.instance = null; + +/** + * @return {Object} An object containing parameters from our window hash. + */ +PageState.prototype.getHashData = function() { + var result = { + q : '', + p : 0 + }; + + if (!window.location.hash) { + return result; + } + + var hashSplit = window.location.hash.substr(1).split("&"); + for (var i = 0; i < hashSplit.length; i++) { + var pair = hashSplit[i].split("="); + if (pair.length > 1) { + result[pair[0]] = unescape(pair[1]); + } + } + + return result; +} + +/** + * Set the hash to a specified state, this will create an entry in the + * session history so the back button cycles through hash states, which + * are then picked up by our listener. + * @param {string} term The current search string. + * @param {string} page The page currently being viewed. + */ +PageState.prototype.setUIState = function(term, page) { + // Make sure the form looks pretty. + document.forms[0].term.value = term; + + var hash = PageState.getHashString(term, page); + if (window.location.hash.substr(1) != hash) { + window.location.hash = hash; + } +} + +/** + * Static method to get the hash string for a specified state + * @param {string} term The current search string. + * @param {string} page The page currently being viewed. + * @return {string} The string to be used in a hash. + */ +PageState.getHashString = function(term, page) { + var newHash = []; + if (term) { + newHash.push("q=" + escape(term)); + } + if (page) { + newHash.push("p=" + page); + } + + return newHash.join("&"); +} + +/////////////////////////////////////////////////////////////////////////////// +// Document Functions: +/** + * Window onload handler, sets up the page. + */ + function load() { + localStrings = new LocalStrings($('l10n')); + historyModel = new HistoryModel(); + historyView = new HistoryView(historyModel); + pageState = new PageState(historyModel, historyView); + + // Create default view. + var hashData = pageState.getHashData(); + historyView.setSearch(hashData.q, hashData.p); +} + +/** + * TODO(glen): Get rid of this function. + * Set the history view to a specified page. + * @param {String} term The string to search for + */ +function setSearch(term) { + if (historyView) { + historyView.setSearch(term); + } +} + +/** + * TODO(glen): Get rid of this function. + * Set the history view to a specified page. + * @param {number} page The page to set the view to. + */ +function setPage(page) { + if (historyView) { + historyView.setPage(page); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Chrome callbacks: +/** + * Our history system calls this function with results from searches. + */ +function historyResult(term, results) { + historyModel.addResults(term, results); +} +</script> +<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; +} +#results-summary { + margin-top:12px; + border-top:1px solid #9cc2ef; + background-color:#ebeff9; + font-weight:bold; + padding:3px; + margin-bottom:-8px; +} +#results-display { + max-width:740px; +} +.day { + margin-top:18px; + margin-left:3px; +} +.gap { + margin-left:18px; + width:15px; + border-right:1px solid #ddd; + height:14px; +} +.entry { + margin-left:18px; + margin-top:6px; + overflow:auto; +} +table.results { + margin-left:4px; +} +.entry .time { + color:#888; + float:left; + min-width:56px; + margin-right:5px; + padding-top:1px; +} +.entry .title { + max-width:600px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.results .time, .results .title { + margin-top:18px; +} +.entry .title a { + background-repeat:no-repeat; + background-size:16px; + background-position:0px 1px; + padding:1px 0px 4px 22px; +} +html[dir='rtl'] .entry .title a { + background-position:right; + padding-left:0px; + padding-right:22px; +} +#results-pagination { + padding-top:24px; + margin-left:18px; +} +.page-navigation { + padding:8px; + background-color:#ebeff9; + margin-right:4px; +} +.footer { + height:24px; +} +</style> +</head> +<body onload="load();"> +<div class="header"> + <a href="" onclick="setSearch(''); return false;"> + <img src="../../app/theme/history_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="results-summary"></div> + <div id="results-display"></div> + <div id="results-pagination"></div> +</div> +<div class="footer"> +</div> +<div id="l10n" style="display:none;"> + <span id="loading" jscontent="loading">Loading...</span> + <span id="newest" jscontent="newest">« Newest</span> + <span id="newer" jscontent="newer">‹ Newer</span> + <span id="older" jscontent="older">Older ›</span> + <span id="searchresultsfor" jscontent="searchresultsfor">Search results for '%s'</span> + <span id="history" jscontent="history">History</span> + <span id="cont" jscontent="cont">(cont.)</span> + <span id="noresults" jscontent="noresults">No results</span> + <span id="noitems" jscontent="noitems">No items</span> + <span id="delete" jscontent="delete">delete</span> +</div> +</body> +</html>
\ No newline at end of file diff --git a/chrome/browser/resources/new_tab.html b/chrome/browser/resources/new_tab.html index 8fe2429..dc0516b 100644 --- a/chrome/browser/resources/new_tab.html +++ b/chrome/browser/resources/new_tab.html @@ -180,7 +180,7 @@ html[dir='rtl'] #mostvisited td { -webkit-transition:all 0.12s; } .thumbnail-title { - background-image:url(chrome-resource://favicon/); + background-image:url(chrome://favicon/); display:block; background-repeat:no-repeat; background-size:16px; @@ -359,7 +359,7 @@ document.addEventListener('DOMContentLoaded', handleDOMContentLoaded); <form onsubmit="chrome.send('searchHistoryPage', [this.search.value]); return false;"> <input type="text" class="hint" name="search" - style="background-image:url(chrome-resource://favicon/);" + style="background-image:url(chrome://favicon/);" jsvalues="value:searchhistory" onfocus="handleInputFocus.apply(this);" onblur="handleInputBlur.apply(this);" /> @@ -416,12 +416,12 @@ function makeMostVisitedDOM(page, number) { /* Create the thumbnail */ var img_thumbnail = DOM('img', {className:'thumbnail'}); img_thumbnail.setAttribute('onload', "logEvent('image loaded');"); - img_thumbnail.src = 'chrome-resource://thumb/' + page.url; + img_thumbnail.src = 'chrome://thumb/' + page.url; /* Create the title */ var div_title = DOM('div', {className:'thumbnail-title'}); div_title.style.backgroundImage = - 'url("chrome-resource://favicon/' + page.url + '")'; + 'url("chrome://favicon/' + page.url + '")'; if (page.title) { div_title.appendChild(document.createTextNode(page.title)); } else { @@ -484,7 +484,7 @@ function makeSearchURL(url) { /* The HTML we want looks like this: <form> <input type="text" class="hint" - style="background-image:url(chrome-resource://favicon/"+url+");" + style="background-image:url(chrome://favicon/"+url+");" value="Search Wikipedia" onfocus="handleInputFocus();" onblur="handleInputBlur();" /> @@ -497,10 +497,10 @@ function makeSearchURL(url) { if (url.favIconURL) { input.style.backgroundImage = - 'url("chrome-resource://favicon/iconurl/' + url.favIconURL + '")'; + 'url("chrome://favicon/iconurl/' + url.favIconURL + '")'; } else { input.style.backgroundImage = - 'url("chrome-resource://favicon/http://' + url.short_name + '")'; + 'url("chrome://favicon/http://' + url.short_name + '")'; } input.onfocus = handleInputFocus; @@ -555,7 +555,7 @@ function renderRecentlyBookmarked(entries) { chrome.send("metrics", ["NTP_Bookmark" + i]) }, false); link.style.backgroundImage = - 'url("chrome-resource://favicon/' + entry.url + '")'; + 'url("chrome://favicon/' + entry.url + '")'; link.appendChild(document.createTextNode(entry.title)); container.appendChild(link); } @@ -593,7 +593,7 @@ function renderRecentlyClosedTabs(entries) { } }(entry.sessionId); - link.style.backgroundImage = 'url("chrome-resource://favicon/' + entry.url + '")'; + link.style.backgroundImage = 'url("chrome://favicon/' + entry.url + '")'; link.appendChild(document.createTextNode(entry.title)); container.appendChild(link); } diff --git a/chrome/browser/site_instance.cc b/chrome/browser/site_instance.cc index 63517d8..ec1eaf1 100644 --- a/chrome/browser/site_instance.cc +++ b/chrome/browser/site_instance.cc @@ -81,7 +81,7 @@ GURL SiteInstance::GetSiteForURL(const GURL& url) { GURL site; // TODO(creis): For many protocols, we should just treat the scheme as the - // site, since there is no host. e.g., file:, about:, chrome-resource: + // site, since there is no host. e.g., file:, about:, chrome: // If the url has a host, then determine the site. if (url.has_host()) { diff --git a/chrome/browser/tab_contents_factory.cc b/chrome/browser/tab_contents_factory.cc index 2488222..6a6abba 100644 --- a/chrome/browser/tab_contents_factory.cc +++ b/chrome/browser/tab_contents_factory.cc @@ -7,6 +7,7 @@ #include "chrome/browser/about_internets_status_view.h" #include "chrome/browser/browser_about_handler.h" #include "chrome/browser/browser_url_handler.h" +#include "chrome/browser/dom_ui/dom_ui_contents.h" #include "chrome/browser/dom_ui/html_dialog_contents.h" #include "chrome/browser/dom_ui/new_tab_ui.h" #include "chrome/browser/ipc_status_view.h" @@ -75,6 +76,9 @@ TabContents* TabContents::CreateWithType(TabContentsType type, case TAB_CONTENTS_DEBUGGER: contents = new DebuggerContents(profile, instance); break; + case TAB_CONTENTS_DOM_UI: + contents = new DOMUIContents(profile, instance, NULL); + break; default: if (g_extra_types) { TabContentsFactoryMap::const_iterator it = g_extra_types->find(type); @@ -119,6 +123,9 @@ TabContentsType TabContents::TypeForURL(GURL* url) { if (DebuggerContents::IsDebuggerUrl(*url)) return TAB_CONTENTS_DEBUGGER; + if (url->SchemeIs(DOMUIContents::GetScheme().c_str())) + return TAB_CONTENTS_DOM_UI; + if (url->SchemeIs("view-source")) { // Load the inner URL instead, but render it using a ViewSourceContents. *url = GURL(url->path()); diff --git a/chrome/browser/tab_contents_type.h b/chrome/browser/tab_contents_type.h index 462227f..86f38cc 100644 --- a/chrome/browser/tab_contents_type.h +++ b/chrome/browser/tab_contents_type.h @@ -23,6 +23,7 @@ enum TabContentsType { TAB_CONTENTS_HTML_DIALOG, TAB_CONTENTS_ABOUT_UI, TAB_CONTENTS_DEBUGGER, + TAB_CONTENTS_DOM_UI, TAB_CONTENTS_NUM_TYPES }; diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index ec8bce5..ec4fec0 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -100,7 +100,7 @@ static int32 next_page_id_ = 1; static const int kMaximumNumberOfUnacknowledgedPopups = 25; static const char* const kUnreachableWebDataURL = - "chrome-resource://chromewebdata/"; + "chrome://chromewebdata/"; static const char* const kBackForwardNavigationScheme = "history"; diff --git a/chrome/renderer/renderer_glue.cc b/chrome/renderer/renderer_glue.cc index 0d958cf..1fafed2 100644 --- a/chrome/renderer/renderer_glue.cc +++ b/chrome/renderer/renderer_glue.cc @@ -226,11 +226,11 @@ void webkit_glue::ClipboardReadHTML(std::wstring* markup, GURL* url) { } GURL webkit_glue::GetInspectorURL() { - return GURL("chrome-resource://inspector/inspector.html"); + return GURL("chrome://inspector/inspector.html"); } std::string webkit_glue::GetUIResourceProtocol() { - return "chrome-resource"; + return "chrome"; } bool webkit_glue::GetPlugins(bool refresh, diff --git a/webkit/port/bindings/v8/v8_proxy.cpp b/webkit/port/bindings/v8/v8_proxy.cpp index c785c6b..e4b4c91 100644 --- a/webkit/port/bindings/v8/v8_proxy.cpp +++ b/webkit/port/bindings/v8/v8_proxy.cpp @@ -1978,7 +1978,6 @@ bool V8Proxy::CanAccessPrivate(DOMWindow* target_window) if (active_security_origin->protocol() == ui_resource_protocol) { KURL inspector_url = ChromiumBridge::inspectorURL(); ASSERT(inspector_url.protocol() == ui_resource_protocol); - ASSERT(inspector_url.protocol().endsWith("-resource")); // The Inspector can access anything. if (active_security_origin->host() == inspector_url.host()) |