summaryrefslogtreecommitdiffstats
path: root/chrome/browser/dom_ui
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/dom_ui
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_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.cc336
-rw-r--r--chrome/browser/dom_ui/chrome_url_data_manager.h168
-rw-r--r--chrome/browser/dom_ui/dom_ui_host.cc139
-rw-r--r--chrome/browser/dom_ui/dom_ui_host.h127
-rw-r--r--chrome/browser/dom_ui/html_dialog_contents.cc101
-rw-r--r--chrome/browser/dom_ui/html_dialog_contents.h97
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.cc852
-rw-r--r--chrome/browser/dom_ui/new_tab_ui.h330
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__