diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/dom_ui | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/dom_ui')
-rw-r--r-- | chrome/browser/dom_ui/chrome_url_data_manager.cc | 336 | ||||
-rw-r--r-- | chrome/browser/dom_ui/chrome_url_data_manager.h | 168 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui_host.cc | 139 | ||||
-rw-r--r-- | chrome/browser/dom_ui/dom_ui_host.h | 127 | ||||
-rw-r--r-- | chrome/browser/dom_ui/html_dialog_contents.cc | 101 | ||||
-rw-r--r-- | chrome/browser/dom_ui/html_dialog_contents.h | 97 | ||||
-rw-r--r-- | chrome/browser/dom_ui/new_tab_ui.cc | 852 | ||||
-rw-r--r-- | chrome/browser/dom_ui/new_tab_ui.h | 330 |
8 files changed, 2150 insertions, 0 deletions
diff --git a/chrome/browser/dom_ui/chrome_url_data_manager.cc b/chrome/browser/dom_ui/chrome_url_data_manager.cc new file mode 100644 index 0000000..b35e049 --- /dev/null +++ b/chrome/browser/dom_ui/chrome_url_data_manager.cc @@ -0,0 +1,336 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/ref_counted_util.h" +#include "googleurl/src/url_util.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_file_job.h" +#include "net/url_request/url_request_job.h" + +// 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"; + +// The single global instance of ChromeURLDataManager. +ChromeURLDataManager chrome_url_data_manager; + +// The ProtocolFactory for creating URLRequestChromeJobs. +static URLRequest::ProtocolFactory Factory; + +// URLRequestChromeJob is a URLRequestJob that manages running chrome-internal +// resource requests asynchronously. +// It hands off URL requests to ChromeURLDataManager, which asynchronously +// calls back once the data is available. +class URLRequestChromeJob : public URLRequestJob { + public: + explicit URLRequestChromeJob(URLRequest* request); + virtual ~URLRequestChromeJob(); + + // URLRequestJob implementation. + virtual void Start(); + virtual void Kill(); + virtual bool ReadRawData(char* buf, int buf_size, int *bytes_read); + virtual bool GetMimeType(std::string* mime_type); + + // Called by ChromeURLDataManager to notify us that the data blob is ready + // for us. + void DataAvailable(RefCountedBytes* bytes); + + private: + // Helper for Start(), to let us start asynchronously. + // (This pattern is shared by most URLRequestJob implementations.) + void StartAsync(); + + // Do the actual copy from data_ (the data we're serving) into |buf|. + // Separate from ReadRawData so we can handle async I/O. + void CompleteRead(char* buf, int buf_size, int* bytes_read); + + // The actual data we're serving. NULL until it's been fetched. + scoped_refptr<RefCountedBytes> data_; + // The current offset into the data that we're handing off to our + // callers via the Read interfaces. + int data_offset_; + + // For async reads, we keep around a pointer to the buffer that + // we're reading into. + char* pending_buf_; + int pending_buf_size_; + + DISALLOW_EVIL_CONSTRUCTORS(URLRequestChromeJob); +}; + +// URLRequestChromeFileJob is a URLRequestJob that acts like a file:// URL +class URLRequestChromeFileJob : public URLRequestFileJob { + public: + URLRequestChromeFileJob(URLRequest* request, const std::wstring& path); + virtual ~URLRequestChromeFileJob(); + + private: + DISALLOW_EVIL_CONSTRUCTORS(URLRequestChromeFileJob); +}; + +void RegisterURLRequestChromeJob() { + // Being a standard scheme allows us to resolve relative paths + url_util::AddStandardScheme(kChromeURLScheme); + + std::wstring inspector_dir; + if (PathService::Get(chrome::DIR_INSPECTOR, &inspector_dir)) + chrome_url_data_manager.AddFileSource("inspector", inspector_dir); + + URLRequest::RegisterProtocolFactory(kChromeURLScheme, + &ChromeURLDataManager::Factory); +} + +// static +void ChromeURLDataManager::URLToRequest(const GURL& url, + std::string* source_name, + std::string* path) { + DCHECK(url.SchemeIs(kChromeURLScheme)); + if (!url.is_valid()) { + NOTREACHED(); + return; + } + + // Our input looks like: chrome-resource://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()); + + const std::string& spec = url.possibly_invalid_spec(); + const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec(); + int offset = parsed.CountCharactersBefore(url_parse::Parsed::PATH, false); + ++offset; // Skip the slash at the beginning of the path. + if (offset < static_cast<int>(spec.size())) + path->assign(spec.substr(offset)); +} + +// static +bool ChromeURLDataManager::URLToFilePath(const GURL& url, + std::wstring* file_path) { + // Parse the URL into a request for a source and path. + std::string source_name; + std::string relative_path; + URLToRequest(url, &source_name, &relative_path); + + FileSourceMap::const_iterator i( + chrome_url_data_manager.file_sources_.find(source_name)); + if (i == chrome_url_data_manager.file_sources_.end()) + return false; + + file_path->assign(i->second); + file_util::AppendToPath(file_path, UTF8ToWide(relative_path)); + return true; +} + +ChromeURLDataManager::ChromeURLDataManager() : next_request_id_(0) { } + +ChromeURLDataManager::~ChromeURLDataManager() { } + +void ChromeURLDataManager::AddDataSource(scoped_refptr<DataSource> source) { + // TODO(jackson): A new data source with same name should not clobber the + // existing one. + data_sources_[source->source_name()] = source; +} + +void ChromeURLDataManager::AddFileSource(const std::string& source_name, + const std::wstring& file_path) { + DCHECK(file_sources_.count(source_name) == 0); + file_sources_[source_name] = file_path; +} + +void ChromeURLDataManager::RemoveFileSource(const std::string& source_name) { + DCHECK(file_sources_.count(source_name) == 1); + file_sources_.erase(source_name); +} + +bool ChromeURLDataManager::StartRequest(const GURL& url, + URLRequestChromeJob* job) { + // Parse the URL into a request for a source and path. + std::string source_name; + std::string path; + URLToRequest(url, &source_name, &path); + + // Look up the data source for the request. + DataSourceMap::iterator i = data_sources_.find(source_name); + if (i == data_sources_.end()) + return false; + DataSource* source = i->second; + + // Save this request so we know where to send the data. + RequestID request_id = next_request_id_++; + pending_requests_.insert(std::make_pair(request_id, job)); + + // Forward along the request to the data source. + source->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(source, &DataSource::StartDataRequest, + path, request_id)); + return true; +} + +void ChromeURLDataManager::RemoveRequest(URLRequestChromeJob* job) { + // Remove the request from our list of pending requests. + // If/when the source sends the data that was requested, the data will just + // be thrown away. + for (PendingRequestMap::iterator i = pending_requests_.begin(); + i != pending_requests_.end(); ++i) { + if (i->second == job) { + pending_requests_.erase(i); + return; + } + } +} + +void ChromeURLDataManager::DataAvailable( + RequestID request_id, + scoped_refptr<RefCountedBytes> bytes) { + // Forward this data on to the pending URLRequest, if it exists. + PendingRequestMap::iterator i = pending_requests_.find(request_id); + if (i != pending_requests_.end()) { + URLRequestChromeJob* job = i->second; + pending_requests_.erase(i); + job->DataAvailable(bytes); + } +} + +void ChromeURLDataManager::DataSource::SendResponse( + RequestID request_id, + RefCountedBytes* bytes) { + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::DataAvailable, + request_id, scoped_refptr<RefCountedBytes>(bytes))); +} + +// static +URLRequestJob* ChromeURLDataManager::Factory(URLRequest* request, + const std::string& scheme) { + // Try first with a file handler + std::wstring path; + if (ChromeURLDataManager::URLToFilePath(request->url(), &path)) + return new URLRequestChromeFileJob(request, path); + + // Fall back to using a custom handler + return new URLRequestChromeJob(request); +} + +URLRequestChromeJob::URLRequestChromeJob(URLRequest* request) + : URLRequestJob(request), data_offset_(0), pending_buf_(NULL) {} + +URLRequestChromeJob::~URLRequestChromeJob() { +} + +void URLRequestChromeJob::Start() { + // Start reading asynchronously so that all error reporting and data + // callbacks happen as they would for network requests. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestChromeJob::StartAsync)); +} + +void URLRequestChromeJob::Kill() { + chrome_url_data_manager.RemoveRequest(this); +} + +bool URLRequestChromeJob::GetMimeType(std::string* mime_type) { + // Rely on MIME sniffing to simplify the logic here. + *mime_type = "text/html"; + return true; +} + +void URLRequestChromeJob::DataAvailable(RefCountedBytes* bytes) { + if (bytes) { + // The request completed, and we have all the data. + // Clear any IO pending status. + SetStatus(URLRequestStatus()); + + data_ = bytes; + int bytes_read; + if (pending_buf_) { + CompleteRead(pending_buf_, pending_buf_size_, &bytes_read); + NotifyReadComplete(bytes_read); + } + } else { + // The request failed. + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, 0)); + } +} + +bool URLRequestChromeJob::ReadRawData(char* buf, int buf_size, + int* bytes_read) { + if (!data_.get()) { + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + pending_buf_ = buf; + pending_buf_size_ = buf_size; + return false; // Tell the caller we're still waiting for data. + } + + // Otherwise, the data is available. + CompleteRead(buf, buf_size, bytes_read); + return true; +} + +void URLRequestChromeJob::CompleteRead(char* buf, int buf_size, + int* bytes_read) { + int remaining = static_cast<int>(data_->data.size()) - data_offset_; + if (buf_size > remaining) + buf_size = remaining; + if (buf_size > 0) { + memcpy(buf, &data_->data[0] + data_offset_, buf_size); + data_offset_ += buf_size; + } + *bytes_read = buf_size; +} + +void URLRequestChromeJob::StartAsync() { + if (!request_) + return; + + if (chrome_url_data_manager.StartRequest(request_->url(), this)) { + NotifyHeadersComplete(); + } else { + NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, + net::ERR_INVALID_URL)); + } +} + +URLRequestChromeFileJob::URLRequestChromeFileJob(URLRequest* request, + const std::wstring& path) + : URLRequestFileJob(request) { + this->file_path_ = path; // set URLRequestFileJob::file_path_ +} + +URLRequestChromeFileJob::~URLRequestChromeFileJob() { } diff --git a/chrome/browser/dom_ui/chrome_url_data_manager.h b/chrome/browser/dom_ui/chrome_url_data_manager.h new file mode 100644 index 0000000..5edb126 --- /dev/null +++ b/chrome/browser/dom_ui/chrome_url_data_manager.h @@ -0,0 +1,168 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BROWSER_DOM_UI_CHROME_URL_DATA_MANAGER_H__ +#define BROWSER_DOM_UI_CHROME_URL_DATA_MANAGER_H__ + +#include <map> +#include <string> + +#include "base/task.h" +#include "chrome/common/ref_counted_util.h" + +class GURL; +class MessageLoop; +class URLRequest; +class URLRequestJob; + +// To serve dynamic data off of chrome-resource: URLs, implement the +// ChromeURLDataManager::DataSource interface and register your handler +// with AddDataSource. + +// ChromeURLDataManager lives on the IO thread, so any interfacing with +// it from the UI thread needs to go through an InvokeLater. +class ChromeURLDataManager { + public: + ChromeURLDataManager(); + ~ChromeURLDataManager(); + + typedef int RequestID; + + // A DataSource is an object that can answer requests for data + // asynchronously. It should live on a thread that outlives the IO thread + // (in particular, the UI thread). + // An implementation of DataSource should handle calls to StartDataRequest() + // by starting its (implementation-specific) asynchronous request for + // the data, then call SendResponse() to notify + class DataSource : public base::RefCountedThreadSafe<DataSource> { + public: + // See source_name_ and message_loop_ below for docs on these parameters. + DataSource(const std::string& source_name, + MessageLoop* message_loop) + : source_name_(source_name), message_loop_(message_loop) {} + virtual ~DataSource() {} + + // Sent by the DataManager to request data at |path|. The source should + // call SendResponse() when the data is available or if the request could + // not be satisfied. + virtual void StartDataRequest(const std::string& path, int request_id) = 0; + + // Report that a request has resulted in the data |bytes|. + // If the request can't be satisfied, pass NULL for |bytes| to indicate + // the request is over. + void SendResponse(int request_id, RefCountedBytes* bytes); + + MessageLoop* message_loop() const { return message_loop_; } + const std::string& source_name() const { return source_name_; } + + private: + // The name of this source. + // E.g., for favicons, this could be "favicon", which results in paths for + // specific resources like "favicon/34" getting sent to this source. + const std::string source_name_; + + // The MessageLoop for the thread where this DataSource lives. + // Used to send messages to the DataSource. + MessageLoop* message_loop_; + }; + + // Add a DataSource to the collection of data sources. + // Because we don't track users of a given path, we can't know when it's + // safe to remove them, so the added source effectively leaks. + // This could be improved in the future but currently the users of this + // interface are conceptually permanent registration anyway. + // Adding a second DataSource with the same name clobbers the first. + // NOTE: Calling this from threads other the IO thread must be done via + // InvokeLater. + void AddDataSource(scoped_refptr<DataSource> source); + + // Add/remove a path from the collection of file sources. + // A file source acts like a file:// URL to the specified path. + // Calling this from threads other the IO thread must be done via + // InvokeLater. + void AddFileSource(const std::string& source_name, const std::wstring& path); + void RemoveFileSource(const std::string& source_name); + + static URLRequestJob* Factory(URLRequest* request, const std::string& scheme); + +private: + friend class URLRequestChromeJob; + + // Parse a URL into the components used to resolve its request. + static void URLToRequest(const GURL& url, + std::string* source, + std::string* path); + + // Translate a chrome resource URL into a local file path if there is one. + // Returns false if there is no file handler for this URL + static bool URLToFilePath(const GURL& url, std::wstring* file_path); + + // Called by the job when it's starting up. + // Returns false if |url| is not a URL managed by this object. + bool StartRequest(const GURL& url, URLRequestChromeJob* job); + // Remove a request from the list of pending requests. + void RemoveRequest(URLRequestChromeJob* job); + + // Sent by Request::SendResponse. + void DataAvailable(RequestID request_id, + scoped_refptr<RefCountedBytes> bytes); + + // File sources of data, keyed by source name (e.g. "inspector"). + typedef std::map<std::string, std::wstring> FileSourceMap; + FileSourceMap file_sources_; + + // Custom sources of data, keyed by source path (e.g. "favicon"). + typedef std::map<std::string, scoped_refptr<DataSource> > DataSourceMap; + DataSourceMap data_sources_; + + // All pending URLRequestChromeJobs, keyed by ID of the request. + // URLRequestChromeJob calls into this object when it's constructed and + // destructed to ensure that the pointers in this map remain valid. + typedef std::map<RequestID, URLRequestChromeJob*> PendingRequestMap; + PendingRequestMap pending_requests_; + + // The ID we'll use for the next request we receive. + RequestID next_request_id_; +}; + +// Since we have a single global ChromeURLDataManager, we don't need to +// grab a reference to it when creating Tasks involving it. +template <> struct RunnableMethodTraits<ChromeURLDataManager> { + static void RetainCallee(ChromeURLDataManager*) {} + static void ReleaseCallee(ChromeURLDataManager*) {} +}; + +// The single global instance of ChromeURLDataManager. +extern ChromeURLDataManager chrome_url_data_manager; + +// Register our special URL handler under our special URL scheme. +// Must be done once at startup. +void RegisterURLRequestChromeJob(); + +#endif // BROWSER_DOM_UI_CHROME_URL_DATA_MANAGER_H__ diff --git a/chrome/browser/dom_ui/dom_ui_host.cc b/chrome/browser/dom_ui/dom_ui_host.cc new file mode 100644 index 0000000..8bb7cad --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui_host.cc @@ -0,0 +1,139 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/dom_ui/dom_ui_host.h" + +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/tab_contents_type.h" +#include "chrome/browser/render_view_host.h" + +DOMUIHost::DOMUIHost(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory) + : WebContents(profile, + instance, + render_view_factory, + MSG_ROUTING_NONE, + NULL) { + // Implementors of this class will have a specific tab contents type. + type_ = TAB_CONTENTS_UNKNOWN_TYPE; +} + +DOMUIHost::~DOMUIHost() { + STLDeleteContainerPairSecondPointers(message_callbacks_.begin(), + message_callbacks_.end()); + STLDeleteContainerPointers(handlers_.begin(), handlers_.end()); +} + +bool DOMUIHost::CreateRenderView(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::CreateRenderView(render_view_host); +} + +void DOMUIHost::AddMessageHandler(DOMMessageHandler* handler) { + handlers_.push_back(handler); +} + +void DOMUIHost::RegisterMessageCallback(const std::string& name, + MessageCallback* callback) { + message_callbacks_.insert(std::make_pair(name, callback)); +} + + +void DOMUIHost::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 DOMUIHost::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 DOMUIHost::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)) { + // 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; +} + +WebPreferences DOMUIHost::GetWebkitPrefs() { + // Get the users preferences then force image loading to always be on. + WebPreferences web_prefs = WebContents::GetWebkitPrefs(); + web_prefs.loads_images_automatically = true; + + return web_prefs; +} + +void DOMUIHost::ExecuteJavascript(const std::wstring& javascript) { + // We're taking a string and making a javascript URL out of it. This means + // that escaping will follow the rules of a URL. Yet, the JSON text may have + // stuff in it that would be interpreted as escaped characters in a URL, but + // we want to preserve them literally. + // + // We just escape all the percents to avoid this, since when this javascript + // URL is interpreted, it will be unescaped. + std::wstring escaped_js(javascript); + ReplaceSubstringsAfterOffset(&escaped_js, 0, L"%", L"%25"); + ExecuteJavascriptInWebFrame(L"", L"javascript:" + escaped_js); +} diff --git a/chrome/browser/dom_ui/dom_ui_host.h b/chrome/browser/dom_ui/dom_ui_host.h new file mode 100644 index 0000000..09d624f --- /dev/null +++ b/chrome/browser/dom_ui/dom_ui_host.h @@ -0,0 +1,127 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// DOMUIHost is a special type of TabContents that can display Chrome-specific +// content using HTML. +// It provides methods to send data to and from the page Javascript. + +// TODO(evanm): Add more limitations on what the contents of this view +// can do (especially regarding network requests). + +#ifndef CHROME_BROWSER_DOM_UI_DOM_UI_HOST_H__ +#define CHROME_BROWSER_DOM_UI_DOM_UI_HOST_H__ + +#include "chrome/browser/web_contents.h" +#include "webkit/glue/webpreferences.h" + +class DOMMessageDispatcher; +class RenderProcessHost; +class RenderViewHost; + +// Messages sent from the DOM are forwarded via the DOMUIHost to handler +// classes. These objects are owned by DOMUIHost and destroyed when the +// host is destroyed. +class DOMMessageHandler { + public: + virtual ~DOMMessageHandler() {} +}; + +// See the comments at the top of this file. +class DOMUIHost : public WebContents { + public: + DOMUIHost(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory); + + // Initializes the given renderer, after enabling DOM UI bindings on it. + virtual bool CreateRenderView(RenderViewHost* render_view_host); + + // Add type-specific javascript message handlers. + // TODO(timsteele): Any implementation of this method should really be done + // upon construction, but that won't work until the TabContents::controller() + // API is fixed to never return NULL, and likewise for TabContents::profile(). + // Only then could any Handlers we attach here access the profile upon + // construction, which is the most common case; currently they'll blow up. + virtual void AttachMessageHandlers() = 0; + + // Add |handler| to the list of handlers owned by this object. + // They will be destroyed when this page is hidden. + void AddMessageHandler(DOMMessageHandler* handler); + + // Register a callback for a specific message. + 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); + + + // Overrides from WebContents. + virtual void ProcessDOMUIMessage(const std::string& message, + const std::string& content); + virtual DOMUIHost* AsDOMUIHost() { return this; } + + // Override this method so we can ensure that javascript and image loading + // are always on even for DOMUIHost tabs. + virtual WebPreferences GetWebkitPrefs(); + + // We override updating history with a no-op so these pages + // are not saved to history. + virtual void UpdateHistoryForNavigation(const GURL& url, + const ViewHostMsg_FrameNavigate_Params& params) { } + + protected: + // Should delete via CloseContents. + virtual ~DOMUIHost(); + + 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_EVIL_CONSTRUCTORS(DOMUIHost); +}; + +#endif // CHROME_BROWSER_DOM_UI_DOM_UI_HOST_H__ diff --git a/chrome/browser/dom_ui/html_dialog_contents.cc b/chrome/browser/dom_ui/html_dialog_contents.cc new file mode 100644 index 0000000..3618509 --- /dev/null +++ b/chrome/browser/dom_ui/html_dialog_contents.cc @@ -0,0 +1,101 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/dom_ui/html_dialog_contents.h" + +#include "chrome/browser/render_view_host.h" + +const char kGearsScheme[] = "gears"; + +HtmlDialogContents::HtmlDialogContents(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* rvf) + : DOMUIHost(profile, instance, rvf), + delegate_(NULL) { + type_ = TAB_CONTENTS_HTML_DIALOG; +} + +HtmlDialogContents::~HtmlDialogContents() { +} + +void HtmlDialogContents::Init(HtmlDialogContentsDelegate* delegate) { + delegate_ = delegate; + + std::string dialog_args; + if (delegate_) + dialog_args = delegate_->GetDialogArgs(); + + DCHECK(render_view_host()); + render_view_host()->SetDOMUIProperty("dialogArguments", dialog_args); +} + +//////////////////////////////////////////////////////////////////////////////// +// DOMUIHost implementation: + +void HtmlDialogContents::AttachMessageHandlers() { + // Hook up the javascript function calls, also known as chrome.send("foo") + // calls in the HTML, to the actual C++ functions. + RegisterMessageCallback("DialogClose", + NewCallback(this, &HtmlDialogContents::OnDialogClosed)); +} + +// static +bool HtmlDialogContents::IsHtmlDialogUrl(const GURL& url) { + return url.SchemeIs(kGearsScheme); +} + +//////////////////////////////////////////////////////////////////////////////// +// Private: + +// Helper function to read the JSON string from the Value parameter. +std::string GetJsonResponse(const Value* content) { + if (!content || !content->IsType(Value::TYPE_LIST)) { + NOTREACHED(); + return ""; + } + const ListValue* args = static_cast<const ListValue*>(content); + if (args->GetSize() != 1) { + NOTREACHED(); + return ""; + } + + std::wstring result; + Value* value = NULL; + if (!args->Get(0, &value) || !value->GetAsString(&result)) { + NOTREACHED(); + return ""; + } + + return WideToASCII(result); +} + +void HtmlDialogContents::OnDialogClosed(const Value* content) { + if (delegate_) + delegate_->OnDialogClosed(GetJsonResponse(content)); +} diff --git a/chrome/browser/dom_ui/html_dialog_contents.h b/chrome/browser/dom_ui/html_dialog_contents.h new file mode 100644 index 0000000..2441ae0 --- /dev/null +++ b/chrome/browser/dom_ui/html_dialog_contents.h @@ -0,0 +1,97 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_BROWSER_DOM_UI_HTML_DIALOG_CONTENTS_H__ +#define CHROME_BROWSER_DOM_UI_HTML_DIALOG_CONTENTS_H__ + +#include "chrome/browser/dom_ui/dom_ui_host.h" +#include "chrome/views/window_delegate.h" + +// Implement this class to receive notifications. +class HtmlDialogContentsDelegate : public ChromeViews::WindowDelegate { + public: + // Get the HTML file path for the content to load in the dialog. + virtual GURL GetDialogContentURL() const = 0; + // Get the size of the dialog. + virtual void GetDialogSize(CSize* size) const = 0; + // Gets the JSON string input to use when showing the dialog. + virtual std::string GetDialogArgs() const = 0; + // A callback to notify the delegate that the dialog closed. + virtual void OnDialogClosed(const std::string& json_retval) = 0; + + protected: + ~HtmlDialogContentsDelegate() {} +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// HtmlDialogContents is a simple wrapper around DOMUIHost and is used to +// display file URL contents inside a modal HTML dialog. +// +//////////////////////////////////////////////////////////////////////////////// +class HtmlDialogContents : public DOMUIHost { + public: + struct HtmlDialogParams { + // The URL for the content that will be loaded in the dialog. + GURL url; + // Width of the dialog. + int width; + // Height of the dialog. + int height; + // The JSON input to pass to the dialog when showing it. + std::string json_input; + }; + + HtmlDialogContents(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* rvf); + virtual ~HtmlDialogContents(); + + // Initialize the HtmlDialogContents with the given delegate. Must be called + // after the RenderViewHost is created. + void Init(HtmlDialogContentsDelegate* d); + + // Overridden from DOMUIHost: + virtual void AttachMessageHandlers(); + + // Returns true of this URL should be handled by the HtmlDialogContents. + static bool IsHtmlDialogUrl(const GURL& url); + + private: + // JS message handlers: + void OnDialogClosed(const Value* content); + + // The delegate that knows how to display the dialog and receives the response + // back from the dialog. + HtmlDialogContentsDelegate* delegate_; + + DISALLOW_EVIL_CONSTRUCTORS(HtmlDialogContents); +}; + +#endif // CHROME_BROWSER_DOM_UI_HTML_DIALOG_CONTENTS_H__ diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc new file mode 100644 index 0000000..894414c --- /dev/null +++ b/chrome/browser/dom_ui/new_tab_ui.cc @@ -0,0 +1,852 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/dom_ui/new_tab_ui.h" + +#include "base/histogram.h" +#include "base/string_piece.h" +#include "chrome/app/locales/locale_settings.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_resources.h" +#include "chrome/browser/history_tab_ui.h" +#include "chrome/browser/history/page_usage_data.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/template_url.h" +#include "chrome/browser/user_metrics.h" +#include "chrome/browser/views/keyword_editor_view.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/resource_bundle.h" + +#include "generated_resources.h" + +// 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; + +// The number of days of history we consider for most visited entries. +const int kMostVisitedScope = 90; + +// The number of recent bookmarks we show. +static const int kRecentBookmarks = 9; + +// Strings sent to the page via jstemplates used to set the direction of the +// HTML document based on locale. +static const wchar_t kRTLHtmlTextDirection[] = L"rtl"; +static const wchar_t kDefaultHtmlTextDirection[] = L"ltr"; + +namespace { + +// To measure end-to-end performance of the new tab page, we observe paint +// messages and wait for the page to stop repainting. +class PaintTimer : public RenderWidgetHost::PaintObserver { + public: + PaintTimer() : method_factory_(this) { + Start(); + } + + // Start the benchmarking and the timer. + void Start() { + start_ = TimeTicks::Now(); + last_paint_ = start_; + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod(&PaintTimer::Timeout), + static_cast<int>(kTimeout.InMilliseconds())); + } + + // A callback that is invoked whenever our RenderWidgetHost paints. + virtual void RenderWidgetHostDidPaint(RenderWidgetHost* rwh) { + last_paint_ = TimeTicks::Now(); + } + + // The timer callback. If enough time has elapsed since the last paint + // message, we say we're done painting; otherwise, we keep waiting. + void Timeout() { + TimeTicks now = TimeTicks::Now(); + if ((now - last_paint_) >= kTimeout) { + // Painting has quieted down. Log this as the full time to run. + TimeDelta load_time = last_paint_ - start_; + int load_time_ms = static_cast<int>(load_time.InMilliseconds()); + NotificationService::current()->Notify( + NOTIFY_INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources(), + Details<int>(&load_time_ms)); + UMA_HISTOGRAM_TIMES(L"NewTabUI load", load_time); + } else { + // Not enough quiet time has elapsed. + // Some more paints must've occurred since we set the timeout. + // Wait some more. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod(&PaintTimer::Timeout), + static_cast<int>(kTimeout.InMilliseconds())); + } + } + + private: + // The amount of time there must be no painting for us to consider painting + // finished. Observed times are in the ~1200ms range. + static const TimeDelta kTimeout; + // The time when we started benchmarking. + TimeTicks start_; + // The last time we got a paint notification. + TimeTicks last_paint_; + // Scoping so we can be sure our timeouts don't outlive us. + ScopedRunnableMethodFactory<PaintTimer> method_factory_; + + DISALLOW_EVIL_CONSTRUCTORS(PaintTimer); +}; +const TimeDelta PaintTimer::kTimeout(TimeDelta::FromMilliseconds(2000)); + +// Adds "url" and "title" keys on incoming dictionary, setting title +// as the url as a fallback on empty title. +void 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); +} + +} // end anonymous namespace + +NewTabHTMLSource::NewTabHTMLSource(int message_id) + : DataSource("new-tab", MessageLoop::current()), + message_id_(message_id) { +} + +void NewTabHTMLSource::StartDataRequest(const std::string& path, + int request_id) { + if (!path.empty()) { + // A path under new-tab was requested; it's likely a bad relative + // URL from the new tab page, but in any case it's an error. + NOTREACHED(); + return; + } + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_NEW_TAB_TITLE)); + localized_strings.SetString(L"mostvisited", + l10n_util::GetString(IDS_NEW_TAB_MOST_VISITED)); + localized_strings.SetString(L"searches", + l10n_util::GetString(IDS_NEW_TAB_SEARCHES)); + localized_strings.SetString(L"bookmarks", + l10n_util::GetString(IDS_NEW_TAB_BOOKMARKS)); + localized_strings.SetString(L"showhistory", + l10n_util::GetString(IDS_NEW_TAB_HISTORY_SHOW)); + localized_strings.SetString(L"searchhistory", + l10n_util::GetString(IDS_NEW_TAB_HISTORY_SEARCH)); + localized_strings.SetString(L"closedtabs", + l10n_util::GetString(IDS_NEW_TAB_CLOSED_TABS)); + localized_strings.SetString(L"mostvisitedintro", + l10n_util::GetStringF(IDS_NEW_TAB_MOST_VISITED_INTRO, + l10n_util::GetString(IDS_WELCOME_PAGE_URL))); + + localized_strings.SetString(L"textdirection", + (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? + kRTLHtmlTextDirection : kDefaultHtmlTextDirection); + + // Let the page know whether this is the first New Tab view for + // this session (and let it trigger all manner of fancy). + static bool new_session = true; + localized_strings.SetString(L"newsession", + new_session ? L"true" : std::wstring()); + new_session = false; + + if (message_id_) + localized_strings.SetString(L"motd", l10n_util::GetString(message_id_)); + else + localized_strings.SetString(L"motd", std::wstring()); + + static const StringPiece new_tab_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_NEW_TAB_HTML)); + + const std::string full_html = jstemplate_builder::GetTemplateHtml( + new_tab_html, &localized_strings, "t" /* template root node id */); + + 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); +} + +IncognitoTabHTMLSource::IncognitoTabHTMLSource() + : DataSource("new-tab", MessageLoop::current()) { +} + +void IncognitoTabHTMLSource::StartDataRequest(const std::string& path, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString(L"title", + l10n_util::GetString(IDS_NEW_TAB_TITLE)); + localized_strings.SetString(L"content", + l10n_util::GetStringF(IDS_NEW_TAB_OTR_MESSAGE, + l10n_util::GetString(IDS_LEARN_MORE_INCOGNITO_URL))); + + localized_strings.SetString(L"textdirection", + (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? + kRTLHtmlTextDirection : kDefaultHtmlTextDirection); + + static const StringPiece incognito_tab_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_INCOGNITO_TAB_HTML)); + + const std::string full_html = jstemplate_builder::GetTemplateHtml( + incognito_tab_html, &localized_strings, "t" /* template root node id */); + + 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); +} + +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(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(DOMUIHost* dom_ui_host) + : dom_ui_host_(dom_ui_host) { + // Register ourselves as the handler for the "mostvisited" message from + // Javascript. + dom_ui_host_->RegisterMessageCallback("getMostVisited", + NewCallback(this, &MostVisitedHandler::HandleGetMostVisited)); + + // Set up our sources for thumbnail and favicon data. + // Ownership is passed to the ChromeURLDataManager. + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + new ThumbnailSource(dom_ui_host->profile()))); + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + new FavIconSource(dom_ui_host->profile()))); + + // Get notifications when history is cleared. + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_HISTORY_URLS_DELETED, + Source<Profile>(dom_ui_host_->profile())); +} + +MostVisitedHandler::~MostVisitedHandler() { + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_HISTORY_URLS_DELETED, + Source<Profile>(dom_ui_host_->profile())); +} + +void MostVisitedHandler::HandleGetMostVisited(const Value* value) { + HistoryService* hs = + dom_ui_host_->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QuerySegmentUsageSince( + &cancelable_consumer_, + Time::Now() - TimeDelta::FromDays(kMostVisitedScope), + NewCallback(this, &MostVisitedHandler::OnSegmentUsageAvailable)); +} + +void MostVisitedHandler::OnSegmentUsageAvailable( + CancelableRequestProvider::Handle handle, + std::vector<PageUsageData*>* data) { + most_visited_urls_.clear(); + + ListValue pages_value; + const size_t count = std::min<size_t>(kMostVisitedPages, data->size()); + for (size_t i = 0; i < count; ++i) { + const PageUsageData& page = *(*data)[i]; + DictionaryValue* page_value = new DictionaryValue; + SetURLAndTitle(page_value, page.GetTitle(), page.GetURL()); + pages_value.Append(page_value); + most_visited_urls_.push_back(page.GetURL()); + } + dom_ui_host_->CallJavascriptFunction(L"mostVisitedPages", pages_value); +} + +void MostVisitedHandler::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. + HandleGetMostVisited(NULL); +} + + +TemplateURLHandler::TemplateURLHandler(DOMUIHost* dom_ui_host) + : dom_ui_host_(dom_ui_host), template_url_model_(NULL) { + dom_ui_host->RegisterMessageCallback("getMostSearched", + NewCallback(this, &TemplateURLHandler::HandleGetMostSearched)); + dom_ui_host->RegisterMessageCallback("doSearch", + NewCallback(this, &TemplateURLHandler::HandleDoSearch)); +} + +TemplateURLHandler::~TemplateURLHandler() { + if (template_url_model_) + template_url_model_->RemoveObserver(this); +} + +void TemplateURLHandler::HandleGetMostSearched(const Value* content) { + // The page Javascript has requested the list of keyword searches. + // Start loading them from the template URL backend. + if (!template_url_model_) { + template_url_model_ = dom_ui_host_->profile()->GetTemplateURLModel(); + template_url_model_->AddObserver(this); + } + if (template_url_model_->loaded()) { + OnTemplateURLModelChanged(); + } else { + template_url_model_->Load(); + } +} + +void TemplateURLHandler::HandleDoSearch(const Value* content) { + // Extract the parameters out of the input list. + if (!content || !content->IsType(Value::TYPE_LIST)) { + NOTREACHED(); + return; + } + const ListValue* args = static_cast<const ListValue*>(content); + if (args->GetSize() != 2) { + NOTREACHED(); + return; + } + std::wstring keyword, search; + Value* value = NULL; + if (!args->Get(0, &value) || !value->GetAsString(&keyword)) { + NOTREACHED(); + return; + } + if (!args->Get(1, &value) || !value->GetAsString(&search)) { + NOTREACHED(); + return; + } + + // Combine the keyword and search into a URL. + const TemplateURL* template_url = + template_url_model_->GetTemplateURLForKeyword(keyword); + if (!template_url) { + // The keyword seems to have changed out from under us. + // Not an error, but nothing we can do... + return; + } + const TemplateURLRef* url_ref = template_url->url(); + if (!url_ref || !url_ref->SupportsReplacement()) { + NOTREACHED(); + return; + } + std::wstring url = url_ref->ReplaceSearchTerms(*template_url, search, + TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()); + + // Load the URL. + if (!url.empty()) { + dom_ui_host_->OpenURL(GURL(WideToUTF8(url)), CURRENT_TAB, + PageTransition::LINK); + } +} + +// A helper function for sorting TemplateURLs where the most used ones show up +// first. +static bool TemplateURLSortByUsage(const TemplateURL* a, + const TemplateURL* b) { + return a->usage_count() > b->usage_count(); +} + +void TemplateURLHandler::OnTemplateURLModelChanged() { + // We've loaded some template URLs. Send them to the page. + const int kMaxURLs = 3; // The maximum number of URLs we're willing to send. + std::vector<const TemplateURL*> urls = template_url_model_->GetTemplateURLs(); + sort(urls.begin(), urls.end(), TemplateURLSortByUsage); + ListValue urls_value; + for (size_t i = 0; i < std::min<size_t>(urls.size(), kMaxURLs); ++i) { + if (urls[i]->usage_count() == 0) + break; // urls is sorted by usage count; the remainder would be no good. + + const TemplateURLRef* urlref = urls[i]->url(); + if (!urlref) + continue; + DictionaryValue* entry_value = new DictionaryValue; + entry_value->SetString(L"short_name", urls[i]->short_name()); + entry_value->SetString(L"keyword", urls[i]->keyword()); + + const GURL& url = urls[i]->GetFavIconURL(); + if (url.is_valid()) + entry_value->SetString(L"favIconURL", UTF8ToWide(url.spec())); + + urls_value.Append(entry_value); + } + dom_ui_host_->CallJavascriptFunction(L"searchURLs", urls_value); +} + +RecentlyBookmarkedHandler::RecentlyBookmarkedHandler(DOMUIHost* dom_ui_host) + : dom_ui_host_(dom_ui_host) { + dom_ui_host->RegisterMessageCallback("getRecentlyBookmarked", + NewCallback(this, + &RecentlyBookmarkedHandler::HandleGetRecentlyBookmarked)); +} + +void RecentlyBookmarkedHandler::HandleGetRecentlyBookmarked(const Value*) { + HistoryService* hs = + dom_ui_host_->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + if (hs) { + HistoryService::Handle handle = hs->GetMostRecentStarredEntries( + kRecentBookmarks, + &cancelable_consumer_, + NewCallback(this, + &RecentlyBookmarkedHandler::OnMostRecentStarredEntries)); + } +} + +void RecentlyBookmarkedHandler::OnMostRecentStarredEntries( + HistoryService::Handle request_handle, + std::vector<history::StarredEntry>* entries) { + ListValue list_value; + for (size_t i = 0; i < entries->size(); ++i) { + const history::StarredEntry& entry = (*entries)[i]; + DictionaryValue* entry_value = new DictionaryValue; + SetURLAndTitle(entry_value, entry.title, entry.url); + list_value.Append(entry_value); + } + dom_ui_host_->CallJavascriptFunction(L"recentlyBookmarked", list_value); +} + +RecentlyClosedTabsHandler::RecentlyClosedTabsHandler(DOMUIHost* dom_ui_host) + : dom_ui_host_(dom_ui_host), + tab_restore_service_(NULL), + handle_recently_closed_tab_factory_(this) { + dom_ui_host->RegisterMessageCallback("getRecentlyClosedTabs", + NewCallback(this, + &RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs)); + dom_ui_host->RegisterMessageCallback("reopenTab", + NewCallback(this, &RecentlyClosedTabsHandler::HandleReopenTab)); +} + +RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() { + if (tab_restore_service_) + tab_restore_service_->RemoveObserver(this); +} + +void RecentlyClosedTabsHandler::HandleReopenTab(const Value* content) { + UserMetrics::RecordAction(L"NewTabPage_ReopenTab", + dom_ui_host_->profile()); + + NavigationController* controller = dom_ui_host_->controller(); + Browser* browser = Browser::GetBrowserForController( + controller, NULL); + if (!browser) + return; + + // Extract the integer value of the tab session to restore from the + // incoming string array. This will be greatly simplified when + // DOMUIBindings::send() is generalized to all data types instead of + // silently failing when passed anything other then an array of + // strings. + if (content->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(content); + Value* list_member; + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + if (string_value->GetAsString(&wstring_value)) { + int session_to_restore = _wtoi(wstring_value.c_str()); + + const TabRestoreService::Tabs& tabs = tab_restore_service_->tabs(); + for (TabRestoreService::Tabs::const_iterator it = tabs.begin(); + it != tabs.end(); ++it) { + if (it->id == session_to_restore) { + browser->ReplaceRestoredTab( + it->navigations, it->current_navigation_index); + // The current tab has been nuked at this point; + // don't touch any member variables. + break; + } + } + } + } + } +} + +void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs( + const Value* content) { + if (!tab_restore_service_) { + tab_restore_service_ = dom_ui_host_->profile()->GetTabRestoreService(); + + // GetTabRestoreService() can return NULL (i.e., when in Off the + // Record mode) + if (tab_restore_service_) + tab_restore_service_->AddObserver(this); + } + + if (tab_restore_service_) + TabRestoreServiceChanged(tab_restore_service_); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceChanged( + TabRestoreService* service) { + handle_recently_closed_tab_factory_.RevokeAll(); + + const TabRestoreService::Tabs& tabs = service->tabs(); + ListValue list_value; + int added_count = 0; + + Time now = Time::Now(); + TimeDelta expire_delta = TimeDelta::FromMinutes(5); + Time five_minutes_ago = now - expire_delta; + Time oldest_item = now; + + // We filter the list of recently closed to only show 'interesting' + // tabs, where an interesting tab has navigations, was closed within + // the last five minutes and is not the new tab ui. + for (TabRestoreService::Tabs::const_iterator it = tabs.begin(); + it != tabs.end() && added_count < 3; ++it) { + if (it->navigations.empty()) + continue; + + if (five_minutes_ago > it->close_time) + continue; + + if (it->close_time < oldest_item) + oldest_item = it->close_time; + + const TabNavigation& navigator = + it->navigations.at(it->current_navigation_index); + if (navigator.url == NewTabUIURL()) + continue; + + DictionaryValue* dictionary = new DictionaryValue; + SetURLAndTitle(dictionary, navigator.title, navigator.url); + dictionary->SetInteger(L"sessionId", it->id); + + list_value.Append(dictionary); + added_count++; + } + + // If we displayed anything, we must schedule a redisplay when the + // oldest item expires. + if (added_count) { + TimeDelta next_run = (oldest_item + expire_delta) - now; + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + handle_recently_closed_tab_factory_.NewRunnableMethod( + &RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs, + reinterpret_cast<const Value*>(NULL)), + static_cast<int>(next_run.InMilliseconds())); + } + + dom_ui_host_->CallJavascriptFunction(L"recentlyClosedTabs", list_value); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed( + TabRestoreService* service) { + tab_restore_service_ = NULL; +} + +HistoryHandler::HistoryHandler(DOMUIHost* dom_ui_host) + : dom_ui_host_(dom_ui_host) { + dom_ui_host->RegisterMessageCallback("showHistoryPage", + NewCallback(this, &HistoryHandler::HandleShowHistoryPage)); + dom_ui_host->RegisterMessageCallback("searchHistoryPage", + NewCallback(this, &HistoryHandler::HandleSearchHistoryPage)); +} + +void HistoryHandler::HandleShowHistoryPage(const Value*) { + NavigationController* controller = dom_ui_host_->controller(); + if (controller) + controller->LoadURL(HistoryTabUI::GetURL(), PageTransition::LINK); +} + +void HistoryHandler::HandleSearchHistoryPage(const Value* content) { + if (content && content->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(content); + Value* list_member; + if (list_value->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + std::wstring wstring_value; + if (string_value->GetAsString(&wstring_value)) { + NavigationController* controller = dom_ui_host_->controller(); + controller->LoadURL( + HistoryTabUI::GetHistoryURLWithSearchText(wstring_value), + PageTransition::LINK); + } + } + } +} + + +// This is the top-level URL handler for chrome-internal: URLs, and exposed in +// our header file. +bool NewTabUIHandleURL(GURL* url, + TabContentsType* result_type) { + if (!url->SchemeIs(kNewTabUIScheme)) + return false; + + *result_type = TAB_CONTENTS_NEW_TAB_UI; + *url = GURL("chrome-resource://new-tab/"); + + return true; +} + +GURL NewTabUIURL() { + std::string url(kNewTabUIScheme); + url += ":"; + return GURL(url); +} + +NewTabUIContents::NewTabUIContents(Profile* profile, + SiteInstance* instance, RenderViewHostFactory* render_view_factory) : + DOMUIHost(profile, instance, render_view_factory), + motd_message_id_(0), + incognito_(false), + most_visited_handler_(NULL) { + type_ = TAB_CONTENTS_NEW_TAB_UI; + set_forced_title(l10n_util::GetString(IDS_NEW_TAB_TITLE)); + + if (profile->IsOffTheRecord()) + incognito_ = true; + + render_view_host()->SetPaintObserver(new PaintTimer); +} + +void NewTabUIContents::AttachMessageHandlers() { + // Regretfully, DataSources are global, instead of + // per-TabContents. Because of the motd_message_id_ member, each + // NewTabUIContents instance could theoretically have a different + // message. Moving this from the constructor to here means that we + // reconnect this source each time we reload so we should no longer + // have the bug where we open a normal new tab page (no motd), open + // another OTR new tab page (blurb motd describing what 'incognito' + // means), refresh the normal new page (which now displays the + // incognito blurb because that was the last NewTabHTMLSource hooked + // up). + // + // This is a workaround until http://b/issue?id=1230312 is fixed. + + if (incognito_) { + IncognitoTabHTMLSource* html_source = new IncognitoTabHTMLSource(); + + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + html_source)); + } else { + AddMessageHandler(new TemplateURLHandler(this)); + most_visited_handler_ = new MostVisitedHandler(this); + AddMessageHandler(most_visited_handler_); // Takes ownership. + AddMessageHandler(new RecentlyBookmarkedHandler(this)); + AddMessageHandler(new RecentlyClosedTabsHandler(this)); + AddMessageHandler(new HistoryHandler(this)); + + NewTabHTMLSource* html_source = new NewTabHTMLSource(0); + + g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE, + NewRunnableMethod(&chrome_url_data_manager, + &ChromeURLDataManager::AddDataSource, + html_source)); + } +} + +bool NewTabUIContents::Navigate(const NavigationEntry& entry, bool reload) { + const bool result = WebContents::Navigate(entry, reload); + + // Force the title to say 'New tab', even when loading. The supplied entry is + // also the pending entry. + NavigationEntry* pending_entry = controller()->GetPendingEntry(); + DCHECK(pending_entry && pending_entry == &entry); + pending_entry->SetTitle(forced_title_); + + return result; +} + +const std::wstring& NewTabUIContents::GetTitle() const { + if (!forced_title_.empty()) + return forced_title_; + return WebContents::GetTitle(); +} + +void NewTabUIContents::SetInitialFocus() { + // TODO(evanm): this code is duplicated in three places now. + // Should probably be refactored. + // Focus the location bar when we first get the focus. + int tab_index; + Browser* browser = Browser::GetBrowserForController( + this->controller(), &tab_index); + if (browser) + browser->FocusLocationBar(); + else + ::SetFocus(GetHWND()); +} + +bool NewTabUIContents::SupportsURL(GURL* url) { + if (url->SchemeIs("javascript")) + return true; + return DOMUIHost::SupportsURL(url); +} + +void NewTabUIContents::RequestOpenURL(const GURL& url, + WindowOpenDisposition disposition) { + // The user opened a URL on the page (including "open in new window"). + // We count all such clicks as AUTO_BOOKMARK, which increments the site's + // visit count (which is used for ranking the most visited entries). + // Note this means we're including clicks on not only most visited thumbnails, + // but also clicks on recently bookmarked. + OpenURL(url, disposition, PageTransition::AUTO_BOOKMARK); + + // Figure out if this was a click on a MostVisited entry, and log it if so. + if (most_visited_handler_) { + const std::vector<GURL>& urls = most_visited_handler_->most_visited_urls(); + for (size_t i = 0; i < urls.size(); ++i) { + if (url == urls[i]) { + UserMetrics::RecordComputedAction(StringPrintf(L"MostVisited%d", i), + profile()); + break; + } + } + } +} diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h new file mode 100644 index 0000000..04d0855 --- /dev/null +++ b/chrome/browser/dom_ui/new_tab_ui.h @@ -0,0 +1,330 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_BROWSER_DOM_UI_NEW_TAB_UI_H__ +#define CHROME_BROWSER_DOM_UI_NEW_TAB_UI_H__ + +#include "chrome/browser/dom_ui/dom_ui_host.h" +#include "chrome/browser/dom_ui/chrome_url_data_manager.h" +#include "chrome/browser/history/history.h" +#include "chrome/browser/tab_restore_service.h" +#include "chrome/browser/template_url_model.h" + +class GURL; +class Profile; +class Value; +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. +bool NewTabUIHandleURL(GURL* url, TabContentsType* result_type); + +// The following classes aren't used outside of new_tab_ui.cc but are +// put here for clarity. + +class NewTabHTMLSource : public ChromeURLDataManager::DataSource { + public: + // Creates our datasource and sets our user message to a specific message + // from our string bundle. + NewTabHTMLSource(int message_id); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, int request_id); + + private: + // The ID of the message from our string bundle to display to the user. + int message_id_; + + DISALLOW_EVIL_CONSTRUCTORS(NewTabHTMLSource); +}; + +class IncognitoTabHTMLSource : public ChromeURLDataManager::DataSource { + public: + // Creates our datasource and sets our user message to a specific message + // from our string bundle. + IncognitoTabHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, int request_id); + + private: + 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); + + // 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); + + // 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 { + public: + explicit MostVisitedHandler(DOMUIHost* dom_ui_host); + virtual ~MostVisitedHandler(); + + // Callback for the "getMostVisited" message. + void HandleGetMostVisited(const Value* value); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + const std::vector<GURL>& most_visited_urls() const { + return most_visited_urls_; + } + + private: + // Callback from the history system when the most visited list is available. + void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle, + std::vector<PageUsageData*>* data); + + DOMUIHost* dom_ui_host_; + + // Our consumer for the history service. + CancelableRequestConsumerT<PageUsageData*, NULL> cancelable_consumer_; + + // The most visited URLs, in priority order. + // Only used for matching up clicks on the page to which most visited entry + // was clicked on for metrics purposes. + std::vector<GURL> most_visited_urls_; + + DISALLOW_EVIL_CONSTRUCTORS(MostVisitedHandler); +}; + +// The handler for Javascript messages related to the "common searches" view. +class TemplateURLHandler : public DOMMessageHandler, + public TemplateURLModelObserver { + public: + explicit TemplateURLHandler(DOMUIHost* dom_ui_host); + virtual ~TemplateURLHandler(); + + // Callback for the "getMostSearched" message, sent when the page requests + // the list of available searches. + void HandleGetMostSearched(const Value* content); + // Callback for the "doSearch" message, sent when the user wants to + // run a search. Content of the message is an array containing + // [<the search keyword>, <the search term>]. + void HandleDoSearch(const Value* content); + + // TemplateURLModelObserver implementation. + virtual void OnTemplateURLModelChanged(); + + private: + DOMUIHost* dom_ui_host_; + TemplateURLModel* template_url_model_; // Owned by profile. + + DISALLOW_EVIL_CONSTRUCTORS(TemplateURLHandler); +}; + +class RecentlyBookmarkedHandler : public DOMMessageHandler { + public: + explicit RecentlyBookmarkedHandler(DOMUIHost* dom_ui_host); + + // Callback which navigates to the bookmarks page. + void HandleShowBookmarkPage(const Value*); + + // Callback for the "getRecentlyBookmarked" message. + // It takes no arguments. + void HandleGetRecentlyBookmarked(const Value*); + + void OnMostRecentStarredEntries( + HistoryService::Handle request_handle, + std::vector<history::StarredEntry>* entries); + + private: + DOMUIHost* dom_ui_host_; + CancelableRequestConsumerT<int, 0> cancelable_consumer_; + + DISALLOW_EVIL_CONSTRUCTORS(RecentlyBookmarkedHandler); +}; + +class RecentlyClosedTabsHandler : public DOMMessageHandler, + public TabRestoreService::Observer { + public: + explicit RecentlyClosedTabsHandler(DOMUIHost* dom_ui_host); + virtual ~RecentlyClosedTabsHandler(); + + // Callback for the "reopenTab" message. Rewrites the history of the + // currently displayed tab to be the one in TabRestoreService with a + // history of a session passed in through the content pointer. + void HandleReopenTab(const Value* content); + + // Callback for the "getRecentlyClosedTabs" message. + void HandleGetRecentlyClosedTabs(const Value* content); + + // Observer callback for TabRestoreService::Observer. Sends data on + // recently closed tabs to the javascript side of this page to + // display to the user. + virtual void TabRestoreServiceChanged(TabRestoreService* service); + + // Observer callback to notice when our associated TabRestoreService + // is destroyed. + virtual void TabRestoreServiceDestroyed(TabRestoreService* service); + + private: + DOMUIHost* dom_ui_host_; + + /// TabRestoreService that we are observing. + TabRestoreService* tab_restore_service_; + + /// Controls the periodic calling of HandleRecentlyClosedTabs. + ScopedRunnableMethodFactory<RecentlyClosedTabsHandler> handle_recently_closed_tab_factory_; + + DISALLOW_EVIL_CONSTRUCTORS(RecentlyClosedTabsHandler); +}; + +class HistoryHandler : public DOMMessageHandler { + public: + explicit HistoryHandler(DOMUIHost* dom_ui_host); + + // Callback which navigates to the history page. + void HandleShowHistoryPage(const Value*); + + // Callback which navigates to the history page and performs a search. + void HandleSearchHistoryPage(const Value* content); + + private: + DOMUIHost* dom_ui_host_; + DISALLOW_EVIL_CONSTRUCTORS(HistoryHandler); +}; + +// The TabContents used for the New Tab page. +class NewTabUIContents : public DOMUIHost { + public: + NewTabUIContents(Profile* profile, + SiteInstance* instance, + RenderViewHostFactory* render_view_factory); + + // Set the title that overrides any other title provided for the tab. + // This lets you set the title that's displayed before the content loads, + // as well as override any "Loading..." text. + void set_forced_title(const std::wstring& title) { + forced_title_ = title; + } + + // DOMUIHost implementation. + virtual void AttachMessageHandlers(); + + // WebContents overrides. + // Overriden to force the title of the page to forced_title_. + virtual bool Navigate(const NavigationEntry& entry, bool reload); + // We don't want a favicon on the new tab page. + virtual bool ShouldDisplayFavIcon() { return false; } + // The bookmark bar is always visible on the new tab. + virtual bool IsBookmarkBarAlwaysVisible() { return true; } + // Return forced_title_ if it's available. + virtual const std::wstring& GetTitle() const; + // When we get the initial focus, focus the URL bar. + virtual void SetInitialFocus(); + // The URL bar should not display the current page's URL. + virtual bool ShouldDisplayURL() { return false; } + virtual bool SupportsURL(GURL* url); + // Clicking a URL on the page should count as an autobookmark click. + virtual void RequestOpenURL(const GURL& url, + WindowOpenDisposition disposition); + + private: + // The message id that should be displayed in this NewTabUIContents + // instance's motd area. + int motd_message_id_; + + // Whether the user is in incognito mode or not, used to determine + // what HTML to load. + bool incognito_; + + // A title for the page we force display of. + // This prevents intermediate titles (like "Loading...") from displaying. + std::wstring forced_title_; + + // A pointer to the handler for most visited. + // Owned by the DOMUIHost. + MostVisitedHandler* most_visited_handler_; + + DISALLOW_EVIL_CONSTRUCTORS(NewTabUIContents); +}; + +#endif CHROME_BROWSER_DOM_UI_NEW_TAB_UI_H__ |