From 67fc03984940b07daa38970e6876fcec1fce0e8e Mon Sep 17 00:00:00 2001
From: "tfarina@chromium.org"
 <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Fri, 25 Feb 2011 02:56:57 +0000
Subject: WebUI: Move the core files of WebUI from chrome/browser/webui to
 content/browser/webui.

BUG=59946
TEST=trybots

Review URL: http://codereview.chromium.org/6575054

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76020 0039d316-1c4b-4281-b951-d872f2087c98
---
 content/browser/webui/web_ui.cc                    | 217 +++++++++++++
 content/browser/webui/web_ui.h                     | 221 +++++++++++++
 content/browser/webui/web_ui_browsertest.cc        |  77 +++++
 content/browser/webui/web_ui_browsertest.h         |  58 ++++
 content/browser/webui/web_ui_factory.cc            | 351 +++++++++++++++++++++
 content/browser/webui/web_ui_factory.h             |  67 ++++
 .../browser/webui/web_ui_handler_browsertest.cc    |  41 +++
 content/browser/webui/web_ui_handler_browsertest.h |  40 +++
 content/browser/webui/web_ui_unittest.cc           | 199 ++++++++++++
 content/browser/webui/web_ui_util.cc               |  64 ++++
 content/browser/webui/web_ui_util.h                |  49 +++
 11 files changed, 1384 insertions(+)
 create mode 100644 content/browser/webui/web_ui.cc
 create mode 100644 content/browser/webui/web_ui.h
 create mode 100644 content/browser/webui/web_ui_browsertest.cc
 create mode 100644 content/browser/webui/web_ui_browsertest.h
 create mode 100644 content/browser/webui/web_ui_factory.cc
 create mode 100644 content/browser/webui/web_ui_factory.h
 create mode 100644 content/browser/webui/web_ui_handler_browsertest.cc
 create mode 100644 content/browser/webui/web_ui_handler_browsertest.h
 create mode 100644 content/browser/webui/web_ui_unittest.cc
 create mode 100644 content/browser/webui/web_ui_util.cc
 create mode 100644 content/browser/webui/web_ui_util.h

(limited to 'content/browser/webui')

diff --git a/content/browser/webui/web_ui.cc b/content/browser/webui/web_ui.cc
new file mode 100644
index 0000000..11e346e
--- /dev/null
+++ b/content/browser/webui/web_ui.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webui/web_ui.h"
+
+#include "base/i18n/rtl.h"
+#include "base/json/json_writer.h"
+#include "base/stl_util-inl.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/browser/themes/browser_theme_provider.h"
+#include "chrome/browser/webui/generic_handler.h"
+#include "chrome/common/bindings_policy.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+
+namespace {
+
+std::wstring GetJavascript(const std::wstring& function_name,
+                           const std::vector<const Value*>& arg_list) {
+  std::wstring parameters;
+  std::string json;
+  for (size_t i = 0; i < arg_list.size(); ++i) {
+    if (i > 0)
+      parameters += L",";
+
+    base::JSONWriter::Write(arg_list[i], false, &json);
+    parameters += UTF8ToWide(json);
+  }
+  return function_name + L"(" + parameters + L");";
+}
+
+}  // namespace
+
+WebUI::WebUI(TabContents* contents)
+    : hide_favicon_(false),
+      force_bookmark_bar_visible_(false),
+      focus_location_bar_by_default_(false),
+      should_hide_url_(false),
+      link_transition_type_(PageTransition::LINK),
+      bindings_(BindingsPolicy::WEB_UI),
+      register_callback_overwrites_(false),
+      tab_contents_(contents) {
+  GenericHandler* handler = new GenericHandler();
+  AddMessageHandler(handler->Attach(this));
+}
+
+WebUI::~WebUI() {
+  STLDeleteContainerPairSecondPointers(message_callbacks_.begin(),
+                                       message_callbacks_.end());
+  STLDeleteContainerPointers(handlers_.begin(), handlers_.end());
+}
+
+// WebUI, public: -------------------------------------------------------------
+
+void WebUI::ProcessWebUIMessage(const ViewHostMsg_DomMessage_Params& params) {
+  // Look up the callback for this message.
+  MessageCallbackMap::const_iterator callback =
+      message_callbacks_.find(params.name);
+  if (callback == message_callbacks_.end())
+    return;
+
+  // Forward this message and content on.
+  callback->second->Run(&params.arguments);
+}
+
+void WebUI::CallJavascriptFunction(const std::wstring& function_name) {
+  std::wstring javascript = function_name + L"();";
+  ExecuteJavascript(javascript);
+}
+
+void WebUI::CallJavascriptFunction(const std::wstring& function_name,
+                                   const Value& arg) {
+  std::vector<const Value*> args;
+  args.push_back(&arg);
+  ExecuteJavascript(GetJavascript(function_name, args));
+}
+
+void WebUI::CallJavascriptFunction(
+    const std::wstring& function_name,
+    const Value& arg1, const Value& arg2) {
+  std::vector<const Value*> args;
+  args.push_back(&arg1);
+  args.push_back(&arg2);
+  ExecuteJavascript(GetJavascript(function_name, args));
+}
+
+void WebUI::CallJavascriptFunction(
+    const std::wstring& function_name,
+    const Value& arg1, const Value& arg2, const Value& arg3) {
+  std::vector<const Value*> args;
+  args.push_back(&arg1);
+  args.push_back(&arg2);
+  args.push_back(&arg3);
+  ExecuteJavascript(GetJavascript(function_name, args));
+}
+
+void WebUI::CallJavascriptFunction(
+    const std::wstring& function_name,
+    const Value& arg1,
+    const Value& arg2,
+    const Value& arg3,
+    const Value& arg4) {
+  std::vector<const Value*> args;
+  args.push_back(&arg1);
+  args.push_back(&arg2);
+  args.push_back(&arg3);
+  args.push_back(&arg4);
+  ExecuteJavascript(GetJavascript(function_name, args));
+}
+
+void WebUI::CallJavascriptFunction(
+    const std::wstring& function_name,
+    const std::vector<const Value*>& args) {
+  ExecuteJavascript(GetJavascript(function_name, args));
+}
+
+ui::ThemeProvider* WebUI::GetThemeProvider() const {
+  return GetProfile()->GetThemeProvider();
+}
+
+void WebUI::RegisterMessageCallback(const std::string &message,
+                                    MessageCallback *callback) {
+  std::pair<MessageCallbackMap::iterator, bool> result =
+      message_callbacks_.insert(std::make_pair(message, callback));
+
+  // Overwrite preexisting message callback mappings.
+  if (register_callback_overwrites() && !result.second)
+    result.first->second = callback;
+}
+
+Profile* WebUI::GetProfile() const {
+  DCHECK(tab_contents());
+  return tab_contents()->profile();
+}
+
+RenderViewHost* WebUI::GetRenderViewHost() const {
+  DCHECK(tab_contents());
+  return tab_contents()->render_view_host();
+}
+
+// WebUI, protected: ----------------------------------------------------------
+
+void WebUI::AddMessageHandler(WebUIMessageHandler* handler) {
+  handlers_.push_back(handler);
+}
+
+void WebUI::ExecuteJavascript(const std::wstring& javascript) {
+  GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(),
+                                                   WideToUTF16Hack(javascript));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// WebUIMessageHandler
+WebUIMessageHandler::WebUIMessageHandler() : web_ui_(NULL) {
+}
+
+WebUIMessageHandler::~WebUIMessageHandler() {
+}
+
+WebUIMessageHandler* WebUIMessageHandler::Attach(WebUI* web_ui) {
+  web_ui_ = web_ui;
+  RegisterMessages();
+  return this;
+}
+
+// WebUIMessageHandler, protected: ---------------------------------------------
+
+void WebUIMessageHandler::SetURLAndTitle(DictionaryValue* dictionary,
+                                         string16 title,
+                                         const GURL& gurl) {
+  dictionary->SetString("url", gurl.spec());
+
+  bool using_url_as_the_title = false;
+  if (title.empty()) {
+    using_url_as_the_title = true;
+    title = UTF8ToUTF16(gurl.spec());
+  }
+
+  // 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.
+  string16 title_to_set(title);
+  if (base::i18n::IsRTL()) {
+    if (using_url_as_the_title) {
+      base::i18n::WrapStringWithLTRFormatting(&title_to_set);
+    } else {
+      base::i18n::AdjustStringForLocaleDirection(&title_to_set);
+    }
+  }
+  dictionary->SetString("title", title_to_set);
+}
+
+bool WebUIMessageHandler::ExtractIntegerValue(const ListValue* value,
+                                              int* out_int) {
+  std::string string_value;
+  if (value->GetString(0, &string_value))
+    return base::StringToInt(string_value, out_int);
+  NOTREACHED();
+  return false;
+}
+
+// TODO(viettrungluu): convert to string16 (or UTF-8 std::string?).
+std::wstring WebUIMessageHandler::ExtractStringValue(const ListValue* value) {
+  string16 string16_value;
+  if (value->GetString(0, &string16_value))
+    return UTF16ToWideHack(string16_value);
+  NOTREACHED();
+  return std::wstring();
+}
diff --git a/content/browser/webui/web_ui.h b/content/browser/webui/web_ui.h
new file mode 100644
index 0000000..0ff0b15
--- /dev/null
+++ b/content/browser/webui/web_ui.h
@@ -0,0 +1,221 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_H_
+#define CONTENT_BROWSER_WEBUI_WEB_UI_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/string16.h"
+#include "chrome/common/page_transition_types.h"
+
+class DictionaryValue;
+class WebUIMessageHandler;
+class GURL;
+class ListValue;
+class Profile;
+class RenderViewHost;
+class TabContents;
+class Value;
+struct ViewHostMsg_DomMessage_Params;
+
+namespace ui {
+class ThemeProvider;
+}
+
+// A WebUI sets up the datasources and message handlers for a given HTML-based
+// UI. It is contained by a WebUIManager.
+class WebUI {
+ public:
+  explicit WebUI(TabContents* contents);
+  virtual ~WebUI();
+
+  // Called by RenderViewHost when the RenderView is first created. This is
+  // *not* called for every page load because in some cases
+  // RenderViewHostManager will reuse RenderView instances. In those cases,
+  // RenderViewReused will be called instead.
+  virtual void RenderViewCreated(RenderViewHost* render_view_host) {}
+
+  // Called by RenderViewHostManager when a RenderView is reused to display a
+  // page.
+  virtual void RenderViewReused(RenderViewHost* render_view_host) {}
+
+  // Called when this becomes the active WebUI instance for a re-used
+  // RenderView; this is the point at which this WebUI instance will receive
+  // DOM messages instead of the previous WebUI instance.
+  //
+  // If a WebUI instance has code that is usually triggered from a JavaScript
+  // onload handler, this should be overridden to check to see if the web page's
+  // DOM is still intact (e.g., due to a back/forward navigation that remains
+  // within the same page), and if so trigger that code manually since onload
+  // won't be run in that case.
+  virtual void DidBecomeActiveForReusedRenderView() {}
+
+  // Called from TabContents.
+  virtual void ProcessWebUIMessage(const ViewHostMsg_DomMessage_Params& params);
+
+  // Used by WebUIMessageHandlers.
+  typedef Callback1<const ListValue*>::Type MessageCallback;
+  void RegisterMessageCallback(const std::string& message,
+                               MessageCallback* callback);
+
+  // Returns true if the favicon should be hidden for the current tab.
+  bool hide_favicon() const {
+    return hide_favicon_;
+  }
+
+  // Returns true if the bookmark bar should be forced to being visible,
+  // overriding the user's preference.
+  bool force_bookmark_bar_visible() const {
+    return force_bookmark_bar_visible_;
+  }
+
+  // Returns true if the location bar should be focused by default rather than
+  // the page contents. Some pages will want to use this to encourage the user
+  // to type in the URL bar.
+  bool focus_location_bar_by_default() const {
+    return focus_location_bar_by_default_;
+  }
+
+  // Returns true if the page's URL should be hidden. Some Web UI pages
+  // like the new tab page will want to hide it.
+  bool should_hide_url() const {
+    return should_hide_url_;
+  }
+
+  // Gets a custom tab title provided by the Web UI. If there is no title
+  // override, the string will be empty which should trigger the default title
+  // behavior for the tab.
+  const string16& overridden_title() const {
+    return overridden_title_;
+  }
+
+  // Returns the transition type that should be used for link clicks on this
+  // Web UI. This will default to LINK but may be overridden.
+  PageTransition::Type link_transition_type() const {
+    return link_transition_type_;
+  }
+
+  int bindings() const {
+    return bindings_;
+  }
+
+  // Indicates whether RegisterMessageCallback() will overwrite an existing
+  // message callback mapping.  Serves as the hook for test mocks.
+  bool register_callback_overwrites() const {
+    return register_callback_overwrites_;
+  }
+
+  void register_callback_overwrites(bool value) {
+    register_callback_overwrites_ = value;
+  }
+
+  // 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 variants for calls with more arguments.
+  void CallJavascriptFunction(const std::wstring& function_name);
+  void CallJavascriptFunction(const std::wstring& function_name,
+                              const Value& arg);
+  void CallJavascriptFunction(const std::wstring& function_name,
+                              const Value& arg1,
+                              const Value& arg2);
+  void CallJavascriptFunction(const std::wstring& function_name,
+                              const Value& arg1,
+                              const Value& arg2,
+                              const Value& arg3);
+  void CallJavascriptFunction(const std::wstring& function_name,
+                              const Value& arg1,
+                              const Value& arg2,
+                              const Value& arg3,
+                              const Value& arg4);
+  void CallJavascriptFunction(const std::wstring& function_name,
+                              const std::vector<const Value*>& args);
+
+  ui::ThemeProvider* GetThemeProvider() const;
+
+  // May be overridden by WebUI's which do not have a tab contents.
+  virtual Profile* GetProfile() const;
+
+  // May be overridden by WebUI's which do not have a tab contents.
+  virtual RenderViewHost* GetRenderViewHost() const;
+
+  TabContents* tab_contents() const { return tab_contents_; }
+
+ protected:
+  void AddMessageHandler(WebUIMessageHandler* handler);
+
+  // Execute a string of raw Javascript on the page.  Overridable for
+  // testing purposes.
+  virtual void ExecuteJavascript(const std::wstring& javascript);
+
+  // Options that may be overridden by individual Web UI implementations. The
+  // bool options default to false. See the public getters for more information.
+  bool hide_favicon_;
+  bool force_bookmark_bar_visible_;
+  bool focus_location_bar_by_default_;
+  bool should_hide_url_;
+  string16 overridden_title_;  // Defaults to empty string.
+  PageTransition::Type link_transition_type_;  // Defaults to LINK.
+  int bindings_;  // The bindings from BindingsPolicy that should be enabled for
+                  // this page.
+
+  // Used by test mocks. See the public getters for more information.
+  bool register_callback_overwrites_;  // Defaults to false.
+
+  // The WebUIMessageHandlers we own.
+  std::vector<WebUIMessageHandler*> handlers_;
+
+  // Non-owning pointer to the TabContents this WebUI is associated with.
+  TabContents* tab_contents_;
+
+ private:
+  // A map of message name -> message handling callback.
+  typedef std::map<std::string, MessageCallback*> MessageCallbackMap;
+  MessageCallbackMap message_callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUI);
+};
+
+// Messages sent from the DOM are forwarded via the WebUI to handler
+// classes. These objects are owned by WebUI and destroyed when the
+// host is destroyed.
+class WebUIMessageHandler {
+ public:
+  WebUIMessageHandler();
+  virtual ~WebUIMessageHandler();
+
+  // Attaches |this| to |web_ui| in order to handle messages from it.  Declared
+  // virtual so that subclasses can do special init work as soon as the web_ui
+  // is provided.  Returns |this| for convenience.
+  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
+
+ protected:
+  // Adds "url" and "title" keys on incoming dictionary, setting title
+  // as the url as a fallback on empty title.
+  static void SetURLAndTitle(DictionaryValue* dictionary,
+                             string16 title,
+                             const GURL& gurl);
+
+  // This is where subclasses specify which messages they'd like to handle.
+  virtual void RegisterMessages() = 0;
+
+  // Extract an integer value from a list Value.
+  bool ExtractIntegerValue(const ListValue* value, int* out_int);
+
+  // Extract a string value from a list Value.
+  std::wstring ExtractStringValue(const ListValue* value);
+
+  WebUI* web_ui_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebUIMessageHandler);
+};
+
+#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_H_
diff --git a/content/browser/webui/web_ui_browsertest.cc b/content/browser/webui/web_ui_browsertest.cc
new file mode 100644
index 0000000..18836c3
--- /dev/null
+++ b/content/browser/webui/web_ui_browsertest.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webui/web_ui_browsertest.h"
+
+#include "base/path_service.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/ui_test_utils.h"
+#include "ui/base/resource/resource_bundle.h"
+
+static const FilePath::CharType* kWebUILibraryJS =
+    FILE_PATH_LITERAL("test_api.js");
+static const FilePath::CharType* kWebUITestFolder = FILE_PATH_LITERAL("webui");
+
+WebUIBrowserTest::~WebUIBrowserTest() {}
+
+bool WebUIBrowserTest::RunWebUITest(const FilePath::CharType* src_path) {
+  std::string content;
+  BuildJavaScriptTest(FilePath(src_path), &content);
+  SetupHandlers();
+  return test_handler_->Execute(content);
+}
+
+WebUIBrowserTest::WebUIBrowserTest()
+    : test_handler_(new WebUIHandlerBrowserTest()) {}
+
+void WebUIBrowserTest::SetUpInProcessBrowserTestFixture() {
+  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_));
+  test_data_directory_ = test_data_directory_.Append(kWebUITestFolder);
+
+  // TODO(dtseng): should this be part of every BrowserTest or just WebUI test.
+  FilePath resources_pack_path;
+  PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
+  ResourceBundle::AddDataPackToSharedInstance(resources_pack_path);
+}
+
+WebUIMessageHandler* WebUIBrowserTest::GetMockMessageHandler() {
+  return NULL;
+}
+
+void WebUIBrowserTest::BuildJavaScriptTest(const FilePath& src_path,
+                                           std::string* content) {
+  ASSERT_TRUE(content != NULL);
+  std::string library_content, src_content;
+  ASSERT_TRUE(file_util::ReadFileToString(
+      test_data_directory_.Append(FilePath(kWebUILibraryJS)),
+          &library_content));
+  ASSERT_TRUE(file_util::ReadFileToString(
+      test_data_directory_.Append(src_path), &src_content));
+
+  content->append(library_content);
+  content->append(";\n");
+  content->append(src_content);
+}
+
+void WebUIBrowserTest::SetupHandlers() {
+  WebUI* web_ui_instance =
+      browser()->GetSelectedTabContents()->web_ui();
+  ASSERT_TRUE(web_ui_instance != NULL);
+  web_ui_instance->register_callback_overwrites(true);
+  test_handler_->Attach(web_ui_instance);
+
+  if (GetMockMessageHandler())
+    GetMockMessageHandler()->Attach(web_ui_instance);
+}
+
+IN_PROC_BROWSER_TEST_F(WebUIBrowserTest, TestSamplePass) {
+  // Navigate to UI.
+  // TODO(dtseng): make accessor for subclasses to return?
+  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIDownloadsURL));
+
+  ASSERT_TRUE(RunWebUITest(FILE_PATH_LITERAL("sample_downloads.js")));
+}
diff --git a/content/browser/webui/web_ui_browsertest.h b/content/browser/webui/web_ui_browsertest.h
new file mode 100644
index 0000000..000fbde
--- /dev/null
+++ b/content/browser/webui/web_ui_browsertest.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_BROWSERTEST_H_
+#define CONTENT_BROWSER_WEBUI_WEB_UI_BROWSERTEST_H_
+#pragma once
+
+#include <string>
+
+#include "base/file_path.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "content/browser/webui/web_ui_handler_browsertest.h"
+
+class WebUIMessageHandler;
+
+// The runner of WebUI javascript based tests.
+// See chrome/test/data/webui/test_api.js for the javascript side test API's.
+//
+// These tests should follow the form given in:
+// chrome/test/data/webui/sample_downloads.js.
+// and the lone test within this class.
+class WebUIBrowserTest : public InProcessBrowserTest {
+ public:
+  virtual ~WebUIBrowserTest();
+
+  bool RunWebUITest(const FilePath::CharType* src_path);
+
+ protected:
+  WebUIBrowserTest();
+
+  // Setup test path.
+  virtual void SetUpInProcessBrowserTestFixture();
+
+  // Returns a mock WebUI object under test (if any).
+  virtual WebUIMessageHandler* GetMockMessageHandler();
+
+ private:
+  // Builds a javascript test in the form:
+  // <js_library> ...
+  // <src_path> ...
+  //   runTests(function test1() {...},
+  //      ...
+  //   );
+  void BuildJavaScriptTest(const FilePath& src_path,
+                           std::string* content);
+
+  // Attaches mock and test handlers.
+  void SetupHandlers();
+
+  // Handles test framework messages.
+  scoped_ptr<WebUIHandlerBrowserTest> test_handler_;
+
+  // Location of test data (currently test/data/webui).
+  FilePath test_data_directory_;
+};
+
+#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_BROWSERTEST_H_
diff --git a/content/browser/webui/web_ui_factory.cc b/content/browser/webui/web_ui_factory.cc
new file mode 100644
index 0000000..f73a516
--- /dev/null
+++ b/content/browser/webui/web_ui_factory.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webui/web_ui_factory.h"
+
+#include "base/command_line.h"
+#include "chrome/browser/about_flags.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_web_ui.h"
+#include "chrome/browser/extensions/extensions_ui.h"
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/webui/bookmarks_ui.h"
+#include "chrome/browser/webui/bug_report_ui.h"
+#include "chrome/browser/webui/constrained_html_ui.h"
+#include "chrome/browser/webui/crashes_ui.h"
+#include "chrome/browser/webui/devtools_ui.h"
+#include "chrome/browser/webui/downloads_ui.h"
+#include "chrome/browser/webui/flags_ui.h"
+#include "chrome/browser/webui/gpu_internals_ui.h"
+#include "chrome/browser/webui/history2_ui.h"
+#include "chrome/browser/webui/history_ui.h"
+#include "chrome/browser/webui/html_dialog_ui.h"
+#include "chrome/browser/webui/net_internals_ui.h"
+#include "chrome/browser/webui/new_tab_ui.h"
+#include "chrome/browser/webui/options/options_ui.h"
+#include "chrome/browser/webui/plugins_ui.h"
+#include "chrome/browser/webui/print_preview_ui.h"
+#include "chrome/browser/webui/remoting_ui.h"
+#include "chrome/browser/webui/slideshow_ui.h"
+#include "chrome/browser/webui/sync_internals_ui.h"
+#include "chrome/browser/webui/textfields_ui.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/webui/imageburner_ui.h"
+#include "chrome/browser/chromeos/webui/keyboard_overlay_ui.h"
+#include "chrome/browser/chromeos/webui/menu_ui.h"
+#include "chrome/browser/chromeos/webui/mobile_setup_ui.h"
+#include "chrome/browser/chromeos/webui/network_menu_ui.h"
+#include "chrome/browser/chromeos/webui/register_page_ui.h"
+#include "chrome/browser/chromeos/webui/system_info_ui.h"
+#include "chrome/browser/chromeos/webui/wrench_menu_ui.h"
+#include "chrome/browser/webui/filebrowse_ui.h"
+#include "chrome/browser/webui/mediaplayer_ui.h"
+#endif
+
+#if defined(TOUCH_UI)
+#include "chrome/browser/webui/keyboard_ui.h"
+#endif
+
+#if defined(TOUCH_UI) && defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/webui/login/login_ui.h"
+#endif
+
+#if defined(OS_WIN)
+#include "chrome/browser/webui/conflicts_ui.h"
+#endif
+
+const WebUITypeID WebUIFactory::kNoWebUI = NULL;
+
+// A function for creating a new WebUI. The caller owns the return value, which
+// may be NULL (for example, if the URL refers to an non-existent extension).
+typedef WebUI* (*WebUIFactoryFunction)(TabContents* tab_contents,
+                                       const GURL& url);
+
+// Template for defining WebUIFactoryFunction.
+template<class T>
+WebUI* NewWebUI(TabContents* contents, const GURL& url) {
+  return new T(contents);
+}
+
+// Special case for extensions.
+template<>
+WebUI* NewWebUI<ExtensionWebUI>(TabContents* contents, const GURL& url) {
+  // Don't use a WebUI for incognito tabs because we require extensions to run
+  // within a single process.
+  ExtensionService* service = contents->profile()->GetExtensionService();
+  if (service &&
+      service->ExtensionBindingsAllowed(url)) {
+    return new ExtensionWebUI(contents, url);
+  }
+  return NULL;
+}
+
+// Returns a function that can be used to create the right type of WebUI for a
+// tab, based on its URL. Returns NULL if the URL doesn't have WebUI associated
+// with it. Even if the factory function is valid, it may yield a NULL WebUI
+// when invoked for a particular tab - see NewWebUI<ExtensionWebUI>.
+static WebUIFactoryFunction GetWebUIFactoryFunction(Profile* profile,
+    const GURL& url) {
+  // Currently, any gears: URL means an HTML dialog.
+  if (url.SchemeIs(chrome::kGearsScheme))
+    return &NewWebUI<HtmlDialogUI>;
+
+  if (url.host() == chrome::kChromeUIDialogHost)
+    return &NewWebUI<ConstrainedHtmlUI>;
+
+  ExtensionService* service = profile ? profile->GetExtensionService() : NULL;
+  if (service && service->ExtensionBindingsAllowed(url))
+    return &NewWebUI<ExtensionWebUI>;
+
+  // All platform builds of Chrome will need to have a cloud printing
+  // dialog as backup.  It's just that on Chrome OS, it's the only
+  // print dialog.
+  if (url.host() == chrome::kCloudPrintResourcesHost)
+    return &NewWebUI<ExternalHtmlDialogUI>;
+
+  // This will get called a lot to check all URLs, so do a quick check of other
+  // schemes (gears was handled above) to filter out most URLs.
+  if (!url.SchemeIs(chrome::kChromeDevToolsScheme) &&
+      !url.SchemeIs(chrome::kChromeInternalScheme) &&
+      !url.SchemeIs(chrome::kChromeUIScheme))
+    return NULL;
+
+  if (url.host() == chrome::kChromeUISyncResourcesHost ||
+      url.host() == chrome::kChromeUIRemotingResourcesHost ||
+      url.host() == chrome::kCloudPrintSetupHost)
+    return &NewWebUI<HtmlDialogUI>;
+
+  // Special case the new tab page. In older versions of Chrome, the new tab
+  // page was hosted at chrome-internal:<blah>. This might be in people's saved
+  // sessions or bookmarks, so we say any URL with that scheme triggers the new
+  // tab page.
+  if (url.host() == chrome::kChromeUINewTabHost ||
+      url.SchemeIs(chrome::kChromeInternalScheme))
+    return &NewWebUI<NewTabUI>;
+
+  // Give about:about a generic Web UI so it can navigate to pages with Web UIs.
+  if (url.spec() == chrome::kChromeUIAboutAboutURL)
+    return &NewWebUI<WebUI>;
+
+  // We must compare hosts only since some of the Web UIs append extra stuff
+  // after the host name.
+  if (url.host() == chrome::kChromeUIBookmarksHost)
+    return &NewWebUI<BookmarksUI>;
+  if (url.host() == chrome::kChromeUIBugReportHost)
+    return &NewWebUI<BugReportUI>;
+  if (url.host() == chrome::kChromeUICrashesHost)
+    return &NewWebUI<CrashesUI>;
+  if (url.host() == chrome::kChromeUIDevToolsHost)
+    return &NewWebUI<DevToolsUI>;
+#if defined(OS_WIN)
+  if (url.host() == chrome::kChromeUIConflictsHost)
+    return &NewWebUI<ConflictsUI>;
+#endif
+  if (url.host() == chrome::kChromeUIDownloadsHost)
+    return &NewWebUI<DownloadsUI>;
+  if (url.host() == chrome::kChromeUITextfieldsHost)
+    return &NewWebUI<TextfieldsUI>;
+  if (url.host() == chrome::kChromeUIExtensionsHost)
+    return &NewWebUI<ExtensionsUI>;
+  if (url.host() == chrome::kChromeUIHistoryHost)
+    return &NewWebUI<HistoryUI>;
+  if (url.host() == chrome::kChromeUIHistory2Host)
+    return &NewWebUI<HistoryUI2>;
+  if (url.host() == chrome::kChromeUIFlagsHost)
+    return &NewWebUI<FlagsUI>;
+#if defined(TOUCH_UI)
+  if (url.host() == chrome::kChromeUIKeyboardHost)
+    return &NewWebUI<KeyboardUI>;
+#endif
+  if (url.host() == chrome::kChromeUIGpuInternalsHost)
+    return &NewWebUI<GpuInternalsUI>;
+  if (url.host() == chrome::kChromeUINetInternalsHost)
+    return &NewWebUI<NetInternalsUI>;
+  if (url.host() == chrome::kChromeUIPluginsHost)
+    return &NewWebUI<PluginsUI>;
+  if (url.host() == chrome::kChromeUISyncInternalsHost)
+    return &NewWebUI<SyncInternalsUI>;
+#if defined(ENABLE_REMOTING)
+  if (url.host() == chrome::kChromeUIRemotingHost) {
+    if (CommandLine::ForCurrentProcess()->HasSwitch(
+        switches::kEnableRemoting)) {
+      return &NewWebUI<RemotingUI>;
+    }
+  }
+#endif
+
+#if defined(OS_CHROMEOS)
+  if (url.host() == chrome::kChromeUIFileBrowseHost)
+    return &NewWebUI<FileBrowseUI>;
+  if (url.host() == chrome::kChromeUIImageBurnerHost)
+    return &NewWebUI<ImageBurnUI>;
+  if (url.host() == chrome::kChromeUIKeyboardOverlayHost)
+    return &NewWebUI<KeyboardOverlayUI>;
+  if (url.host() == chrome::kChromeUIMediaplayerHost)
+    return &NewWebUI<MediaplayerUI>;
+  if (url.host() == chrome::kChromeUIMobileSetupHost)
+    return &NewWebUI<MobileSetupUI>;
+  if (url.host() == chrome::kChromeUIRegisterPageHost)
+    return &NewWebUI<RegisterPageUI>;
+  if (url.host() == chrome::kChromeUISettingsHost)
+    return &NewWebUI<OptionsUI>;
+  if (url.host() == chrome::kChromeUISlideshowHost)
+    return &NewWebUI<SlideshowUI>;
+  if (url.host() == chrome::kChromeUISystemInfoHost)
+    return &NewWebUI<SystemInfoUI>;
+  if (url.host() == chrome::kChromeUIMenu)
+    return &NewWebUI<chromeos::MenuUI>;
+  if (url.host() == chrome::kChromeUIWrenchMenu)
+    return &NewWebUI<chromeos::WrenchMenuUI>;
+  if (url.host() == chrome::kChromeUINetworkMenu)
+    return &NewWebUI<chromeos::NetworkMenuUI>;
+#else
+  if (url.host() == chrome::kChromeUISettingsHost) {
+    if (!CommandLine::ForCurrentProcess()->HasSwitch(
+        switches::kDisableTabbedOptions)) {
+      return &NewWebUI<OptionsUI>;
+    }
+  }
+  if (url.host() == chrome::kChromeUIPrintHost) {
+    if (CommandLine::ForCurrentProcess()->HasSwitch(
+        switches::kEnablePrintPreview)) {
+      return &NewWebUI<PrintPreviewUI>;
+    }
+  }
+#endif  // defined(OS_CHROMEOS)
+
+#if defined(TOUCH_UI) && defined(OS_CHROMEOS)
+  if (url.host() == chrome::kChromeUILoginHost)
+    return &NewWebUI<chromeos::LoginUI>;
+#endif
+
+  if (url.spec() == chrome::kChromeUIConstrainedHTMLTestURL)
+    return &NewWebUI<ConstrainedHtmlUI>;
+
+  return NULL;
+}
+
+// static
+WebUITypeID WebUIFactory::GetWebUIType(Profile* profile, const GURL& url) {
+  WebUIFactoryFunction function = GetWebUIFactoryFunction(profile, url);
+  return function ? reinterpret_cast<WebUITypeID>(function) : kNoWebUI;
+}
+
+// static
+bool WebUIFactory::HasWebUIScheme(const GURL& url) {
+  return url.SchemeIs(chrome::kChromeDevToolsScheme) ||
+         url.SchemeIs(chrome::kChromeInternalScheme) ||
+         url.SchemeIs(chrome::kChromeUIScheme) ||
+         url.SchemeIs(chrome::kExtensionScheme);
+}
+
+// static
+bool WebUIFactory::UseWebUIForURL(Profile* profile, const GURL& url) {
+  return GetWebUIFactoryFunction(profile, url) != NULL;
+}
+
+// static
+bool WebUIFactory::IsURLAcceptableForWebUI(Profile* profile, const GURL& url) {
+  return UseWebUIForURL(profile, url) ||
+      // javacsript: URLs are allowed to run in Web UI pages
+      url.SchemeIs(chrome::kJavaScriptScheme) ||
+      // It's possible to load about:blank in a Web UI renderer.
+      // See http://crbug.com/42547
+      url.spec() == chrome::kAboutBlankURL ||
+      // about:crash, about:kill, about:hang, and about:shorthang are allowed.
+      url.spec() == chrome::kAboutCrashURL ||
+      url.spec() == chrome::kAboutKillURL ||
+      url.spec() == chrome::kAboutHangURL ||
+      url.spec() == chrome::kAboutShorthangURL;
+}
+
+// static
+WebUI* WebUIFactory::CreateWebUIForURL(TabContents* tab_contents,
+                                       const GURL& url) {
+  WebUIFactoryFunction function = GetWebUIFactoryFunction(
+      tab_contents->profile(), url);
+  if (!function)
+    return NULL;
+  return (*function)(tab_contents, url);
+}
+
+// static
+void WebUIFactory::GetFaviconForURL(Profile* profile,
+                                    FaviconService::GetFaviconRequest* request,
+                                    const GURL& page_url) {
+  // All extensions but the bookmark manager get their favicon from the icons
+  // part of the manifest.
+  if (page_url.SchemeIs(chrome::kExtensionScheme) &&
+      page_url.host() != extension_misc::kBookmarkManagerId) {
+    ExtensionWebUI::GetFaviconForURL(profile, request, page_url);
+  } else {
+    scoped_refptr<RefCountedMemory> icon_data(
+        WebUIFactory::GetFaviconResourceBytes(profile, page_url));
+    bool know_icon = icon_data.get() != NULL && icon_data->size() > 0;
+    request->ForwardResultAsync(
+        FaviconService::FaviconDataCallback::TupleType(request->handle(),
+            know_icon, icon_data, false, GURL()));
+  }
+}
+
+// static
+RefCountedMemory* WebUIFactory::GetFaviconResourceBytes(Profile* profile,
+                                                        const GURL& page_url) {
+  // The bookmark manager is a chrome extension, so we have to check for it
+  // before we check for extension scheme.
+  if (page_url.host() == extension_misc::kBookmarkManagerId)
+    return BookmarksUI::GetFaviconResourceBytes();
+
+  // The extension scheme is handled in GetFaviconForURL.
+  if (page_url.SchemeIs(chrome::kExtensionScheme)) {
+    NOTREACHED();
+    return NULL;
+  }
+
+  if (!HasWebUIScheme(page_url))
+    return NULL;
+
+#if defined(OS_WIN)
+  if (page_url.host() == chrome::kChromeUIConflictsHost)
+    return ConflictsUI::GetFaviconResourceBytes();
+#endif
+
+  if (page_url.host() == chrome::kChromeUICrashesHost)
+    return CrashesUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIDownloadsHost)
+    return DownloadsUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIExtensionsHost)
+    return ExtensionsUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIHistoryHost)
+    return HistoryUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIHistory2Host)
+    return HistoryUI2::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIFlagsHost)
+    return FlagsUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUISettingsHost)
+    return OptionsUI::GetFaviconResourceBytes();
+
+  if (page_url.host() == chrome::kChromeUIPluginsHost)
+    return PluginsUI::GetFaviconResourceBytes();
+
+#if defined(ENABLE_REMOTING)
+  if (page_url.host() == chrome::kChromeUIRemotingHost)
+    return RemotingUI::GetFaviconResourceBytes();
+#endif
+
+  return NULL;
+}
diff --git a/content/browser/webui/web_ui_factory.h b/content/browser/webui/web_ui_factory.h
new file mode 100644
index 0000000..2be656a
--- /dev/null
+++ b/content/browser/webui/web_ui_factory.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_FACTORY_H_
+#define CONTENT_BROWSER_WEBUI_WEB_UI_FACTORY_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "chrome/browser/favicon_service.h"
+
+class WebUI;
+class GURL;
+class Profile;
+class RefCountedMemory;
+class TabContents;
+
+// An opaque identifier used to identify a WebUI. This can only be compared to
+// kNoWebUI or other WebUI types. See GetWebUIType.
+typedef void* WebUITypeID;
+
+class WebUIFactory {
+ public:
+  // A special WebUI type that signifies that a given page would not use the
+  // Web UI system.
+  static const WebUITypeID kNoWebUI;
+
+  // Returns a type identifier indicating what WebUI we would use for the
+  // given URL. This is useful for comparing the potential WebUIs for two URLs.
+  // Returns kNoWebUI if the given URL will not use the Web UI system.
+  static WebUITypeID GetWebUIType(Profile* profile, const GURL& url);
+
+  // Returns true if the given URL's scheme would trigger the Web UI system.
+  // This is a less precise test than UseDONUIForURL, which tells you whether
+  // that specific URL matches a known one. This one is faster and can be used
+  // to determine security policy.
+  static bool HasWebUIScheme(const GURL& url);
+
+  // Returns true if the given URL must use the Web UI system.
+  static bool UseWebUIForURL(Profile* profile, const GURL& url);
+
+  // Returns true if the given URL can be loaded by Web UI system.  This
+  // includes URLs that can be loaded by normal tabs as well, such as
+  // javascript: URLs or about:hang.
+  static bool IsURLAcceptableForWebUI(Profile* profile, const GURL& url);
+
+  // Allocates a new WebUI object for the given URL, and returns it. If the URL
+  // is not a Web UI URL, then it will return NULL. When non-NULL, ownership of
+  // the returned pointer is passed to the caller.
+  static WebUI* CreateWebUIForURL(TabContents* tab_contents, const GURL& url);
+
+  // Get the favicon for |page_url| and forward the result to the |request|
+  // when loaded.
+  static void GetFaviconForURL(Profile* profile,
+                               FaviconService::GetFaviconRequest* request,
+                               const GURL& page_url);
+
+ private:
+  // Gets the data for the favicon for a WebUI page. Returns NULL if the WebUI
+  // does not have a favicon.
+  static RefCountedMemory* GetFaviconResourceBytes(Profile* profile,
+                                                   const GURL& page_url);
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WebUIFactory);
+};
+
+#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_FACTORY_H_
diff --git a/content/browser/webui/web_ui_handler_browsertest.cc b/content/browser/webui/web_ui_handler_browsertest.cc
new file mode 100644
index 0000000..ab580ee
--- /dev/null
+++ b/content/browser/webui/web_ui_handler_browsertest.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webui/web_ui_handler_browsertest.h"
+
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/test/ui_test_utils.h"
+
+bool WebUIHandlerBrowserTest::Execute(const std::string& js_test) {
+  web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
+      string16(), UTF8ToUTF16(js_test));
+  return WaitForResult();
+}
+
+void WebUIHandlerBrowserTest::HandlePass(const ListValue* args) {
+  test_succeeded_ = true;
+  if (is_waiting_)
+    MessageLoopForUI::current()->Quit();
+}
+
+void WebUIHandlerBrowserTest::HandleFail(const ListValue* args) {
+  test_succeeded_ = false;
+  if (is_waiting_)
+    MessageLoopForUI::current()->Quit();
+}
+
+void WebUIHandlerBrowserTest::RegisterMessages() {
+  web_ui_->RegisterMessageCallback("Pass",
+      NewCallback(this, &WebUIHandlerBrowserTest::HandlePass));
+  web_ui_->RegisterMessageCallback("Fail",
+      NewCallback(this, &WebUIHandlerBrowserTest::HandleFail));
+}
+
+bool WebUIHandlerBrowserTest::WaitForResult() {
+  is_waiting_ = true;
+  ui_test_utils::RunMessageLoop();
+  is_waiting_ = false;
+  return test_succeeded_;
+}
diff --git a/content/browser/webui/web_ui_handler_browsertest.h b/content/browser/webui/web_ui_handler_browsertest.h
new file mode 100644
index 0000000..b19484a
--- /dev/null
+++ b/content/browser/webui/web_ui_handler_browsertest.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_HANDLER_BROWSERTEST_H_
+#define CONTENT_BROWSER_WEBUI_WEB_UI_HANDLER_BROWSERTEST_H_
+#pragma once
+
+#include <string>
+
+#include "content/browser/webui/web_ui.h"
+
+// This class registers test framework specific handlers on WebUI objects.
+class WebUIHandlerBrowserTest : public WebUIMessageHandler {
+ public:
+  // Executes a string of javascript.  Returns pass fail.
+  bool Execute(const std::string& js_test);
+
+ protected:
+  // WebUI handlers which deliver results to any waiting message loops.
+  // |args| is currently ignored.
+  void HandlePass(const ListValue* args);
+  void HandleFail(const ListValue* args);
+
+  // WebUIMessageHandler overrides.
+  // Add test handlers to the current WebUI object.
+  virtual void RegisterMessages();
+
+ private:
+  // Runs a message loop until test finishes.  Returns the result of the test.
+  bool WaitForResult();
+
+  // Pass fail result of current tests.
+  bool test_succeeded_;
+
+  // Waiting for a test to finish.
+  bool is_waiting_;
+};
+
+#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_HANDLER_BROWSERTEST_H_
diff --git a/content/browser/webui/web_ui_unittest.cc b/content/browser/webui/web_ui_unittest.cc
new file mode 100644
index 0000000..4c5b48b
--- /dev/null
+++ b/content/browser/webui/web_ui_unittest.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/webui/new_tab_ui.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/renderer_host/test_render_view_host.h"
+#include "content/browser/site_instance.h"
+#include "content/browser/tab_contents/navigation_controller.h"
+#include "content/browser/tab_contents/test_tab_contents.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class WebUITest : public RenderViewHostTestHarness {
+ public:
+  WebUITest() : ui_thread_(BrowserThread::UI, MessageLoop::current()) {}
+
+  // Tests navigating with a Web UI from a fresh (nothing pending or committed)
+  // state, through pending, committed, then another navigation. The first page
+  // ID that we should use is passed as a parameter. We'll use the next two
+  // values. This must be increasing for the life of the tests.
+  static void DoNavigationTest(TabContents* contents, int page_id) {
+    NavigationController* controller = &contents->controller();
+
+    // Start a pending load.
+    GURL new_tab_url(chrome::kChromeUINewTabURL);
+    controller->LoadURL(new_tab_url, GURL(), PageTransition::LINK);
+
+    // The navigation entry should be pending with no committed entry.
+    ASSERT_TRUE(controller->pending_entry());
+    ASSERT_FALSE(controller->GetLastCommittedEntry());
+
+    // Check the things the pending Web UI should have set.
+    EXPECT_FALSE(contents->ShouldDisplayURL());
+    EXPECT_FALSE(contents->ShouldDisplayFavIcon());
+    EXPECT_TRUE(contents->ShouldShowBookmarkBar());
+    EXPECT_TRUE(contents->FocusLocationBarByDefault());
+
+    // Now commit the load.
+    static_cast<TestRenderViewHost*>(
+        contents->render_view_host())->SendNavigate(page_id, new_tab_url);
+
+    // The same flags should be set as before now that the load has committed.
+    EXPECT_FALSE(contents->ShouldDisplayURL());
+    EXPECT_FALSE(contents->ShouldDisplayFavIcon());
+    EXPECT_TRUE(contents->ShouldShowBookmarkBar());
+    EXPECT_TRUE(contents->FocusLocationBarByDefault());
+
+    // Start a pending navigation to a regular page.
+    GURL next_url("http://google.com/");
+    controller->LoadURL(next_url, GURL(), PageTransition::LINK);
+
+    // Check the flags. Some should reflect the new page (URL, title), some
+    // should reflect the old one (bookmark bar) until it has committed.
+    EXPECT_TRUE(contents->ShouldDisplayURL());
+    EXPECT_TRUE(contents->ShouldDisplayFavIcon());
+    EXPECT_TRUE(contents->ShouldShowBookmarkBar());
+    EXPECT_FALSE(contents->FocusLocationBarByDefault());
+
+    // Commit the regular page load. Note that we must send it to the "pending"
+    // RenderViewHost if there is one, since this transition will also cause a
+    // process transition, and our RVH pointer will be the "committed" one.
+    // In the second call to this function from WebUIToStandard, it won't
+    // actually be pending, which is the point of this test.
+    if (contents->render_manager()->pending_render_view_host()) {
+      static_cast<TestRenderViewHost*>(
+          contents->render_manager()->pending_render_view_host())->SendNavigate(
+              page_id + 1, next_url);
+    } else {
+      static_cast<TestRenderViewHost*>(
+          contents->render_view_host())->SendNavigate(page_id + 1, next_url);
+    }
+
+    // The state should now reflect a regular page.
+    EXPECT_TRUE(contents->ShouldDisplayURL());
+    EXPECT_TRUE(contents->ShouldDisplayFavIcon());
+    EXPECT_FALSE(contents->ShouldShowBookmarkBar());
+    EXPECT_FALSE(contents->FocusLocationBarByDefault());
+  }
+
+ private:
+  BrowserThread ui_thread_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUITest);
+};
+
+// Tests that the New Tab Page flags are correctly set and propogated by
+// TabContents when we first navigate to a Web UI page, then to a standard
+// non-DOM-UI page.
+TEST_F(WebUITest, WebUIToStandard) {
+  DoNavigationTest(contents(), 1);
+
+  // Test the case where we're not doing the initial navigation. This is
+  // slightly different than the very-first-navigation case since the
+  // SiteInstance will be the same (the original TabContents must still be
+  // alive), which will trigger different behavior in RenderViewHostManager.
+  TestTabContents contents2(profile_.get(), NULL);
+
+  DoNavigationTest(&contents2, 101);
+}
+
+TEST_F(WebUITest, WebUIToWebUI) {
+  // Do a load (this state is tested above).
+  GURL new_tab_url(chrome::kChromeUINewTabURL);
+  controller().LoadURL(new_tab_url, GURL(), PageTransition::LINK);
+  rvh()->SendNavigate(1, new_tab_url);
+
+  // Start another pending load of the new tab page.
+  controller().LoadURL(new_tab_url, GURL(), PageTransition::LINK);
+  rvh()->SendNavigate(2, new_tab_url);
+
+  // The flags should be the same as the non-pending state.
+  EXPECT_FALSE(contents()->ShouldDisplayURL());
+  EXPECT_FALSE(contents()->ShouldDisplayFavIcon());
+  EXPECT_TRUE(contents()->ShouldShowBookmarkBar());
+  EXPECT_TRUE(contents()->FocusLocationBarByDefault());
+}
+
+TEST_F(WebUITest, StandardToWebUI) {
+  // Start a pending navigation to a regular page.
+  GURL std_url("http://google.com/");
+
+  controller().LoadURL(std_url, GURL(), PageTransition::LINK);
+
+  // The state should now reflect the default.
+  EXPECT_TRUE(contents()->ShouldDisplayURL());
+  EXPECT_TRUE(contents()->ShouldDisplayFavIcon());
+  EXPECT_FALSE(contents()->ShouldShowBookmarkBar());
+  EXPECT_FALSE(contents()->FocusLocationBarByDefault());
+
+  // Commit the load, the state should be the same.
+  rvh()->SendNavigate(1, std_url);
+  EXPECT_TRUE(contents()->ShouldDisplayURL());
+  EXPECT_TRUE(contents()->ShouldDisplayFavIcon());
+  EXPECT_FALSE(contents()->ShouldShowBookmarkBar());
+  EXPECT_FALSE(contents()->FocusLocationBarByDefault());
+
+  // Start a pending load for a WebUI.
+  GURL new_tab_url(chrome::kChromeUINewTabURL);
+  controller().LoadURL(new_tab_url, GURL(), PageTransition::LINK);
+  EXPECT_FALSE(contents()->ShouldDisplayURL());
+  EXPECT_TRUE(contents()->ShouldDisplayFavIcon());
+  EXPECT_FALSE(contents()->ShouldShowBookmarkBar());
+  EXPECT_TRUE(contents()->FocusLocationBarByDefault());
+
+  // Committing Web UI is tested above.
+}
+
+class TabContentsForFocusTest : public TestTabContents {
+ public:
+  TabContentsForFocusTest(Profile* profile, SiteInstance* instance)
+      : TestTabContents(profile, instance), focus_called_(0) {
+  }
+
+  virtual void SetFocusToLocationBar(bool select_all) { ++focus_called_; }
+  int focus_called() const { return focus_called_; }
+
+ private:
+  int focus_called_;
+};
+
+TEST_F(WebUITest, FocusOnNavigate) {
+  // Setup.  |tc| will be used to track when we try to focus the location bar.
+  TabContentsForFocusTest* tc = new TabContentsForFocusTest(
+      contents()->profile(),
+      SiteInstance::CreateSiteInstance(contents()->profile()));
+  tc->controller().CopyStateFrom(controller());
+  scoped_ptr<TestTabContents> tc_scoped_ptr(tc);
+  contents_.swap(tc_scoped_ptr);
+  int page_id = 200;
+
+  // Load the NTP.
+  GURL new_tab_url(chrome::kChromeUINewTabURL);
+  controller().LoadURL(new_tab_url, GURL(), PageTransition::LINK);
+  rvh()->SendNavigate(page_id, new_tab_url);
+
+  // Navigate to another page.
+  GURL next_url("http://google.com/");
+  int next_page_id = page_id + 1;
+  controller().LoadURL(next_url, GURL(), PageTransition::LINK);
+  pending_rvh()->SendNavigate(next_page_id, next_url);
+
+  // Navigate back.  Should focus the location bar.
+  int focus_called = tc->focus_called();
+  ASSERT_TRUE(controller().CanGoBack());
+  controller().GoBack();
+  pending_rvh()->SendNavigate(page_id, new_tab_url);
+  EXPECT_LT(focus_called, tc->focus_called());
+
+  // Navigate forward.  Shouldn't focus the location bar.
+  focus_called = tc->focus_called();
+  ASSERT_TRUE(controller().CanGoForward());
+  controller().GoForward();
+  pending_rvh()->SendNavigate(next_page_id, next_url);
+  EXPECT_EQ(focus_called, tc->focus_called());
+
+  contents_.swap(tc_scoped_ptr);
+}
diff --git a/content/browser/webui/web_ui_util.cc b/content/browser/webui/web_ui_util.cc
new file mode 100644
index 0000000..e76aaef
--- /dev/null
+++ b/content/browser/webui/web_ui_util.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/webui/web_ui_util.h"
+
+#include <vector>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace web_ui_util {
+
+std::string GetJsonResponseFromFirstArgumentInList(const ListValue* args) {
+  return GetJsonResponseFromArgumentList(args, 0);
+}
+
+std::string GetJsonResponseFromArgumentList(const ListValue* args,
+                                            size_t list_index) {
+  std::string result;
+  if (args->GetSize() <= list_index) {
+    NOTREACHED();
+    return result;
+  }
+
+  Value* value = NULL;
+  if (args->Get(list_index, &value))
+    value->GetAsString(&result);
+  else
+    NOTREACHED();
+
+  return result;
+}
+
+std::string GetImageDataUrl(const SkBitmap& bitmap) {
+  std::vector<unsigned char> output;
+  gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &output);
+  std::string str_url;
+  std::copy(output.begin(), output.end(),
+            std::back_inserter(str_url));
+  base::Base64Encode(str_url, &str_url);
+  str_url.insert(0, "data:image/png;base64,");
+  return str_url;
+}
+
+std::string GetImageDataUrlFromResource(int res) {
+  // Load resource icon and covert to base64 encoded data url
+  RefCountedStaticMemory* icon_data =
+      ResourceBundle::GetSharedInstance().LoadDataResourceBytes(res);
+  if (!icon_data)
+    return std::string();
+  scoped_refptr<RefCountedMemory> raw_icon(icon_data);
+  std::string str_url;
+  std::copy(raw_icon->front(), raw_icon->front() + raw_icon->size(),
+            std::back_inserter(str_url));
+  base::Base64Encode(str_url, &str_url);
+  str_url.insert(0, "data:image/png;base64,");
+  return str_url;
+}
+
+}  // namespace web_ui_util
diff --git a/content/browser/webui/web_ui_util.h b/content/browser/webui/web_ui_util.h
new file mode 100644
index 0000000..d7034dc
--- /dev/null
+++ b/content/browser/webui/web_ui_util.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEBUI_WEB_UI_UTIL_H_
+#define CONTENT_BROWSER_WEBUI_WEB_UI_UTIL_H_
+#pragma once
+
+#include <string>
+
+class ListValue;
+class SkBitmap;
+
+namespace web_ui_util {
+
+// Convenience routine to get the response string from an argument
+// list.  Typically used when supporting a WebUI and getting calls
+// from the hosted code.  Content must be a ListValue with at least
+// one entry in it, and that first entry must be a string, which is
+// returned.  The parameter is a Value for convenience.  Returns an
+// empty string on error or if the parameter is not a ListValue.
+std::string GetJsonResponseFromFirstArgumentInList(const ListValue* args);
+
+// Convenience routine to get one of the response strings from an
+// argument list.  content must be a ListValue, with at least
+// (list_index+1) entries in it.  list_index is the 0-based index of
+// the entry to pull from that list, and that entry must be a string,
+// which is returned.  The parameter is a Value for convenience.
+// Returns an empty string on error or if the parameter is not a
+// ListValue.
+std::string GetJsonResponseFromArgumentList(const ListValue* args,
+                                            size_t list_index);
+
+
+// Convenience routine to convert SkBitmap object to data url
+// so that it can be used in WebUI.
+std::string GetImageDataUrl(const SkBitmap& bitmap);
+
+// Convenience routine to get data url that corresponds to given
+// resource_id as an image. This function does not check if the
+// resource for the |resource_id| is an image, therefore it is the
+// caller's responsibility to make sure the resource is indeed an
+// image. Returns empty string if a resource does not exist for given
+// |resource_id|.
+std::string GetImageDataUrlFromResource(int resource_id);
+
+}  // namespace web_ui_util
+
+#endif  // CONTENT_BROWSER_WEBUI_WEB_UI_UTIL_H_
-- 
cgit v1.1