diff options
author | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-27 05:58:07 +0000 |
---|---|---|
committer | tfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-27 05:58:07 +0000 |
commit | 265a149061df91fb2c794a877994a6b9b17c0395 (patch) | |
tree | de806aa83fd520d92ede0e06923744158e73eeaa /chrome/browser/ui | |
parent | 4ff6ce9702a91c52892786464b15f3c8588d4070 (diff) | |
download | chromium_src-265a149061df91fb2c794a877994a6b9b17c0395.zip chromium_src-265a149061df91fb2c794a877994a6b9b17c0395.tar.gz chromium_src-265a149061df91fb2c794a877994a6b9b17c0395.tar.bz2 |
WebUI: Move more files from chrome/browser/webui to chrome/browser/ui/webui.
I'm moving the files I have found with:
$ ls chrome/browser/webui | grep _ui
BUG=59946
TEST=trybots
Review URL: http://codereview.chromium.org/6599024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76183 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui')
74 files changed, 9958 insertions, 25 deletions
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 11ac457..51ff4b5 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -84,12 +84,12 @@ #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/browser/ui/tabs/dock_info.h" #include "chrome/browser/ui/tabs/tab_menu_model.h" +#include "chrome/browser/ui/webui/bug_report_ui.h" +#include "chrome/browser/ui/webui/filebrowse_ui.h" #include "chrome/browser/ui/webui/options/content_settings_handler.h" #include "chrome/browser/ui/window_sizer.h" #include "chrome/browser/upgrade_detector.h" #include "chrome/browser/web_applications/web_app.h" -#include "chrome/browser/webui/bug_report_ui.h" -#include "chrome/browser/webui/filebrowse_ui.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/content_restriction.h" diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc index d8cf4fc..a803d7b 100644 --- a/chrome/browser/ui/browser_init.cc +++ b/chrome/browser/ui/browser_init.cc @@ -93,7 +93,7 @@ #include "chrome/browser/chromeos/usb_mount_observer.h" #include "chrome/browser/chromeos/wm_message_listener.h" #include "chrome/browser/chromeos/wm_overview_controller.h" -#include "chrome/browser/webui/mediaplayer_ui.h" +#include "chrome/browser/ui/webui/mediaplayer_ui.h" #endif #if defined(HAVE_XINPUT2) diff --git a/chrome/browser/ui/cocoa/constrained_html_delegate_mac.mm b/chrome/browser/ui/cocoa/constrained_html_delegate_mac.mm index 52efd8e..61b9a75 100644 --- a/chrome/browser/ui/cocoa/constrained_html_delegate_mac.mm +++ b/chrome/browser/ui/cocoa/constrained_html_delegate_mac.mm @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/webui/constrained_html_ui.h" +#include "chrome/browser/ui/webui/constrained_html_ui.h" #include "base/scoped_nsobject.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/ui/cocoa/constrained_window_mac.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" #import <Cocoa/Cocoa.h> #include "ipc/ipc_message.h" diff --git a/chrome/browser/ui/cocoa/html_dialog_window_controller.h b/chrome/browser/ui/cocoa/html_dialog_window_controller.h index 89c3bf4..e5660da 100644 --- a/chrome/browser/ui/cocoa/html_dialog_window_controller.h +++ b/chrome/browser/ui/cocoa/html_dialog_window_controller.h @@ -10,7 +10,7 @@ #include "base/basictypes.h" #include "base/scoped_ptr.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" class HtmlDialogWindowDelegateBridge; class Profile; diff --git a/chrome/browser/ui/cocoa/html_dialog_window_controller.mm b/chrome/browser/ui/cocoa/html_dialog_window_controller.mm index d9fd127..68d25df 100644 --- a/chrome/browser/ui/cocoa/html_dialog_window_controller.mm +++ b/chrome/browser/ui/cocoa/html_dialog_window_controller.mm @@ -12,8 +12,8 @@ #import "chrome/browser/ui/browser_dialogs.h" #import "chrome/browser/ui/cocoa/browser_command_executor.h" #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "chrome/common/native_web_keyboard_event.h" #include "ipc/ipc_message.h" #include "ui/base/keycodes/keyboard_codes.h" diff --git a/chrome/browser/ui/cocoa/html_dialog_window_controller_unittest.mm b/chrome/browser/ui/cocoa/html_dialog_window_controller_unittest.mm index a8c9927..b8e2c66 100644 --- a/chrome/browser/ui/cocoa/html_dialog_window_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/html_dialog_window_controller_unittest.mm @@ -13,7 +13,7 @@ #include "base/sys_string_conversions.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/cocoa/cocoa_test_helper.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/test/browser_with_test_window_test.h" #include "chrome/test/testing_profile.h" #include "content/browser/webui/web_ui.h" diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc index b31becfc..e6b6eb4 100644 --- a/chrome/browser/ui/gtk/browser_window_gtk.cc +++ b/chrome/browser/ui/gtk/browser_window_gtk.cc @@ -73,8 +73,8 @@ #include "chrome/browser/ui/gtk/update_recommended_dialog.h" #include "chrome/browser/ui/omnibox/location_bar.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/browser/ui/webui/bug_report_ui.h" #include "chrome/browser/ui/window_sizer.h" -#include "chrome/browser/webui/bug_report_ui.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/notification_service.h" diff --git a/chrome/browser/ui/gtk/constrained_html_delegate_gtk.cc b/chrome/browser/ui/gtk/constrained_html_delegate_gtk.cc index 670d4cb..689125f 100644 --- a/chrome/browser/ui/gtk/constrained_html_delegate_gtk.cc +++ b/chrome/browser/ui/gtk/constrained_html_delegate_gtk.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/webui/constrained_html_ui.h" +#include "chrome/browser/ui/webui/constrained_html_ui.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/ui/gtk/constrained_window_gtk.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "chrome/browser/ui/gtk/tab_contents_container_gtk.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "chrome/common/notification_source.h" #include "ipc/ipc_message.h" #include "ui/gfx/rect.h" diff --git a/chrome/browser/ui/gtk/html_dialog_gtk.cc b/chrome/browser/ui/gtk/html_dialog_gtk.cc index 72ed2a1..e064b09 100644 --- a/chrome/browser/ui/gtk/html_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/html_dialog_gtk.cc @@ -13,7 +13,7 @@ #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "chrome/browser/ui/gtk/tab_contents_container_gtk.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/common/native_web_keyboard_event.h" #include "ipc/ipc_message.h" diff --git a/chrome/browser/ui/gtk/html_dialog_gtk.h b/chrome/browser/ui/gtk/html_dialog_gtk.h index 25f0fdb..11cbcca 100644 --- a/chrome/browser/ui/gtk/html_dialog_gtk.h +++ b/chrome/browser/ui/gtk/html_dialog_gtk.h @@ -10,8 +10,8 @@ #include <vector> #include "base/scoped_ptr.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "ui/base/gtk/gtk_signal.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/size.h" diff --git a/chrome/browser/ui/views/constrained_html_delegate_gtk.cc b/chrome/browser/ui/views/constrained_html_delegate_gtk.cc index 7b48fc4..df9ba4f 100644 --- a/chrome/browser/ui/views/constrained_html_delegate_gtk.cc +++ b/chrome/browser/ui/views/constrained_html_delegate_gtk.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/webui/constrained_html_ui.h" +#include "chrome/browser/ui/webui/constrained_html_ui.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/ui/gtk/constrained_window_gtk.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "chrome/browser/ui/views/tab_contents/tab_contents_container.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "ipc/ipc_message.h" #include "ui/gfx/rect.h" #include "views/widget/widget_gtk.h" diff --git a/chrome/browser/ui/views/constrained_html_delegate_win.cc b/chrome/browser/ui/views/constrained_html_delegate_win.cc index 744c4e0..b8d51c0 100644 --- a/chrome/browser/ui/views/constrained_html_delegate_win.cc +++ b/chrome/browser/ui/views/constrained_html_delegate_win.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/webui/constrained_html_ui.h" +#include "chrome/browser/ui/webui/constrained_html_ui.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/ui/views/tab_contents/tab_contents_container.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "ipc/ipc_message.h" #include "ui/gfx/rect.h" #include "views/view.h" diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 7b3bdc3..e4e92a1 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc @@ -59,8 +59,8 @@ #include "chrome/browser/ui/views/toolbar_view.h" #include "chrome/browser/ui/views/update_recommended_message_box.h" #include "chrome/browser/ui/views/window.h" +#include "chrome/browser/ui/webui/bug_report_ui.h" #include "chrome/browser/ui/window_sizer.h" -#include "chrome/browser/webui/bug_report_ui.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_resource.h" #include "chrome/common/native_window_notification_source.h" diff --git a/chrome/browser/ui/views/html_dialog_view.h b/chrome/browser/ui/views/html_dialog_view.h index 1a2d5cb..9329c1e 100644 --- a/chrome/browser/ui/views/html_dialog_view.h +++ b/chrome/browser/ui/views/html_dialog_view.h @@ -9,8 +9,8 @@ #include <string> #include "chrome/browser/ui/views/dom_view.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/browser/webui/html_dialog_tab_contents_delegate.h" -#include "chrome/browser/webui/html_dialog_ui.h" #include "ui/gfx/size.h" #include "views/window/window_delegate.h" diff --git a/chrome/browser/ui/views/html_dialog_view_browsertest.cc b/chrome/browser/ui/views/html_dialog_view_browsertest.cc index d5f78d0..7862c85 100644 --- a/chrome/browser/ui/views/html_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/html_dialog_view_browsertest.cc @@ -10,7 +10,7 @@ #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/ui/views/html_dialog_view.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/common/url_constants.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" diff --git a/chrome/browser/ui/views/keyboard_overlay_delegate.cc b/chrome/browser/ui/views/keyboard_overlay_delegate.cc index 2b2fef3..2591b68 100644 --- a/chrome/browser/ui/views/keyboard_overlay_delegate.cc +++ b/chrome/browser/ui/views/keyboard_overlay_delegate.cc @@ -10,7 +10,7 @@ #include "chrome/browser/chromeos/frame/bubble_window.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/html_dialog_view.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/common/url_constants.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chrome/browser/ui/views/keyboard_overlay_delegate.h b/chrome/browser/ui/views/keyboard_overlay_delegate.h index c85b17b..75ab7ea 100644 --- a/chrome/browser/ui/views/keyboard_overlay_delegate.h +++ b/chrome/browser/ui/views/keyboard_overlay_delegate.h @@ -5,7 +5,7 @@ #ifndef CHROME_BROWSER_UI_VIEWS_KEYBOARD_OVERLAY_DELEGATE_H_ #define CHROME_BROWSER_UI_VIEWS_KEYBOARD_OVERLAY_DELEGATE_H_ -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "ui/gfx/native_widget_types.h" class KeyboardOverlayDelegate : public HtmlDialogUIDelegate { diff --git a/chrome/browser/ui/views/options/general_page_view.cc b/chrome/browser/ui/views/options/general_page_view.cc index ce3027b..0996b4a 100644 --- a/chrome/browser/ui/views/options/general_page_view.cc +++ b/chrome/browser/ui/views/options/general_page_view.cc @@ -28,7 +28,7 @@ #include "chrome/browser/ui/views/keyword_editor_view.h" #include "chrome/browser/ui/views/options/managed_prefs_banner_view.h" #include "chrome/browser/ui/views/options/options_group_view.h" -#include "chrome/browser/webui/new_tab_ui.h" +#include "chrome/browser/ui/webui/new_tab_ui.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" diff --git a/chrome/browser/ui/views/select_file_dialog.cc b/chrome/browser/ui/views/select_file_dialog.cc index b4a08ef..4ea81b7 100644 --- a/chrome/browser/ui/views/select_file_dialog.cc +++ b/chrome/browser/ui/views/select_file_dialog.cc @@ -19,7 +19,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/views/html_dialog_view.h" -#include "chrome/browser/webui/html_dialog_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" #include "chrome/common/url_constants.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chrome/browser/ui/views/textfield_views.cc b/chrome/browser/ui/views/textfield_views.cc index 8e1268f..b4aabdb 100644 --- a/chrome/browser/ui/views/textfield_views.cc +++ b/chrome/browser/ui/views/textfield_views.cc @@ -7,7 +7,7 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/webui/textfields_ui.h" +#include "chrome/browser/ui/webui/textfields_ui.h" TextfieldViews::TextfieldViews() : DOMView() {} diff --git a/chrome/browser/ui/webui/bookmarks_ui.cc b/chrome/browser/ui/webui/bookmarks_ui.cc new file mode 100644 index 0000000..3cdd0f4 --- /dev/null +++ b/chrome/browser/ui/webui/bookmarks_ui.cc @@ -0,0 +1,60 @@ +// 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/ui/webui/bookmarks_ui.h" + +#include "base/message_loop.h" +#include "base/ref_counted_memory.h" +#include "base/singleton.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/url_constants.h" +#include "grit/theme_resources.h" +#include "ui/base/resource/resource_bundle.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// BookmarksUIHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +BookmarksUIHTMLSource::BookmarksUIHTMLSource() + : DataSource(chrome::kChromeUIBookmarksHost, MessageLoop::current()) { +} + +void BookmarksUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + NOTREACHED() << "We should never get here since the extension should have" + << "been triggered"; + + SendResponse(request_id, NULL); +} + +std::string BookmarksUIHTMLSource::GetMimeType(const std::string& path) const { + NOTREACHED() << "We should never get here since the extension should have" + << "been triggered"; + return "text/html"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// BookmarksUI +// +//////////////////////////////////////////////////////////////////////////////// + +BookmarksUI::BookmarksUI(TabContents* contents) : WebUI(contents) { + BookmarksUIHTMLSource* html_source = new BookmarksUIHTMLSource(); + + // Set up the chrome://bookmarks/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +RefCountedMemory* BookmarksUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_BOOKMARKS_FAVICON); +} diff --git a/chrome/browser/ui/webui/bookmarks_ui.h b/chrome/browser/ui/webui/bookmarks_ui.h new file mode 100644 index 0000000..10c5113 --- /dev/null +++ b/chrome/browser/ui/webui/bookmarks_ui.h @@ -0,0 +1,45 @@ +// 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 CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "content/browser/webui/web_ui.h" + +class GURL; +class RefCountedMemory; + +// This class provides the source for chrome://bookmarks/ +class BookmarksUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + BookmarksUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string& path) const; + + private: + DISALLOW_COPY_AND_ASSIGN(BookmarksUIHTMLSource); +}; + +// This class is used to hook up chrome://bookmarks/ which in turn gets +// overridden by an extension. +class BookmarksUI : public WebUI { + public: + explicit BookmarksUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(BookmarksUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_BOOKMARKS_UI_H_ diff --git a/chrome/browser/ui/webui/bookmarks_ui_uitest.cc b/chrome/browser/ui/webui/bookmarks_ui_uitest.cc new file mode 100644 index 0000000..04c37f6 --- /dev/null +++ b/chrome/browser/ui/webui/bookmarks_ui_uitest.cc @@ -0,0 +1,154 @@ +// 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/test/ui/ui_test.h" + +#include "base/test/test_timeouts.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/automation/window_proxy.h" + +class BookmarksUITest : public UITest { + public: + BookmarksUITest() { + dom_automation_enabled_ = true; + } + + bool WaitForBookmarksUI(TabProxy* tab) { + return WaitUntilJavaScriptCondition(tab, L"", + L"domAutomationController.send(" + L" location.protocol == 'chrome-extension:' && " + L" document.readyState == 'complete')", + TestTimeouts::huge_test_timeout_ms()); + } + + scoped_refptr<TabProxy> GetBookmarksUITab() { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + EXPECT_TRUE(browser.get()); + if (!browser.get()) + return NULL; + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + EXPECT_TRUE(tab.get()); + if (!tab.get()) + return NULL; + bool success = tab->NavigateToURL(GURL(chrome::kChromeUIBookmarksURL)); + EXPECT_TRUE(success); + if (!success) + return NULL; + success = WaitForBookmarksUI(tab); + EXPECT_TRUE(success); + if (!success) + return NULL; + return tab; + } + + void AssertIsBookmarksPage(TabProxy* tab) { + // tab->GetCurrentURL is not up to date. + GURL url; + std::wstring out; + ASSERT_TRUE(tab->ExecuteAndExtractString(L"", + L"domAutomationController.send(location.protocol)", &out)); + ASSERT_EQ(L"chrome-extension:", out); + ASSERT_TRUE(tab->ExecuteAndExtractString(L"", + L"domAutomationController.send(location.pathname)", &out)); + ASSERT_EQ(L"/main.html", out); + } +}; + +// http://code.google.com/p/chromium/issues/detail?id=39532 +TEST_F(BookmarksUITest, FLAKY_ShouldRedirectToExtension) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + int tab_count = -1; + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); + + // Navigate to chrome + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + ASSERT_TRUE(tab->NavigateToURL(GURL(chrome::kChromeUIBookmarksURL))); + + // At this point the URL is chrome://bookmarks. We need to wait for the + // redirect to happen. + ASSERT_TRUE(WaitForBookmarksUI(tab)); + + AssertIsBookmarksPage(tab); +} + +TEST_F(BookmarksUITest, CommandOpensBookmarksTab) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + int tab_count = -1; + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); + + // Bring up the bookmarks manager tab. + ASSERT_TRUE(browser->RunCommand(IDC_SHOW_BOOKMARK_MANAGER)); + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(2, tab_count); + + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + ASSERT_TRUE(WaitForBookmarksUI(tab)); + + AssertIsBookmarksPage(tab); +} + +TEST_F(BookmarksUITest, CommandAgainGoesBackToBookmarksTab) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + int tab_count = -1; + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); + + // Bring up the bookmarks manager tab. + ASSERT_TRUE(browser->RunCommand(IDC_SHOW_BOOKMARK_MANAGER)); + ASSERT_TRUE(browser->WaitForTabToBecomeActive( + 1, TestTimeouts::action_max_timeout_ms())); + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(2, tab_count); + + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + ASSERT_TRUE(tab.get()); + ASSERT_TRUE(WaitForBookmarksUI(tab)); + AssertIsBookmarksPage(tab); + + // Switch to first tab and run command again. + ASSERT_TRUE(browser->ActivateTab(0)); + ASSERT_TRUE(browser->WaitForTabToBecomeActive( + 0, TestTimeouts::action_max_timeout_ms())); + ASSERT_TRUE(browser->RunCommand(IDC_SHOW_BOOKMARK_MANAGER)); + + // Ensure the bookmarks ui tab is active. + ASSERT_TRUE(browser->WaitForTabToBecomeActive( + 1, TestTimeouts::action_max_timeout_ms())); + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(2, tab_count); +} + +TEST_F(BookmarksUITest, TwoCommandsOneTab) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + int tab_count = -1; + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); + + ASSERT_TRUE(browser->RunCommand(IDC_SHOW_BOOKMARK_MANAGER)); + ASSERT_TRUE(browser->RunCommand(IDC_SHOW_BOOKMARK_MANAGER)); + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(2, tab_count); +} + +TEST_F(BookmarksUITest, BookmarksLoaded) { + scoped_refptr<TabProxy> tab = GetBookmarksUITab(); + ASSERT_TRUE(tab.get()); +} diff --git a/chrome/browser/ui/webui/bug_report_ui.cc b/chrome/browser/ui/webui/bug_report_ui.cc new file mode 100644 index 0000000..1b99d6a --- /dev/null +++ b/chrome/browser/ui/webui/bug_report_ui.cc @@ -0,0 +1,745 @@ +// 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/ui/webui/bug_report_ui.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/singleton.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "base/weak_ptr.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/bug_report_data.h" +#include "chrome/browser/bug_report_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/window_snapshot/window_snapshot.h" +#include "chrome/browser/webui/screenshot_source.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/rect.h" + +#if defined(OS_CHROMEOS) +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/synchronization/waitable_event.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/syslogs_library.h" +#include "chrome/browser/chromeos/login/user_manager.h" +#endif + +namespace { + +const char kScreenshotBaseUrl[] = "chrome://screenshots/"; +const char kCurrentScreenshotUrl[] = "chrome://screenshots/current"; +#if defined(OS_CHROMEOS) +const char kSavedScreenshotsUrl[] = "chrome://screenshots/saved/"; + +const char kScreenshotPattern[] = "*.png"; +const char kScreenshotsRelativePath[] = "/Screenshots"; + +const size_t kMaxSavedScreenshots = 2; +#endif + +#if defined(OS_CHROMEOS) + +void GetSavedScreenshots(std::vector<std::string>* saved_screenshots, + base::WaitableEvent* done) { + saved_screenshots->clear(); + + FilePath fileshelf_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &fileshelf_path)) { + done->Signal(); + return; + } + + // TODO(rkc): Change this to use FilePath.Append() once the cros + // issue with it is fixed + FilePath screenshots_path(fileshelf_path.value() + + std::string(kScreenshotsRelativePath)); + file_util::FileEnumerator screenshots(screenshots_path, false, + file_util::FileEnumerator::FILES, + std::string(kScreenshotPattern)); + FilePath screenshot = screenshots.Next(); + while (!screenshot.empty()) { + saved_screenshots->push_back(std::string(kSavedScreenshotsUrl) + + screenshot.BaseName().value()); + if (saved_screenshots->size() >= kMaxSavedScreenshots) + break; + + screenshot = screenshots.Next(); + } + done->Signal(); +} + +// This fuction posts a task to the file thread to create/list all the current +// and saved screenshots. +void GetScreenshotUrls(std::vector<std::string>* saved_screenshots) { + base::WaitableEvent done(true, false); + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableFunction(&GetSavedScreenshots, + saved_screenshots, &done)); + done.Wait(); +} + +std::string GetUserEmail() { + chromeos::UserManager* manager = chromeos::UserManager::Get(); + if (!manager) + return std::string(); + else + return manager->logged_in_user().email(); +} +#endif + +// Returns the index of the feedback tab if already open, -1 otherwise +int GetIndexOfFeedbackTab(Browser* browser) { + GURL bug_report_url(chrome::kChromeUIBugReportURL); + for (int i = 0; i < browser->tab_count(); ++i) { + TabContents* tab = browser->GetTabContentsAt(i); + if (tab && tab->GetURL().GetWithEmptyPath() == bug_report_url) + return i; + } + + return -1; +} + +} // namespace + + +namespace browser { + +// TODO(rkc): Eventually find a better way to do this +std::vector<unsigned char>* last_screenshot_png = 0; +gfx::Rect screen_size; + +void RefreshLastScreenshot(Browser* browser) { + if (last_screenshot_png) + last_screenshot_png->clear(); + else + last_screenshot_png = new std::vector<unsigned char>; + + gfx::NativeWindow native_window = browser->window()->GetNativeHandle(); + screen_size = browser::GrabWindowSnapshot(native_window, last_screenshot_png); +} + +void ShowHtmlBugReportView(Browser* browser) { + // First check if we're already open (we cannot depend on ShowSingletonTab + // for this functionality since we need to make *sure* we never get + // instantiated again while we are open - with singleton tabs, that can + // happen) + int feedback_tab_index = GetIndexOfFeedbackTab(browser); + if (feedback_tab_index >=0) { + // Do not refresh screenshot, do not create a new tab + browser->SelectTabContentsAt(feedback_tab_index, true); + return; + } + + RefreshLastScreenshot(browser); + std::string bug_report_url = std::string(chrome::kChromeUIBugReportURL) + + "#" + base::IntToString(browser->selected_index()); + browser->ShowSingletonTab(GURL(bug_report_url), false); +} + +} // namespace browser + + +class BugReportUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + explicit BugReportUIHTMLSource(base::StringPiece html); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + base::StringPiece bug_report_html_; + ~BugReportUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(BugReportUIHTMLSource); +}; + +// The handler for Javascript messages related to the "bug report" dialog +class BugReportHandler : public WebUIMessageHandler, + public base::SupportsWeakPtr<BugReportHandler> { + public: + explicit BugReportHandler(TabContents* tab); + virtual ~BugReportHandler(); + + // Init work after Attach. + base::StringPiece Init(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + private: + void HandleGetDialogDefaults(const ListValue* args); + void HandleRefreshCurrentScreenshot(const ListValue* args); +#if defined(OS_CHROMEOS) + void HandleRefreshSavedScreenshots(const ListValue* args); +#endif + void HandleSendReport(const ListValue* args); + void HandleCancel(const ListValue* args); + void HandleOpenSystemTab(const ListValue* args); + + void SetupScreenshotsSource(); + void ClobberScreenshotsSource(); + + void CancelFeedbackCollection(); + void CloseFeedbackTab(); + + TabContents* tab_; + ScreenshotSource* screenshot_source_; + + BugReportData* bug_report_; + std::string target_tab_url_; +#if defined(OS_CHROMEOS) + // Variables to track SyslogsLibrary::RequestSyslogs callback. + chromeos::SyslogsLibrary::Handle syslogs_handle_; + CancelableRequestConsumer syslogs_consumer_; +#endif + + DISALLOW_COPY_AND_ASSIGN(BugReportHandler); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// BugReportHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +BugReportUIHTMLSource::BugReportUIHTMLSource(base::StringPiece html) + : DataSource(chrome::kChromeUIBugReportHost, MessageLoop::current()) { + bug_report_html_ = html; +} + +void BugReportUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString(std::string("title"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_TITLE)); + localized_strings.SetString(std::string("page-title"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_REPORT_PAGE_TITLE)); + localized_strings.SetString(std::string("issue-with"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_ISSUE_WITH)); + localized_strings.SetString(std::string("page-url"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_REPORT_URL_LABEL)); + localized_strings.SetString(std::string("description"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_DESCRIPTION_LABEL)); + localized_strings.SetString(std::string("current-screenshot"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SCREENSHOT_LABEL)); + localized_strings.SetString(std::string("saved-screenshot"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SAVED_SCREENSHOT_LABEL)); +#if defined(OS_CHROMEOS) + localized_strings.SetString(std::string("user-email"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_USER_EMAIL_LABEL)); + localized_strings.SetString(std::string("sysinfo"), + l10n_util::GetStringUTF8( + IDS_BUGREPORT_INCLUDE_SYSTEM_INFORMATION_CHKBOX)); + + localized_strings.SetString(std::string("currentscreenshots"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_CURRENT_SCREENSHOTS)); + localized_strings.SetString(std::string("savedscreenshots"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SAVED_SCREENSHOTS)); + + localized_strings.SetString(std::string("choose-different-screenshot"), + l10n_util::GetStringUTF8( + IDS_BUGREPORT_CHOOSE_DIFFERENT_SCREENSHOT)); + localized_strings.SetString(std::string("choose-original-screenshot"), + l10n_util::GetStringUTF8( + IDS_BUGREPORT_CHOOSE_ORIGINAL_SCREENSHOT)); +#else + localized_strings.SetString(std::string("currentscreenshots"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_INCLUDE_NEW_SCREEN_IMAGE)); +#endif + localized_strings.SetString(std::string("noscreenshot"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_INCLUDE_NO_SCREENSHOT)); + + localized_strings.SetString(std::string("send-report"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SEND_REPORT)); + localized_strings.SetString(std::string("cancel"), + l10n_util::GetStringUTF8(IDS_CANCEL)); + + // Option strings for the "issue with" drop-down. + localized_strings.SetString(std::string("issue-choose"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_CHOOSE_ISSUE)); + + localized_strings.SetString(std::string("no-issue-selected"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_ISSUE_SELECTED)); + + localized_strings.SetString(std::string("no-description"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_DESCRIPTION)); + + localized_strings.SetString(std::string("no-saved-screenshots"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_NO_SAVED_SCREENSHOTS_HELP)); + + localized_strings.SetString(std::string("privacy-note"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PRIVACY_NOTE)); + + // TODO(rkc): Find some way to ensure this order of dropdowns is in sync + // with the order in the userfeedback ChromeData proto buffer +#if defined(OS_CHROMEOS) + // Dropdown for ChromeOS: + // + // Connectivity + // Sync + // Crash + // Page Formatting + // Extensions or Apps + // Standby or Resume + // Phishing Page + // General Feedback/Other + + localized_strings.SetString(std::string("issue-connectivity"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_CONNECTIVITY)); + localized_strings.SetString(std::string("issue-sync"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SYNC)); + localized_strings.SetString(std::string("issue-crashes"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_CRASHES)); + localized_strings.SetString(std::string("issue-page-formatting"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_FORMATTING)); + localized_strings.SetString(std::string("issue-extensions"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_EXTENSIONS)); + localized_strings.SetString(std::string("issue-standby"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_STANDBY_RESUME)); + localized_strings.SetString(std::string("issue-phishing"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PHISHING_PAGE)); + localized_strings.SetString(std::string("issue-other"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_GENERAL)); +#else + // Dropdown for Chrome: + // + // Page formatting or layout + // Pages not loading + // Plug-ins (e.g. Adobe Flash Player, Quicktime, etc) + // Tabs or windows + // Synced preferences + // Crashes + // Extensions or apps + // Phishing + // Other + + localized_strings.SetString(std::string("issue-page-formatting"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_FORMATTING)); + localized_strings.SetString(std::string("issue-page-load"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PAGE_LOAD)); + localized_strings.SetString(std::string("issue-plugins"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PLUGINS)); + localized_strings.SetString(std::string("issue-tabs"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_TABS)); + localized_strings.SetString(std::string("issue-sync"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_SYNC)); + localized_strings.SetString(std::string("issue-crashes"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_CRASHES)); + localized_strings.SetString(std::string("issue-extensions"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_EXTENSIONS)); + localized_strings.SetString(std::string("issue-phishing"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_PHISHING_PAGE)); + localized_strings.SetString(std::string("issue-other"), + l10n_util::GetStringUTF8(IDS_BUGREPORT_OTHER)); +#endif + + SetFontAndTextDirection(&localized_strings); + + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + bug_report_html_, &localized_strings); + + 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); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// BugReportData +// +//////////////////////////////////////////////////////////////////////////////// +void BugReportData::SendReport() { +#if defined(OS_CHROMEOS) + // In case we already got the syslogs and sent the report, leave + if (sent_report_) return; + // Set send_report_ so that no one else processes SendReport + sent_report_ = true; +#endif + + int image_data_size = image_.size(); + char* image_data = image_data_size ? + reinterpret_cast<char*>(&(image_.front())) : NULL; + BugReportUtil::SendReport(profile_ + , problem_type_ + , page_url_ + , description_ + , image_data + , image_data_size + , browser::screen_size.width() + , browser::screen_size.height() +#if defined(OS_CHROMEOS) + , user_email_ + , zip_content_ ? zip_content_->c_str() : NULL + , zip_content_ ? zip_content_->length() : 0 + , send_sys_info_ ? sys_info_ : NULL +#endif + ); + +#if defined(OS_CHROMEOS) + if (sys_info_) { + delete sys_info_; + sys_info_ = NULL; + } + if (zip_content_) { + delete zip_content_; + zip_content_ = NULL; + } +#endif + + // Once the report has been sent, this object has no purpose in life, delete + // ourselves. + delete this; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// BugReportHandler +// +//////////////////////////////////////////////////////////////////////////////// +BugReportHandler::BugReportHandler(TabContents* tab) + : tab_(tab), + screenshot_source_(NULL), + bug_report_(NULL) +#if defined(OS_CHROMEOS) + , syslogs_handle_(0) +#endif +{ +} + +BugReportHandler::~BugReportHandler() { + // Just in case we didn't send off bug_report_ to SendReport + if (bug_report_) { + // If we're deleting the report object, cancel feedback collection first + CancelFeedbackCollection(); + delete bug_report_; + } +} + +void BugReportHandler::ClobberScreenshotsSource() { + // Re-create our screenshots data source (this clobbers the last source) + // setting the screenshot to NULL, effectively disabling the source + // TODO(rkc): Once there is a method to 'remove' a source, change this code + tab_->profile()->GetChromeURLDataManager()->AddDataSource( + new ScreenshotSource(NULL)); + + // clobber last screenshot + if (browser::last_screenshot_png) + browser::last_screenshot_png->clear(); +} + +void BugReportHandler::SetupScreenshotsSource() { + // If we don't already have a screenshot source object created, create one. + if (!screenshot_source_) + screenshot_source_ = new ScreenshotSource( + browser::last_screenshot_png); + + // Add the source to the data manager. + tab_->profile()->GetChromeURLDataManager()->AddDataSource(screenshot_source_); +} + +WebUIMessageHandler* BugReportHandler::Attach(WebUI* web_ui) { + SetupScreenshotsSource(); + return WebUIMessageHandler::Attach(web_ui); +} + +base::StringPiece BugReportHandler::Init() { + std::string page_url; + if (tab_->controller().GetActiveEntry()) { + page_url = tab_->controller().GetActiveEntry()->url().spec(); + } + + std::string params = page_url.substr(strlen(chrome::kChromeUIBugReportURL)); + // Erase the # - the first character. + if (params.length()) + params.erase(params.begin(), params.begin() + 1); + + int index = 0; + if (!base::StringToInt(params, &index)) { + return base::StringPiece( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_BUGREPORT_HTML_INVALID)); + } + + Browser* browser = BrowserList::GetLastActive(); + // Sanity checks. + if (((index == 0) && (params != "0")) || !browser || + index >= browser->tab_count()) { + return base::StringPiece( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_BUGREPORT_HTML_INVALID)); + } + + TabContents* target_tab = browser->GetTabContentsAt(index); + if (target_tab) { + target_tab_url_ = target_tab->GetURL().spec(); + } + + // Setup the screenshot source after we've verified input is legit. + SetupScreenshotsSource(); + + return base::StringPiece( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_BUGREPORT_HTML)); +} + +void BugReportHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("getDialogDefaults", + NewCallback(this, &BugReportHandler::HandleGetDialogDefaults)); + web_ui_->RegisterMessageCallback("refreshCurrentScreenshot", + NewCallback(this, &BugReportHandler::HandleRefreshCurrentScreenshot)); +#if defined(OS_CHROMEOS) + web_ui_->RegisterMessageCallback("refreshSavedScreenshots", + NewCallback(this, &BugReportHandler::HandleRefreshSavedScreenshots)); +#endif + web_ui_->RegisterMessageCallback("sendReport", + NewCallback(this, &BugReportHandler::HandleSendReport)); + web_ui_->RegisterMessageCallback("cancel", + NewCallback(this, &BugReportHandler::HandleCancel)); + web_ui_->RegisterMessageCallback("openSystemTab", + NewCallback(this, &BugReportHandler::HandleOpenSystemTab)); +} + +void BugReportHandler::HandleGetDialogDefaults(const ListValue*) { + bug_report_ = new BugReportData(); + + // send back values which the dialog js needs initially + ListValue dialog_defaults; + + // 0: current url + if (target_tab_url_.length()) + dialog_defaults.Append(new StringValue(target_tab_url_)); + else + dialog_defaults.Append(new StringValue("")); + +#if defined(OS_CHROMEOS) + // 1: about:system + dialog_defaults.Append(new StringValue(chrome::kChromeUISystemInfoURL)); + // Trigger the request for system information here. + chromeos::SyslogsLibrary* syslogs_lib = + chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); + if (syslogs_lib) { + syslogs_handle_ = syslogs_lib->RequestSyslogs( + true, true, &syslogs_consumer_, + NewCallback(bug_report_, &BugReportData::SyslogsComplete)); + } + // 2: user e-mail + dialog_defaults.Append(new StringValue(GetUserEmail())); +#endif + + web_ui_->CallJavascriptFunction(L"setupDialogDefaults", dialog_defaults); +} + +void BugReportHandler::HandleRefreshCurrentScreenshot(const ListValue*) { + std::string current_screenshot(kCurrentScreenshotUrl); + StringValue screenshot(current_screenshot); + web_ui_->CallJavascriptFunction(L"setupCurrentScreenshot", screenshot); +} + + +#if defined(OS_CHROMEOS) +void BugReportHandler::HandleRefreshSavedScreenshots(const ListValue*) { + std::vector<std::string> saved_screenshots; + GetScreenshotUrls(&saved_screenshots); + + ListValue screenshots_list; + for (size_t i = 0; i < saved_screenshots.size(); ++i) + screenshots_list.Append(new StringValue(saved_screenshots[i])); + web_ui_->CallJavascriptFunction(L"setupSavedScreenshots", screenshots_list); +} +#endif + + +void BugReportHandler::HandleSendReport(const ListValue* list_value) { + if (!bug_report_) { + LOG(ERROR) << "Bug report hasn't been intialized yet."; + return; + } + + ListValue::const_iterator i = list_value->begin(); + if (i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #0 - Problem type. + int problem_type; + std::string problem_type_str; + (*i)->GetAsString(&problem_type_str); + if (!base::StringToInt(problem_type_str, &problem_type)) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + if (++i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #1 - Page url. + std::string page_url; + (*i)->GetAsString(&page_url); + if (++i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #2 - Description. + std::string description; + (*i)->GetAsString(&description); + if (++i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #3 - Screenshot to send. + std::string screenshot_path; + (*i)->GetAsString(&screenshot_path); + screenshot_path.erase(0, strlen(kScreenshotBaseUrl)); + + // Get the image to send in the report. + std::vector<unsigned char> image; + if (screenshot_path.size() > 0) { + image = screenshot_source_->GetScreenshot(screenshot_path); + } + +#if defined(OS_CHROMEOS) + if (++i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #4 - User e-mail + std::string user_email; + (*i)->GetAsString(&user_email); + if (++i == list_value->end()) { + LOG(ERROR) << "Incorrect data passed to sendReport."; + return; + } + + // #5 - System info checkbox. + std::string sys_info_checkbox; + (*i)->GetAsString(&sys_info_checkbox); + bool send_sys_info = (sys_info_checkbox == "true"); + + // If we aren't sending the sys_info, cancel the gathering of the syslogs. + if (!send_sys_info) + CancelFeedbackCollection(); +#endif + + // Update the data in bug_report_ so it can be sent + bug_report_->UpdateData(web_ui_->GetProfile() + , target_tab_url_ + , problem_type + , page_url + , description + , image +#if defined(OS_CHROMEOS) + , user_email + , send_sys_info + , false // sent_report +#endif + ); + +#if defined(OS_CHROMEOS) + // If we don't require sys_info, or we have it, or we never requested it + // (because libcros failed to load), then send the report now. + // Otherwise, the report will get sent when we receive sys_info. + if (!send_sys_info || bug_report_->sys_info() != NULL || + syslogs_handle_ == 0) { + bug_report_->SendReport(); + } +#else + bug_report_->SendReport(); +#endif + // Lose the pointer to the BugReportData object; the object will delete itself + // from SendReport, whether we called it, or will be called by the log + // completion routine. + bug_report_ = NULL; + + // Whether we sent the report, or if it will be sent by the Syslogs complete + // function, close our feedback tab anyway, we have no more use for it. + CloseFeedbackTab(); +} + +void BugReportHandler::HandleCancel(const ListValue*) { + CloseFeedbackTab(); +} + +void BugReportHandler::HandleOpenSystemTab(const ListValue* args) { +#if defined(OS_CHROMEOS) + BrowserList::GetLastActive()->OpenSystemTabAndActivate(); +#endif +} + +void BugReportHandler::CancelFeedbackCollection() { +#if defined(OS_CHROMEOS) + if (syslogs_handle_ != 0) { + chromeos::SyslogsLibrary* syslogs_lib = + chromeos::CrosLibrary::Get()->GetSyslogsLibrary(); + if (syslogs_lib) + syslogs_lib->CancelRequest(syslogs_handle_); + } +#endif +} + +void BugReportHandler::CloseFeedbackTab() { + Browser* browser = BrowserList::GetLastActive(); + if (browser) { + browser->CloseTabContents(tab_); + } else { + LOG(FATAL) << "Failed to get last active browser."; + } + ClobberScreenshotsSource(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// BugReportUI +// +//////////////////////////////////////////////////////////////////////////////// +BugReportUI::BugReportUI(TabContents* tab) : HtmlDialogUI(tab) { + BugReportHandler* handler = new BugReportHandler(tab); + AddMessageHandler((handler)->Attach(this)); + + // The handler's init will specify which html + // resource we'll display to the user + BugReportUIHTMLSource* html_source = + new BugReportUIHTMLSource(handler->Init()); + // Set up the chrome://bugreport/ source. + tab->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} diff --git a/chrome/browser/ui/webui/bug_report_ui.h b/chrome/browser/ui/webui/bug_report_ui.h new file mode 100644 index 0000000..e4814d5 --- /dev/null +++ b/chrome/browser/ui/webui/bug_report_ui.h @@ -0,0 +1,31 @@ +// 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 CHROME_BROWSER_UI_WEBUI_BUG_REPORT_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_BUG_REPORT_UI_H_ + +#include "chrome/browser/ui/views/window.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" + +namespace gfx { +class Rect; +} // namespace gfx + +class Browser; +class NSWindow; +class TabContents; + +namespace browser { +void ShowHtmlBugReportView(Browser* browser); +} // namespace browser + +class BugReportUI : public HtmlDialogUI { + public: + explicit BugReportUI(TabContents* contents); + private: + + DISALLOW_COPY_AND_ASSIGN(BugReportUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_BUG_REPORT_UI_H_ diff --git a/chrome/browser/ui/webui/conflicts_ui.cc b/chrome/browser/ui/webui/conflicts_ui.cc new file mode 100644 index 0000000..5ff5a65 --- /dev/null +++ b/chrome/browser/ui/webui/conflicts_ui.cc @@ -0,0 +1,215 @@ +// 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/ui/webui/conflicts_ui.h" + +#if defined(OS_WIN) + +#include <string> + +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/enumerate_modules_model_win.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// +// ConflictsUIHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +class ConflictsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + ConflictsUIHTMLSource() + : DataSource(chrome::kChromeUIConflictsHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ConflictsUIHTMLSource); +}; + +void ConflictsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString("modulesLongTitle", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_PAGE_TITLE_LONG)); + localized_strings.SetString("modulesBlurb", + l10n_util::GetStringUTF16(IDS_CONFLICTS_EXPLANATION_TEXT)); + localized_strings.SetString("moduleSuspectedBad", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_SUSPECTED)); + localized_strings.SetString("moduleConfirmedBad", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_WARNING_CONFIRMED)); + localized_strings.SetString("helpCenterLink", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HELP_CENTER_LINK)); + localized_strings.SetString("investigatingText", + l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_INVESTIGATING)); + localized_strings.SetString("modulesNoneLoaded", + l10n_util::GetStringUTF16(IDS_CONFLICTS_NO_MODULES_LOADED)); + localized_strings.SetString("headerSoftware", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SOFTWARE)); + localized_strings.SetString("headerSignedBy", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_SIGNED_BY)); + localized_strings.SetString("headerLocation", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_LOCATION)); + localized_strings.SetString("headerVersion", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_VERSION)); + localized_strings.SetString("headerHelpTip", + l10n_util::GetStringUTF16(IDS_CONFLICTS_HEADER_HELP_TIP)); + + ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece flags_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_ABOUT_CONFLICTS_HTML)); + std::string full_html(flags_html.data(), flags_html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// ConflictsDOMHandler +// +//////////////////////////////////////////////////////////////////////////////// + +// The handler for JavaScript messages for the about:flags page. +class ConflictsDOMHandler : public WebUIMessageHandler, + public NotificationObserver { + public: + ConflictsDOMHandler() {} + virtual ~ConflictsDOMHandler() {} + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for the "requestModuleList" message. + void HandleRequestModuleList(const ListValue* args); + + private: + void SendModuleList(); + + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(ConflictsDOMHandler); +}; + +void ConflictsDOMHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("requestModuleList", + NewCallback(this, &ConflictsDOMHandler::HandleRequestModuleList)); +} + +void ConflictsDOMHandler::HandleRequestModuleList(const ListValue* args) { + // This request is handled asynchronously. See Observe for when we reply back. + registrar_.Add(this, NotificationType::MODULE_LIST_ENUMERATED, + NotificationService::AllSources()); + EnumerateModulesModel::GetInstance()->ScanNow(); +} + +void ConflictsDOMHandler::SendModuleList() { + EnumerateModulesModel* loaded_modules = EnumerateModulesModel::GetInstance(); + ListValue* list = loaded_modules->GetModuleList(); + DictionaryValue results; + results.Set("moduleList", list); + + // Add the section title and the total count for bad modules found. + int confirmed_bad = loaded_modules->confirmed_bad_modules_detected(); + int suspected_bad = loaded_modules->suspected_bad_modules_detected(); + string16 table_title; + if (!confirmed_bad && !suspected_bad) { + table_title += l10n_util::GetStringFUTF16( + IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_ONE, + base::IntToString16(list->GetSize())); + } else { + table_title += l10n_util::GetStringFUTF16( + IDS_CONFLICTS_CHECK_PAGE_TABLE_TITLE_SUFFIX_TWO, + base::IntToString16(list->GetSize()), + base::IntToString16(confirmed_bad), + base::IntToString16(suspected_bad)); + } + results.SetString("modulesTableTitle", table_title); + + web_ui_->CallJavascriptFunction(L"returnModuleList", results); +} + +void ConflictsDOMHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::MODULE_LIST_ENUMERATED: + SendModuleList(); + registrar_.RemoveAll(); + break; + default: + NOTREACHED(); + break; + } +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// ConflictsUI +// +/////////////////////////////////////////////////////////////////////////////// + +ConflictsUI::ConflictsUI(TabContents* contents) : WebUI(contents) { + UserMetrics::RecordAction( + UserMetricsAction("ViewAboutConflicts"), contents->profile()); + + AddMessageHandler((new ConflictsDOMHandler())->Attach(this)); + + ConflictsUIHTMLSource* html_source = new ConflictsUIHTMLSource(); + + // Set up the about:conflicts source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +RefCountedMemory* ConflictsUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_CONFLICT_FAVICON); +} + +#endif diff --git a/chrome/browser/ui/webui/conflicts_ui.h b/chrome/browser/ui/webui/conflicts_ui.h new file mode 100644 index 0000000..3e05ccf --- /dev/null +++ b/chrome/browser/ui/webui/conflicts_ui.h @@ -0,0 +1,28 @@ +// 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 CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +#if defined(OS_WIN) + +class RefCountedMemory; + +// The Web UI handler for about:conflicts. +class ConflictsUI : public WebUI { + public: + explicit ConflictsUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(ConflictsUI); +}; + +#endif + +#endif // CHROME_BROWSER_UI_WEBUI_CONFLICTS_UI_H_ diff --git a/chrome/browser/ui/webui/constrained_html_ui.cc b/chrome/browser/ui/webui/constrained_html_ui.cc new file mode 100644 index 0000000..68dd024 --- /dev/null +++ b/chrome/browser/ui/webui/constrained_html_ui.cc @@ -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. + +#include "chrome/browser/ui/webui/constrained_html_ui.h" + +#include "base/lazy_instance.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" +#include "chrome/common/bindings_policy.h" +#include "content/browser/webui/web_ui_util.h" + +static base::LazyInstance<PropertyAccessor<ConstrainedHtmlUIDelegate*> > + g_constrained_html_ui_property_accessor(base::LINKER_INITIALIZED); + +ConstrainedHtmlUI::ConstrainedHtmlUI(TabContents* contents) + : WebUI(contents) { +} + +ConstrainedHtmlUI::~ConstrainedHtmlUI() { +} + +void ConstrainedHtmlUI::RenderViewCreated( + RenderViewHost* render_view_host) { + ConstrainedHtmlUIDelegate* delegate = GetConstrainedDelegate(); + if (!delegate) + return; + + HtmlDialogUIDelegate* dialog_delegate = delegate->GetHtmlDialogUIDelegate(); + std::vector<WebUIMessageHandler*> handlers; + dialog_delegate->GetWebUIMessageHandlers(&handlers); + render_view_host->SetWebUIProperty("dialogArguments", + dialog_delegate->GetDialogArgs()); + for (std::vector<WebUIMessageHandler*>::iterator it = handlers.begin(); + it != handlers.end(); ++it) { + (*it)->Attach(this); + AddMessageHandler(*it); + } + + // Add a "DialogClose" callback which matches HTMLDialogUI behavior. + RegisterMessageCallback("DialogClose", + NewCallback(this, &ConstrainedHtmlUI::OnDialogClose)); +} + +void ConstrainedHtmlUI::OnDialogClose(const ListValue* args) { + ConstrainedHtmlUIDelegate* delegate = GetConstrainedDelegate(); + if (!delegate) + return; + + delegate->GetHtmlDialogUIDelegate()->OnDialogClosed( + web_ui_util::GetJsonResponseFromFirstArgumentInList(args)); + delegate->OnDialogClose(); +} + +ConstrainedHtmlUIDelegate* + ConstrainedHtmlUI::GetConstrainedDelegate() { + ConstrainedHtmlUIDelegate** property = + GetPropertyAccessor().GetProperty(tab_contents()->property_bag()); + return property ? *property : NULL; +} + +// static +PropertyAccessor<ConstrainedHtmlUIDelegate*>& + ConstrainedHtmlUI::GetPropertyAccessor() { + return g_constrained_html_ui_property_accessor.Get(); +} diff --git a/chrome/browser/ui/webui/constrained_html_ui.h b/chrome/browser/ui/webui/constrained_html_ui.h new file mode 100644 index 0000000..55da2db --- /dev/null +++ b/chrome/browser/ui/webui/constrained_html_ui.h @@ -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. + +#ifndef CHROME_BROWSER_UI_WEBUI_CONSTRAINED_HTML_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_CONSTRAINED_HTML_UI_H_ +#pragma once + +#include <vector> + +#include "chrome/browser/tab_contents/constrained_window.h" +#include "chrome/common/property_bag.h" +#include "content/browser/webui/web_ui.h" + +class HtmlDialogUIDelegate; +class Profile; +class RenderViewHost; +class TabContents; + +class ConstrainedHtmlUIDelegate { + public: + virtual HtmlDialogUIDelegate* GetHtmlDialogUIDelegate() = 0; + + // Called when the dialog should close. + virtual void OnDialogClose() = 0; +}; + +// ConstrainedHtmlUI is a facility to show HTML WebUI content +// in a tab-modal constrained dialog. It is implemented as an adapter +// between an HtmlDialogUI object and a ConstrainedWindow object. +// +// Since ConstrainedWindow requires platform-specific delegate +// implementations, this class is just a factory stub. +class ConstrainedHtmlUI : public WebUI { + public: + explicit ConstrainedHtmlUI(TabContents* contents); + virtual ~ConstrainedHtmlUI(); + + virtual void RenderViewCreated(RenderViewHost* render_view_host); + + // Create a constrained HTML dialog. The actual object that gets created + // is a ConstrainedHtmlUIDelegate, which later triggers construction of a + // ConstrainedHtmlUI object. + static void CreateConstrainedHtmlDialog(Profile* profile, + HtmlDialogUIDelegate* delegate, + TabContents* overshadowed); + + // Returns a property accessor that can be used to set the + // ConstrainedHtmlUIDelegate property on a TabContents. + static PropertyAccessor<ConstrainedHtmlUIDelegate*>& + GetPropertyAccessor(); + + private: + // Returns the TabContents' PropertyBag's ConstrainedHtmlUIDelegate. + // Returns NULL if that property is not set. + ConstrainedHtmlUIDelegate* GetConstrainedDelegate(); + + // JS Message Handler + void OnDialogClose(const ListValue* args); + + DISALLOW_COPY_AND_ASSIGN(ConstrainedHtmlUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_CONSTRAINED_HTML_UI_H_ diff --git a/chrome/browser/ui/webui/constrained_html_ui_browsertest.cc b/chrome/browser/ui/webui/constrained_html_ui_browsertest.cc new file mode 100644 index 0000000..5b40613 --- /dev/null +++ b/chrome/browser/ui/webui/constrained_html_ui_browsertest.cc @@ -0,0 +1,74 @@ +// 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/test/ui/ui_test.h" + +#include "base/file_path.h" +#include "base/message_loop.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/webui/constrained_html_ui.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Eq; + +namespace { + +class TestHtmlDialogUIDelegate : public HtmlDialogUIDelegate { + public: + TestHtmlDialogUIDelegate() {} + virtual ~TestHtmlDialogUIDelegate() {} + + // HTMLDialogUIDelegate implementation: + virtual bool IsDialogModal() const { + return true; + } + virtual std::wstring GetDialogTitle() const { + return std::wstring(L"Test"); + } + virtual GURL GetDialogContentURL() const { + return GURL(chrome::kChromeUIConstrainedHTMLTestURL); + } + virtual void GetWebUIMessageHandlers( + std::vector<WebUIMessageHandler*>* handlers) const {} + virtual void GetDialogSize(gfx::Size* size) const { + size->set_width(400); + size->set_height(400); + } + virtual std::string GetDialogArgs() const { + return std::string(); + } + virtual void OnDialogClosed(const std::string& json_retval) { } + virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { + if (out_close_dialog) + *out_close_dialog = true; + } + virtual bool ShouldShowDialogTitle() const { return true; } +}; + +} // namespace + +class ConstrainedHtmlDialogBrowserTest : public InProcessBrowserTest { + public: + ConstrainedHtmlDialogBrowserTest() {} +}; + +// Tests that opening/closing the constrained window won't crash it. +IN_PROC_BROWSER_TEST_F(ConstrainedHtmlDialogBrowserTest, BasicTest) { + // The delegate deletes itself. + HtmlDialogUIDelegate* delegate = new TestHtmlDialogUIDelegate(); + TabContents* tab_contents = browser()->GetSelectedTabContents(); + ASSERT_TRUE(tab_contents != NULL); + + ConstrainedHtmlUI::CreateConstrainedHtmlDialog(browser()->profile(), + delegate, + tab_contents); + + ASSERT_EQ(1U, tab_contents->constrained_window_count()); +} diff --git a/chrome/browser/ui/webui/crashes_ui.cc b/chrome/browser/ui/webui/crashes_ui.cc new file mode 100644 index 0000000..d4bb435 --- /dev/null +++ b/chrome/browser/ui/webui/crashes_ui.cc @@ -0,0 +1,212 @@ +// 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/ui/webui/crashes_ui.h" + +#include "base/i18n/time_formatting.h" +#include "base/ref_counted_memory.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/crash_upload_list.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// +// CrashesUIHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +class CrashesUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + CrashesUIHTMLSource() + : DataSource(chrome::kChromeUICrashesHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~CrashesUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(CrashesUIHTMLSource); +}; + +void CrashesUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString("crashesTitle", + l10n_util::GetStringUTF16(IDS_CRASHES_TITLE)); + localized_strings.SetString("crashCountFormat", + l10n_util::GetStringUTF16(IDS_CRASHES_CRASH_COUNT_BANNER_FORMAT)); + localized_strings.SetString("crashHeaderFormat", + l10n_util::GetStringUTF16(IDS_CRASHES_CRASH_HEADER_FORMAT)); + localized_strings.SetString("crashTimeFormat", + l10n_util::GetStringUTF16(IDS_CRASHES_CRASH_TIME_FORMAT)); + localized_strings.SetString("bugLinkText", + l10n_util::GetStringUTF16(IDS_CRASHES_BUG_LINK_LABEL)); + localized_strings.SetString("noCrashesMessage", + l10n_util::GetStringUTF16(IDS_CRASHES_NO_CRASHES_MESSAGE)); + localized_strings.SetString("disabledHeader", + l10n_util::GetStringUTF16(IDS_CRASHES_DISABLED_HEADER)); + localized_strings.SetString("disabledMessage", + l10n_util::GetStringUTF16(IDS_CRASHES_DISABLED_MESSAGE)); + + ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece crashes_html( + ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_CRASHES_HTML)); + std::string full_html = + jstemplate_builder::GetI18nTemplateHtml(crashes_html, &localized_strings); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// CrashesDOMHandler +// +//////////////////////////////////////////////////////////////////////////////// + +// The handler for Javascript messages for the chrome://crashes/ page. +class CrashesDOMHandler : public WebUIMessageHandler, + public CrashUploadList::Delegate { + public: + explicit CrashesDOMHandler(); + virtual ~CrashesDOMHandler(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // CrashUploadList::Delegate implemenation. + virtual void OnCrashListAvailable(); + + private: + // Asynchronously fetches the list of crashes. Called from JS. + void HandleRequestCrashes(const ListValue* args); + + // Sends the recent crashes list JS. + void UpdateUI(); + + // Returns whether or not crash reporting is enabled. + bool CrashReportingEnabled() const; + + scoped_refptr<CrashUploadList> upload_list_; + bool list_available_; + bool js_request_pending_; + + DISALLOW_COPY_AND_ASSIGN(CrashesDOMHandler); +}; + +CrashesDOMHandler::CrashesDOMHandler() + : list_available_(false), js_request_pending_(false) { + upload_list_ = new CrashUploadList(this); +} + +CrashesDOMHandler::~CrashesDOMHandler() { + upload_list_->ClearDelegate(); +} + +WebUIMessageHandler* CrashesDOMHandler::Attach(WebUI* web_ui) { + upload_list_->LoadCrashListAsynchronously(); + return WebUIMessageHandler::Attach(web_ui); +} + +void CrashesDOMHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("requestCrashList", + NewCallback(this, &CrashesDOMHandler::HandleRequestCrashes)); +} + +void CrashesDOMHandler::HandleRequestCrashes(const ListValue* args) { + if (!CrashReportingEnabled() || list_available_) + UpdateUI(); + else + js_request_pending_ = true; +} + +void CrashesDOMHandler::OnCrashListAvailable() { + list_available_ = true; + if (js_request_pending_) + UpdateUI(); +} + +void CrashesDOMHandler::UpdateUI() { + bool crash_reporting_enabled = CrashReportingEnabled(); + ListValue crash_list; + + if (crash_reporting_enabled) { + std::vector<CrashUploadList::CrashInfo> crashes; + upload_list_->GetUploadedCrashes(50, &crashes); + + for (std::vector<CrashUploadList::CrashInfo>::iterator i = crashes.begin(); + i != crashes.end(); ++i) { + DictionaryValue* crash = new DictionaryValue(); + crash->SetString("id", i->crash_id); + crash->SetString("time", + base::TimeFormatFriendlyDateAndTime(i->crash_time)); + crash_list.Append(crash); + } + } + + FundamentalValue enabled(crash_reporting_enabled); + + web_ui_->CallJavascriptFunction(L"updateCrashList", enabled, crash_list); +} + +bool CrashesDOMHandler::CrashReportingEnabled() const { +#if defined(GOOGLE_CHROME_BUILD) + PrefService* prefs = web_ui_->GetProfile()->GetPrefs(); + return prefs->GetBoolean(prefs::kMetricsReportingEnabled); +#else + return false; +#endif +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// CrashesUI +// +/////////////////////////////////////////////////////////////////////////////// + +CrashesUI::CrashesUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new CrashesDOMHandler())->Attach(this)); + + CrashesUIHTMLSource* html_source = new CrashesUIHTMLSource(); + + // Set up the chrome://crashes/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +RefCountedMemory* CrashesUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_SAD_FAVICON); +} diff --git a/chrome/browser/ui/webui/crashes_ui.h b/chrome/browser/ui/webui/crashes_ui.h new file mode 100644 index 0000000..919b260 --- /dev/null +++ b/chrome/browser/ui/webui/crashes_ui.h @@ -0,0 +1,23 @@ +// 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 CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class RefCountedMemory; + +class CrashesUI : public WebUI { + public: + explicit CrashesUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(CrashesUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_CRASHES_UI_H_ diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc new file mode 100644 index 0000000..50aa4f3 --- /dev/null +++ b/chrome/browser/ui/webui/devtools_ui.cc @@ -0,0 +1,16 @@ +// 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/ui/webui/devtools_ui.h" + +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/common/render_messages.h" + +DevToolsUI::DevToolsUI(TabContents* contents) : WebUI(contents) { +} + +void DevToolsUI::RenderViewCreated(RenderViewHost* render_view_host) { + render_view_host->Send(new ViewMsg_SetupDevToolsClient( + render_view_host->routing_id())); +} diff --git a/chrome/browser/ui/webui/devtools_ui.h b/chrome/browser/ui/webui/devtools_ui.h new file mode 100644 index 0000000..cca4e51 --- /dev/null +++ b/chrome/browser/ui/webui/devtools_ui.h @@ -0,0 +1,22 @@ +// 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 CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class DevToolsUI : public WebUI { + public: + explicit DevToolsUI(TabContents* contents); + + // WebUI + virtual void RenderViewCreated(RenderViewHost* render_view_host); + + private: + DISALLOW_COPY_AND_ASSIGN(DevToolsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_DEVTOOLS_UI_H_ diff --git a/chrome/browser/ui/webui/downloads_ui.cc b/chrome/browser/ui/webui/downloads_ui.cc new file mode 100644 index 0000000..96f7466 --- /dev/null +++ b/chrome/browser/ui/webui/downloads_ui.cc @@ -0,0 +1,145 @@ +// 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/ui/webui/downloads_ui.h" + +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/threading/thread.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/defaults.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/browser/webui/downloads_dom_handler.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// +// DownloadsHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +class DownloadsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + DownloadsUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~DownloadsUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(DownloadsUIHTMLSource); +}; + +DownloadsUIHTMLSource::DownloadsUIHTMLSource() + : DataSource(chrome::kChromeUIDownloadsHost, MessageLoop::current()) { +} + +void DownloadsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString("title", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE)); + localized_strings.SetString("searchbutton", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_SEARCH_BUTTON)); + localized_strings.SetString("no_results", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_SEARCH_BUTTON)); + localized_strings.SetString("searchresultsfor", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_SEARCHRESULTSFOR)); + localized_strings.SetString("downloads", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_TITLE)); + localized_strings.SetString("clear_all", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_CLEAR_ALL)); + + // Status. + localized_strings.SetString("status_cancelled", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_TAB_CANCELED)); + localized_strings.SetString("status_paused", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_PROGRESS_PAUSED)); + + // Dangerous file. + localized_strings.SetString("danger_desc", + l10n_util::GetStringUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD)); + localized_strings.SetString("danger_save", + l10n_util::GetStringUTF16(IDS_SAVE_DOWNLOAD)); + localized_strings.SetString("danger_discard", + l10n_util::GetStringUTF16(IDS_DISCARD_DOWNLOAD)); + + // Controls. + localized_strings.SetString("control_pause", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_PAUSE)); + if (browser_defaults::kDownloadPageHasShowInFolder) { + localized_strings.SetString("control_showinfolder", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_SHOW)); + } + localized_strings.SetString("control_retry", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_RETRY)); + localized_strings.SetString("control_cancel", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_CANCEL)); + localized_strings.SetString("control_resume", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_RESUME)); + localized_strings.SetString("control_removefromlist", + l10n_util::GetStringUTF16(IDS_DOWNLOAD_LINK_REMOVE)); + + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece downloads_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_DOWNLOADS_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + downloads_html, &localized_strings); + + 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); +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// DownloadsUI +// +/////////////////////////////////////////////////////////////////////////////// + +DownloadsUI::DownloadsUI(TabContents* contents) : WebUI(contents) { + DownloadManager* dlm = GetProfile()->GetDownloadManager(); + + DownloadsDOMHandler* handler = new DownloadsDOMHandler(dlm); + AddMessageHandler(handler); + handler->Attach(this); + handler->Init(); + + DownloadsUIHTMLSource* html_source = new DownloadsUIHTMLSource(); + + // Set up the chrome://downloads/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +RefCountedMemory* DownloadsUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_DOWNLOADS_FAVICON); +} diff --git a/chrome/browser/ui/webui/downloads_ui.h b/chrome/browser/ui/webui/downloads_ui.h new file mode 100644 index 0000000..41c1832 --- /dev/null +++ b/chrome/browser/ui/webui/downloads_ui.h @@ -0,0 +1,23 @@ +// 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 CHROME_BROWSER_UI_WEBUI_DOWNLOADS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_DOWNLOADS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class RefCountedMemory; + +class DownloadsUI : public WebUI { + public: + explicit DownloadsUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(DownloadsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_DOWNLOADS_UI_H_ diff --git a/chrome/browser/ui/webui/filebrowse_ui.cc b/chrome/browser/ui/webui/filebrowse_ui.cc new file mode 100644 index 0000000..6d6c1cb --- /dev/null +++ b/chrome/browser/ui/webui/filebrowse_ui.cc @@ -0,0 +1,1272 @@ +// 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/ui/webui/filebrowse_ui.h" + +#include <algorithm> +#include <vector> + +#include "base/callback.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "base/weak_ptr.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/download/download_item.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/download_util.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/webui/mediaplayer_ui.h" +#include "chrome/browser/webui/favicon_source.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/time_format.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "net/base/escape.h" +#include "net/url_request/url_request_file_job.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/browser/chromeos/cros/mount_library.h" +#include "chrome/browser/chromeos/login/user_manager.h" +#endif + +// Maximum number of search results to return in a given search. We should +// eventually remove this. +static const int kMaxSearchResults = 100; +static const char kPropertyPath[] = "path"; +static const char kPropertyTitle[] = "title"; +static const char kPropertyDirectory[] = "isDirectory"; +static const char kPicasawebUserPrefix[] = + "http://picasaweb.google.com/data/feed/api/user/"; +static const char kPicasawebDefault[] = "/albumid/default"; +static const char kPicasawebDropBox[] = "/home"; +static const char kPicasawebBaseUrl[] = "http://picasaweb.google.com/"; +static const char kMediaPath[] = "/media"; +static const char kFilebrowseURLHash[] = "chrome://filebrowse#"; +static const int kPopupLeft = 0; +static const int kPopupTop = 0; + +class FileBrowseUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + FileBrowseUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~FileBrowseUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(FileBrowseUIHTMLSource); +}; + +class TaskProxy; + +// The handler for Javascript messages related to the "filebrowse" view. +class FilebrowseHandler : public net::DirectoryLister::DirectoryListerDelegate, + public WebUIMessageHandler, +#if defined(OS_CHROMEOS) + public chromeos::MountLibrary::Observer, +#endif + public base::SupportsWeakPtr<FilebrowseHandler>, + public URLFetcher::Delegate, + public DownloadManager::Observer, + public DownloadItem::Observer { + public: + FilebrowseHandler(); + virtual ~FilebrowseHandler(); + + // Init work after Attach. + void Init(); + + // DirectoryLister::DirectoryListerDelegate methods: + virtual void OnListFile( + const net::DirectoryLister::DirectoryListerData& data); + virtual void OnListDone(int error); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + +#if defined(OS_CHROMEOS) + void MountChanged(chromeos::MountLibrary* obj, + chromeos::MountEventType evt, + const std::string& path); +#endif + + // DownloadItem::Observer interface + virtual void OnDownloadUpdated(DownloadItem* download); + virtual void OnDownloadFileCompleted(DownloadItem* download); + virtual void OnDownloadOpened(DownloadItem* download) { } + + // DownloadManager::Observer interface + virtual void ModelChanged(); + + // Callback for the "getRoots" message. + void HandleGetRoots(const ListValue* args); + + void GetChildrenForPath(const FilePath& path, bool is_refresh); + + void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const net::URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Callback for the "getChildren" message. + void HandleGetChildren(const ListValue* args); + // Callback for the "refreshDirectory" message. + void HandleRefreshDirectory(const ListValue* args); + void HandleIsAdvancedEnabled(const ListValue* args); + + // Callback for the "getMetadata" message. + void HandleGetMetadata(const ListValue* args); + + // Callback for the "openNewWindow" message. + void OpenNewFullWindow(const ListValue* args); + void OpenNewPopupWindow(const ListValue* args); + + // Callback for the "uploadToPicasaweb" message. + void UploadToPicasaweb(const ListValue* args); + + // Callback for the "getDownloads" message. + void HandleGetDownloads(const ListValue* args); + + void HandleCreateNewFolder(const ListValue* args); + + void PlayMediaFile(const ListValue* args); + void EnqueueMediaFile(const ListValue* args); + + void HandleDeleteFile(const ListValue* args); + void HandleCopyFile(const ListValue* value); + void CopyFile(const FilePath& src, const FilePath& dest, TaskProxy* task); + void DeleteFile(const FilePath& path, TaskProxy* task); + void FireDeleteComplete(const FilePath& path); + void FireCopyComplete(const FilePath& src, const FilePath& dest); + + void HandlePauseToggleDownload(const ListValue* args); + + void HandleCancelDownload(const ListValue* args); + void HandleAllowDownload(const ListValue* args); + + void CreateNewFolder(const FilePath& path) const; + + void ReadInFile(); + void FireUploadComplete(); + + void SendPicasawebRequest(); + + // Callback for the "validateSavePath" message. + void HandleValidateSavePath(const ListValue* args); + + // Validate a save path on file thread. + void ValidateSavePathOnFileThread(const FilePath& save_path, TaskProxy* task); + + // Fire save path validation result to JS onValidatedSavePath. + void FireOnValidatedSavePathOnUIThread(bool valid, const FilePath& save_path); + + private: + + // Retrieves downloads from the DownloadManager and updates the page. + void UpdateDownloadList(); + + void OpenNewWindow(const ListValue* args, bool popup); + + // Clear all download items and their observers. + void ClearDownloadItems(); + + // Send the current list of downloads to the page. + void SendCurrentDownloads(); + + void SendNewDownload(DownloadItem* download); + + bool ValidateSaveDir(const FilePath& save_dir, bool exists) const; + bool AccessDisabled(const FilePath& path) const; + + scoped_ptr<ListValue> filelist_value_; + FilePath currentpath_; + Profile* profile_; + TabContents* tab_contents_; + std::string current_file_contents_; + std::string current_file_uploaded_; + int upload_response_code_; + TaskProxy* current_task_; + scoped_refptr<net::DirectoryLister> lister_; + bool is_refresh_; + scoped_ptr<URLFetcher> fetch_; + + DownloadManager* download_manager_; + typedef std::vector<DownloadItem*> DownloadList; + DownloadList active_download_items_; + DownloadList download_items_; + bool got_first_download_list_; + DISALLOW_COPY_AND_ASSIGN(FilebrowseHandler); +}; + +class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> { + public: + TaskProxy(const base::WeakPtr<FilebrowseHandler>& handler, + const FilePath& path, const FilePath& dest) + : handler_(handler), + src_(path), + dest_(dest) {} + TaskProxy(const base::WeakPtr<FilebrowseHandler>& handler, + const FilePath& path) + : handler_(handler), + src_(path) {} + + // TaskProxy is created on the UI thread, so in some cases, + // we need to post back to the UI thread for destruction. + void DeleteOnUIThread() { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &TaskProxy::DoNothing)); + } + + void DoNothing() {} + + void ReadInFileProxy() { + if (handler_) + handler_->ReadInFile(); + DeleteOnUIThread(); + } + + void DeleteFetcher(URLFetcher* fetch) { + delete fetch; + } + + void SendPicasawebRequestProxy() { + if (handler_) + handler_->SendPicasawebRequest(); + DeleteOnUIThread(); + } + + void FireUploadCompleteProxy() { + if (handler_) + handler_->FireUploadComplete(); + } + + void DeleteFileProxy() { + if (handler_) + handler_->DeleteFile(src_, this); + } + + void CopyFileProxy() { + if (handler_) + handler_->CopyFile(src_, dest_, this); + } + + void CreateNewFolderProxy() { + if (handler_) + handler_->CreateNewFolder(src_); + DeleteOnUIThread(); + } + + void FireDeleteCompleteProxy() { + if (handler_) + handler_->FireDeleteComplete(src_); + } + void FireCopyCompleteProxy() { + if (handler_) + handler_->FireCopyComplete(src_, dest_); + } + + void ValidateSavePathOnFileThread() { + if (handler_) + handler_->ValidateSavePathOnFileThread(src_, this); + } + + void FireOnValidatedSavePathOnUIThread(bool valid) { + if (handler_) + handler_->FireOnValidatedSavePathOnUIThread(valid, src_); + } + + private: + base::WeakPtr<FilebrowseHandler> handler_; + FilePath src_; + FilePath dest_; + friend class base::RefCountedThreadSafe<TaskProxy>; + DISALLOW_COPY_AND_ASSIGN(TaskProxy); +}; + + +//////////////////////////////////////////////////////////////////////////////// +// +// FileBrowseHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +FileBrowseUIHTMLSource::FileBrowseUIHTMLSource() + : DataSource(chrome::kChromeUIFileBrowseHost, MessageLoop::current()) { +} + +void FileBrowseUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + // TODO(dhg): Add stirings to localized strings, also add more strings + // that are currently hardcoded. + localized_strings.SetString("title", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_TITLE)); + localized_strings.SetString("pause", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_PAUSE)); + localized_strings.SetString("resume", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_RESUME)); + localized_strings.SetString("scanning", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_SCANNING)); + localized_strings.SetString("confirmdelete", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_DELETE)); + localized_strings.SetString("confirmyes", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_YES)); + localized_strings.SetString("confirmcancel", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_CANCEL)); + localized_strings.SetString("allowdownload", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_CONFIRM_DOWNLOAD)); + localized_strings.SetString("filenameprompt", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_PROMPT_FILENAME)); + localized_strings.SetString("save", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_SAVE)); + localized_strings.SetString("newfolder", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_NEW_FOLDER)); + localized_strings.SetString("open", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_OPEN)); + localized_strings.SetString("picasaweb", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_UPLOAD_PICASAWEB)); + localized_strings.SetString("flickr", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_UPLOAD_FLICKR)); + localized_strings.SetString("email", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_UPLOAD_EMAIL)); + localized_strings.SetString("delete", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_DELETE)); + localized_strings.SetString("enqueue", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_ENQUEUE)); + localized_strings.SetString("mediapath", kMediaPath); + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + // TODO(viettrungluu): this is wrong -- FilePath's need not be Unicode. + localized_strings.SetString("downloadpath", default_download_path.value()); + localized_strings.SetString("error_unknown_file_type", + l10n_util::GetStringUTF16(IDS_FILEBROWSER_ERROR_UNKNOWN_FILE_TYPE)); + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece filebrowse_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_FILEBROWSE_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + filebrowse_html, &localized_strings); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// FilebrowseHandler +// +//////////////////////////////////////////////////////////////////////////////// +FilebrowseHandler::FilebrowseHandler() + : profile_(NULL), + tab_contents_(NULL), + is_refresh_(false), + fetch_(NULL), + download_manager_(NULL), + got_first_download_list_(false) { + lister_ = NULL; +#if defined(OS_CHROMEOS) + chromeos::MountLibrary* lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + lib->AddObserver(this); +#endif +} + +FilebrowseHandler::~FilebrowseHandler() { +#if defined(OS_CHROMEOS) + chromeos::MountLibrary* lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + lib->RemoveObserver(this); +#endif + if (lister_.get()) { + lister_->Cancel(); + lister_->set_delegate(NULL); + } + + ClearDownloadItems(); + download_manager_->RemoveObserver(this); + URLFetcher* fetch = fetch_.release(); + if (fetch) { + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), currentpath_); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::DeleteFetcher, fetch)); + } +} + +WebUIMessageHandler* FilebrowseHandler::Attach(WebUI* web_ui) { + // Create our favicon data source. + profile_ = web_ui->GetProfile(); + profile_->GetChromeURLDataManager()->AddDataSource( + new FavIconSource(profile_)); + tab_contents_ = web_ui->tab_contents(); + return WebUIMessageHandler::Attach(web_ui); +} + +void FilebrowseHandler::Init() { + download_manager_ = profile_->GetDownloadManager(); + download_manager_->AddObserver(this); + static bool sent_request = false; + if (!sent_request) { + // If we have not sent a request before, we should do one in order to + // ensure that we have the correct cookies. This is for uploads. + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), currentpath_); + current_task_ = task; + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::SendPicasawebRequestProxy)); + sent_request = true; + } +} + +void FilebrowseHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("getRoots", + NewCallback(this, &FilebrowseHandler::HandleGetRoots)); + web_ui_->RegisterMessageCallback("getChildren", + NewCallback(this, &FilebrowseHandler::HandleGetChildren)); + web_ui_->RegisterMessageCallback("getMetadata", + NewCallback(this, &FilebrowseHandler::HandleGetMetadata)); + web_ui_->RegisterMessageCallback("openNewPopupWindow", + NewCallback(this, &FilebrowseHandler::OpenNewPopupWindow)); + web_ui_->RegisterMessageCallback("openNewFullWindow", + NewCallback(this, &FilebrowseHandler::OpenNewFullWindow)); + web_ui_->RegisterMessageCallback("uploadToPicasaweb", + NewCallback(this, &FilebrowseHandler::UploadToPicasaweb)); + web_ui_->RegisterMessageCallback("getDownloads", + NewCallback(this, &FilebrowseHandler::HandleGetDownloads)); + web_ui_->RegisterMessageCallback("createNewFolder", + NewCallback(this, &FilebrowseHandler::HandleCreateNewFolder)); + web_ui_->RegisterMessageCallback("playMediaFile", + NewCallback(this, &FilebrowseHandler::PlayMediaFile)); + web_ui_->RegisterMessageCallback("enqueueMediaFile", + NewCallback(this, &FilebrowseHandler::EnqueueMediaFile)); + web_ui_->RegisterMessageCallback("pauseToggleDownload", + NewCallback(this, &FilebrowseHandler::HandlePauseToggleDownload)); + web_ui_->RegisterMessageCallback("deleteFile", + NewCallback(this, &FilebrowseHandler::HandleDeleteFile)); + web_ui_->RegisterMessageCallback("copyFile", + NewCallback(this, &FilebrowseHandler::HandleCopyFile)); + web_ui_->RegisterMessageCallback("cancelDownload", + NewCallback(this, &FilebrowseHandler::HandleCancelDownload)); + web_ui_->RegisterMessageCallback("allowDownload", + NewCallback(this, &FilebrowseHandler::HandleAllowDownload)); + web_ui_->RegisterMessageCallback("refreshDirectory", + NewCallback(this, &FilebrowseHandler::HandleRefreshDirectory)); + web_ui_->RegisterMessageCallback("isAdvancedEnabled", + NewCallback(this, &FilebrowseHandler::HandleIsAdvancedEnabled)); + web_ui_->RegisterMessageCallback("validateSavePath", + NewCallback(this, &FilebrowseHandler::HandleValidateSavePath)); +} + + +void FilebrowseHandler::FireDeleteComplete(const FilePath& path) { + // We notify the UI by telling it to refresh its contents. + FilePath dir_path = path.DirName(); + GetChildrenForPath(dir_path, true); +}; + +void FilebrowseHandler::FireCopyComplete(const FilePath& src, + const FilePath& dest) { + // Notify the UI somehow. + FilePath dir_path = dest.DirName(); + GetChildrenForPath(dir_path, true); +}; + +void FilebrowseHandler::FireUploadComplete() { +#if defined(OS_CHROMEOS) + DictionaryValue info_value; + info_value.SetString("path", current_file_uploaded_); + + std::string username; + chromeos::UserManager* user_man = chromeos::UserManager::Get(); + username = user_man->logged_in_user().email(); + + if (username.empty()) { + LOG(ERROR) << "Unable to get username"; + return; + } + int location = username.find_first_of('@', 0); + if (location <= 0) { + LOG(ERROR) << "Username not formatted correctly"; + return; + } + username = username.erase(username.find_first_of('@', 0)); + std::string picture_url = kPicasawebBaseUrl; + picture_url += username; + picture_url += kPicasawebDropBox; + info_value.SetString("url", picture_url); + info_value.SetInteger("status_code", upload_response_code_); + web_ui_->CallJavascriptFunction(L"uploadComplete", info_value); +#endif +} + +#if defined(OS_CHROMEOS) +void FilebrowseHandler::MountChanged(chromeos::MountLibrary* obj, + chromeos::MountEventType evt, + const std::string& path) { + if (evt == chromeos::DISK_REMOVED || + evt == chromeos::DISK_CHANGED) { + web_ui_->CallJavascriptFunction(L"rootsChanged"); + } +} +#endif + +void FilebrowseHandler::OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const net::URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + upload_response_code_ = response_code; + VLOG(1) << "Response code: " << response_code; + VLOG(1) << "Request url: " << url; + if (StartsWithASCII(url.spec(), kPicasawebUserPrefix, true)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(current_task_, &TaskProxy::FireUploadCompleteProxy)); + } + fetch_.reset(); +} + +void FilebrowseHandler::HandleGetRoots(const ListValue* args) { + ListValue results_value; + DictionaryValue info_value; + // TODO(dhg): add other entries, make this more general +#if defined(OS_CHROMEOS) + chromeos::MountLibrary* lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + const chromeos::MountLibrary::DiskVector& disks = lib->disks(); + + for (size_t i = 0; i < disks.size(); ++i) { + if (!disks[i].mount_path.empty()) { + DictionaryValue* page_value = new DictionaryValue(); + page_value->SetString(kPropertyPath, disks[i].mount_path); + FilePath currentpath(disks[i].mount_path); + std::string filename; + filename = currentpath.BaseName().value(); + page_value->SetString(kPropertyTitle, filename); + page_value->SetBoolean(kPropertyDirectory, true); + results_value.Append(page_value); + } + } +#else + DictionaryValue* page_value = new DictionaryValue(); + page_value->SetString(kPropertyPath, "/media"); + page_value->SetString(kPropertyTitle, "Removeable"); + page_value->SetBoolean(kPropertyDirectory, true); + results_value.Append(page_value); +#endif + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + + DictionaryValue* download_value = new DictionaryValue(); + download_value->SetString(kPropertyPath, default_download_path.value()); + download_value->SetString(kPropertyTitle, "File Shelf"); + download_value->SetBoolean(kPropertyDirectory, true); + + results_value.Append(download_value); + + info_value.SetString("functionCall", "getRoots"); + info_value.SetString(kPropertyPath, ""); + web_ui_->CallJavascriptFunction(L"browseFileResult", + info_value, results_value); +} + +void FilebrowseHandler::HandleCreateNewFolder(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string path = WideToUTF8(ExtractStringValue(args)); + FilePath currentpath(path); + + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), currentpath); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::CreateNewFolderProxy)); +#endif +} + +void FilebrowseHandler::CreateNewFolder(const FilePath& currentpath) const { + if (!ValidateSaveDir(currentpath, false) || + !file_util::CreateDirectory(currentpath)) + LOG(ERROR) << "Unable to create directory " << currentpath.value(); +} + +void FilebrowseHandler::PlayMediaFile(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string url = WideToUTF8(ExtractStringValue(args)); + GURL gurl(url); + + Browser* browser = Browser::GetBrowserForController( + &tab_contents_->controller(), NULL); + MediaPlayer* mediaplayer = MediaPlayer::GetInstance(); + mediaplayer->ForcePlayMediaURL(gurl, browser); +#endif +} + +void FilebrowseHandler::EnqueueMediaFile(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string url = WideToUTF8(ExtractStringValue(args)); + GURL gurl(url); + + Browser* browser = Browser::GetBrowserForController( + &tab_contents_->controller(), NULL); + MediaPlayer* mediaplayer = MediaPlayer::GetInstance(); + mediaplayer->EnqueueMediaURL(gurl, browser); +#endif +} + +void FilebrowseHandler::HandleIsAdvancedEnabled(const ListValue* args) { +#if defined(OS_CHROMEOS) + bool is_enabled = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableAdvancedFileSystem); + bool mp_enabled = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMediaPlayer); + DictionaryValue info_value; + info_value.SetBoolean("enabled", is_enabled); + info_value.SetBoolean("mpEnabled", mp_enabled); + web_ui_->CallJavascriptFunction(L"enabledResult", + info_value); + +#endif +} + +void FilebrowseHandler::HandleRefreshDirectory(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string path = WideToUTF8(ExtractStringValue(args)); + FilePath currentpath(path); + GetChildrenForPath(currentpath, true); +#endif +} + +void FilebrowseHandler::HandlePauseToggleDownload(const ListValue* args) { +#if defined(OS_CHROMEOS) + int id; + ExtractIntegerValue(args, &id); + if ((id - 1) >= static_cast<int>(active_download_items_.size())) { + return; + } + DownloadItem* item = active_download_items_[id]; + item->TogglePause(); +#endif +} + +void FilebrowseHandler::HandleAllowDownload(const ListValue* args) { +#if defined(OS_CHROMEOS) + int id; + ExtractIntegerValue(args, &id); + if ((id - 1) >= static_cast<int>(active_download_items_.size())) { + return; + } + + DownloadItem* item = active_download_items_[id]; + download_manager_->DangerousDownloadValidated(item); +#endif +} + +void FilebrowseHandler::HandleCancelDownload(const ListValue* args) { +#if defined(OS_CHROMEOS) + int id; + ExtractIntegerValue(args, &id); + if ((id - 1) >= static_cast<int>(active_download_items_.size())) { + return; + } + DownloadItem* item = active_download_items_[id]; + FilePath path = item->full_path(); + item->Cancel(true); + FilePath dir_path = path.DirName(); + item->Remove(true); + GetChildrenForPath(dir_path, true); +#endif +} + +void FilebrowseHandler::OpenNewFullWindow(const ListValue* args) { + OpenNewWindow(args, false); +} + +void FilebrowseHandler::OpenNewPopupWindow(const ListValue* args) { + OpenNewWindow(args, true); +} + +void FilebrowseHandler::OpenNewWindow(const ListValue* args, bool popup) { + std::string url = WideToUTF8(ExtractStringValue(args)); + Browser* browser = popup ? + Browser::CreateForType(Browser::TYPE_APP_PANEL, profile_) : + BrowserList::GetLastActive(); + browser::NavigateParams params(browser, GURL(url), PageTransition::LINK); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + // TODO(beng): The following two calls should be automatic by Navigate(). + if (popup) { + // TODO(dhg): Remove these from being hardcoded. Allow javascript + // to specify. + params.browser->window()->SetBounds(gfx::Rect(0, 0, 400, 300)); + } + params.browser->window()->Show(); +} + +void FilebrowseHandler::SendPicasawebRequest() { +#if defined(OS_CHROMEOS) + chromeos::UserManager* user_man = chromeos::UserManager::Get(); + std::string username = user_man->logged_in_user().email(); + + if (username.empty()) { + LOG(ERROR) << "Unable to get username"; + return; + } + + fetch_.reset(URLFetcher::Create(0, + GURL(kPicasawebBaseUrl), + URLFetcher::GET, + this)); + fetch_->set_request_context(profile_->GetRequestContext()); + fetch_->Start(); +#endif +} + +void FilebrowseHandler::ReadInFile() { +#if defined(OS_CHROMEOS) + // Get the users username + std::string username; + chromeos::UserManager* user_man = chromeos::UserManager::Get(); + username = user_man->logged_in_user().email(); + + if (username.empty()) { + LOG(ERROR) << "Unable to get username"; + return; + } + int location = username.find_first_of('@', 0); + if (location <= 0) { + LOG(ERROR) << "Username not formatted correctly"; + return; + } + username = username.erase(username.find_first_of('@', 0)); + std::string url = kPicasawebUserPrefix; + url += username; + url += kPicasawebDefault; + + FilePath currentpath(current_file_uploaded_); + // Get the filename + std::string filename; + filename = currentpath.BaseName().value(); + std::string filecontents; + if (!file_util::ReadFileToString(currentpath, &filecontents)) { + LOG(ERROR) << "Unable to read this file:" << currentpath.value(); + return; + } + fetch_.reset(URLFetcher::Create(0, + GURL(url), + URLFetcher::POST, + this)); + fetch_->set_upload_data("image/jpeg", filecontents); + // Set the filename on the server + std::string slug = "Slug: "; + slug += filename; + fetch_->set_extra_request_headers(slug); + fetch_->set_request_context(profile_->GetRequestContext()); + fetch_->Start(); +#endif +} + +// This is just a prototype for allowing generic uploads to various sites +// TODO(dhg): Remove this and implement general upload. +void FilebrowseHandler::UploadToPicasaweb(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string search_string = WideToUTF8(ExtractStringValue(args)); + current_file_uploaded_ = search_string; + // ReadInFile(); + FilePath current_path(search_string); + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), current_path); + current_task_ = task; + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::ReadInFileProxy)); +#endif +} + +void FilebrowseHandler::GetChildrenForPath(const FilePath& path, + bool is_refresh) { + if (path.empty()) + return; + + filelist_value_.reset(new ListValue()); + currentpath_ = path; + + if (lister_.get()) { + lister_->Cancel(); + lister_->set_delegate(NULL); + lister_ = NULL; + } + + is_refresh_ = is_refresh; + +#if defined(OS_CHROMEOS) + // Don't allow listing files in inaccessible dirs. + if (AccessDisabled(path)) + return; +#endif + + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + + if (currentpath_ == default_download_path) { + lister_ = new net::DirectoryLister(currentpath_, + false, + net::DirectoryLister::DATE, + this); + } else { + lister_ = new net::DirectoryLister(currentpath_, this); + } + lister_->Start(); +} + +void FilebrowseHandler::HandleGetChildren(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string path = WideToUTF8(ExtractStringValue(args)); + FilePath currentpath(path); + filelist_value_.reset(new ListValue()); + + GetChildrenForPath(currentpath, false); +#endif +} + +void FilebrowseHandler::OnListFile( + const net::DirectoryLister::DirectoryListerData& data) { +#if defined(OS_WIN) + if (data.info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) { + return; + } +#elif defined(OS_POSIX) + if (data.info.filename[0] == '.') { + return; + } + + // Suppress .crdownload files. + static const char crdownload[] = (".crdownload"); + static const size_t crdownload_size = arraysize(crdownload); + const std::string& filename = data.info.filename; + if ((filename.size() > crdownload_size) && + (filename.rfind(crdownload) == (filename.size() - crdownload_size))) + return; +#endif + + DictionaryValue* file_value = new DictionaryValue(); + +#if defined(OS_WIN) + int64 size = (static_cast<int64>(data.info.nFileSizeHigh) << 32) | + data.info.nFileSizeLow; + file_value->SetString(kPropertyTitle, data.info.cFileName); + file_value->SetString(kPropertyPath, + currentpath_.Append(data.info.cFileName).value()); + file_value->SetBoolean(kPropertyDirectory, + (data.info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false); +#elif defined(OS_POSIX) + file_value->SetString(kPropertyTitle, data.info.filename); + file_value->SetString(kPropertyPath, + currentpath_.Append(data.info.filename).value()); + file_value->SetBoolean(kPropertyDirectory, S_ISDIR(data.info.stat.st_mode)); +#endif + filelist_value_->Append(file_value); +} + +void FilebrowseHandler::OnListDone(int error) { + DictionaryValue info_value; + if (is_refresh_) { + info_value.SetString("functionCall", "refresh"); + } else { + info_value.SetString("functionCall", "getChildren"); + } + info_value.SetString(kPropertyPath, currentpath_.value()); + web_ui_->CallJavascriptFunction(L"browseFileResult", + info_value, *(filelist_value_.get())); +} + +void FilebrowseHandler::HandleGetMetadata(const ListValue* args) { +} + +void FilebrowseHandler::HandleGetDownloads(const ListValue* args) { + UpdateDownloadList(); +} + +void FilebrowseHandler::ModelChanged() { + if (!currentpath_.empty()) + GetChildrenForPath(currentpath_, true); + else + UpdateDownloadList(); +} + +void FilebrowseHandler::UpdateDownloadList() { + ClearDownloadItems(); + + std::vector<DownloadItem*> downloads; + download_manager_->GetAllDownloads(FilePath(), &downloads); + + std::vector<DownloadItem*> new_downloads; + // Scan for any in progress downloads and add ourself to them as an observer. + for (DownloadList::iterator it = downloads.begin(); + it != downloads.end(); ++it) { + DownloadItem* download = *it; + // We want to know what happens as the download progresses and be notified + // when the user validates the dangerous download. + if (download->state() == DownloadItem::IN_PROGRESS || + download->safety_state() == DownloadItem::DANGEROUS) { + download->AddObserver(this); + active_download_items_.push_back(download); + } + DownloadList::iterator item = find(download_items_.begin(), + download_items_.end(), + download); + if (item == download_items_.end() && got_first_download_list_) { + SendNewDownload(download); + } + new_downloads.push_back(download); + } + download_items_.swap(new_downloads); + got_first_download_list_ = true; + SendCurrentDownloads(); +} + +void FilebrowseHandler::SendNewDownload(DownloadItem* download) { + ListValue results_value; + results_value.Append(download_util::CreateDownloadItemValue(download, -1)); + web_ui_->CallJavascriptFunction(L"newDownload", results_value); +} + +void FilebrowseHandler::DeleteFile(const FilePath& path, TaskProxy* task) { + if (!file_util::Delete(path, true)) { + LOG(ERROR) << "unable to delete directory"; + } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(task, &TaskProxy::FireDeleteCompleteProxy)); +} + +void FilebrowseHandler::CopyFile(const FilePath& src, + const FilePath& dest, + TaskProxy* task) { + if (file_util::DirectoryExists(src)) { + if (!file_util::CopyDirectory(src, dest, true)) { + LOG(ERROR) << "unable to copy directory:" << src.value(); + } + } else { + if (!file_util::CopyFile(src, dest)) { + LOG(ERROR) << "unable to copy file" << src.value(); + } + } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(task, &TaskProxy::FireCopyCompleteProxy)); +} + +void FilebrowseHandler::HandleDeleteFile(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string path = WideToUTF8(ExtractStringValue(args)); + FilePath currentpath(path); + + // Don't allow file deletion in inaccessible dirs. + if (AccessDisabled(currentpath)) + return; + + for (unsigned int x = 0; x < active_download_items_.size(); x++) { + FilePath item = active_download_items_[x]->full_path(); + if (item == currentpath) { + active_download_items_[x]->Cancel(true); + active_download_items_[x]->Remove(true); + FilePath dir_path = item.DirName(); + GetChildrenForPath(dir_path, true); + return; + } + } + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), currentpath); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::DeleteFileProxy)); +#endif +} + +void FilebrowseHandler::HandleCopyFile(const ListValue* value) { +#if defined(OS_CHROMEOS) + if (value && value->GetType() == Value::TYPE_LIST) { + const ListValue* list_value = static_cast<const ListValue*>(value); + std::string src; + std::string dest; + + // Get path string. + if (list_value->GetString(0, &src) && + list_value->GetString(1, &dest)) { + FilePath SrcPath = FilePath(src); + FilePath DestPath = FilePath(dest); + + // Don't allow file copy to inaccessible dirs. + if (AccessDisabled(DestPath)) + return; + + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), + SrcPath, DestPath); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod( + task.get(), &TaskProxy::CopyFileProxy)); + } else { + LOG(ERROR) << "Unable to get string"; + return; + } + } +#endif +} + +void FilebrowseHandler::HandleValidateSavePath(const ListValue* args) { + std::string string_path; + if (!args || !args->GetString(0, &string_path)) { + FireOnValidatedSavePathOnUIThread(false, FilePath()); // Invalid save path. + return; + } + + FilePath save_path(string_path); + +#if defined(OS_CHROMEOS) + scoped_refptr<TaskProxy> task = new TaskProxy(AsWeakPtr(), save_path); + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(task.get(), &TaskProxy::ValidateSavePathOnFileThread)); +#else + // No save path checking for non-ChromeOS platforms. + FireOnValidatedSavePathOnUIThread(true, save_path); +#endif +} + +void FilebrowseHandler::ValidateSavePathOnFileThread( + const FilePath& save_path, TaskProxy* task) { +#if defined(OS_CHROMEOS) + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + const bool valid = ValidateSaveDir(save_path.DirName(), true); + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(task, + &TaskProxy::FireOnValidatedSavePathOnUIThread, + valid)); +#endif +} + +bool FilebrowseHandler::ValidateSaveDir(const FilePath& save_dir, + bool exists) const { +#if defined(OS_CHROMEOS) + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + + // Valid save dir must be inside default download dir. + if (default_download_path == save_dir) + return true; + if (exists) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + return file_util::ContainsPath(default_download_path, save_dir); + } else { + return default_download_path.IsParent(save_dir); + } +#endif + return false; +} + +void FilebrowseHandler::FireOnValidatedSavePathOnUIThread(bool valid, + const FilePath& save_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + FundamentalValue valid_value(valid); + StringValue path_value(save_path.value()); + web_ui_->CallJavascriptFunction(L"onValidatedSavePath", + valid_value, path_value); +} + +void FilebrowseHandler::OnDownloadUpdated(DownloadItem* download) { + DownloadList::iterator it = find(active_download_items_.begin(), + active_download_items_.end(), + download); + if (it == active_download_items_.end()) + return; + const int id = static_cast<int>(it - active_download_items_.begin()); + + scoped_ptr<DictionaryValue> download_item( + download_util::CreateDownloadItemValue(download, id)); + web_ui_->CallJavascriptFunction(L"downloadUpdated", *download_item.get()); +} + +void FilebrowseHandler::ClearDownloadItems() { + for (DownloadList::iterator it = active_download_items_.begin(); + it != active_download_items_.end(); ++it) { + (*it)->RemoveObserver(this); + } + active_download_items_.clear(); +} + +void FilebrowseHandler::SendCurrentDownloads() { + ListValue results_value; + for (DownloadList::iterator it = active_download_items_.begin(); + it != active_download_items_.end(); ++it) { + int index = static_cast<int>(it - active_download_items_.begin()); + results_value.Append(download_util::CreateDownloadItemValue(*it, index)); + } + + web_ui_->CallJavascriptFunction(L"downloadsList", results_value); +} + +void FilebrowseHandler::OnDownloadFileCompleted(DownloadItem* download) { + GetChildrenForPath(currentpath_, true); +} + +bool FilebrowseHandler::AccessDisabled(const FilePath& path) const { + return !ValidateSaveDir(path, false) && + net::URLRequestFileJob::AccessDisabled(path); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// FileBrowseUI +// +//////////////////////////////////////////////////////////////////////////////// + +FileBrowseUI::FileBrowseUI(TabContents* contents) : HtmlDialogUI(contents) { + FilebrowseHandler* handler = new FilebrowseHandler(); + AddMessageHandler((handler)->Attach(this)); + handler->Init(); + FileBrowseUIHTMLSource* html_source = new FileBrowseUIHTMLSource(); + + // Set up the chrome://filebrowse/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +Browser* FileBrowseUI::OpenPopup(Profile* profile, + const std::string& hashArgument, + int width, + int height) { + // Get existing pop up for given hashArgument. + Browser* browser = GetPopupForPath(hashArgument, profile); + + // Create new browser if no matching pop up found. + if (browser == NULL) { + browser = Browser::CreateForType(Browser::TYPE_APP_PANEL, profile); + std::string url; + if (hashArgument.empty()) { + url = chrome::kChromeUIFileBrowseURL; + } else { + url = kFilebrowseURLHash; + url.append(hashArgument); + } + + browser::NavigateParams params(browser, GURL(url), PageTransition::LINK); + params.disposition = NEW_FOREGROUND_TAB; + browser::Navigate(¶ms); + // TODO(beng): The following two calls should be automatic by Navigate(). + params.browser->window()->SetBounds(gfx::Rect(kPopupLeft, + kPopupTop, + width, + height)); + + params.browser->window()->Show(); + } else { + browser->window()->Show(); + } + + return browser; +} + +Browser* FileBrowseUI::GetPopupForPath(const std::string& path, + Profile* profile) { + std::string current_path = path; + if (current_path.empty()) { + bool is_enabled = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableAdvancedFileSystem); + if (!is_enabled) { + FilePath default_download_path; + if (!PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, + &default_download_path)) { + NOTREACHED(); + } + current_path = default_download_path.value(); + } + } + + for (BrowserList::const_iterator it = BrowserList::begin(); + it != BrowserList::end(); ++it) { + if (((*it)->type() == Browser::TYPE_APP_PANEL)) { + TabContents* tab_contents = (*it)->GetSelectedTabContents(); + DCHECK(tab_contents); + if (!tab_contents) + continue; + const GURL& url = tab_contents->GetURL(); + + if (url.SchemeIs(chrome::kChromeUIScheme) && + url.host() == chrome::kChromeUIFileBrowseHost && + url.ref() == current_path && + (*it)->profile() == profile) { + return (*it); + } + } + } + + return NULL; +} + +const int FileBrowseUI::kPopupWidth = 250; +const int FileBrowseUI::kPopupHeight = 300; +const int FileBrowseUI::kSmallPopupWidth = 250; +const int FileBrowseUI::kSmallPopupHeight = 50; diff --git a/chrome/browser/ui/webui/filebrowse_ui.h b/chrome/browser/ui/webui/filebrowse_ui.h new file mode 100644 index 0000000..1df17d4 --- /dev/null +++ b/chrome/browser/ui/webui/filebrowse_ui.h @@ -0,0 +1,39 @@ +// 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 CHROME_BROWSER_UI_WEBUI_FILEBROWSE_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_FILEBROWSE_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/history/history.h" +#include "chrome/browser/ui/webui/html_dialog_ui.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "net/base/directory_lister.h" + +class Browser; +class Profile; + +class FileBrowseUI : public HtmlDialogUI { + public: + static const int kPopupWidth; + static const int kPopupHeight; + static const int kSmallPopupWidth; + static const int kSmallPopupHeight; + + explicit FileBrowseUI(TabContents* contents); + + static Browser* OpenPopup(Profile* profile, + const std::string& hashArgument, + int width, + int height); + static Browser* GetPopupForPath(const std::string& path, + Profile* profile); + + private: + DISALLOW_COPY_AND_ASSIGN(FileBrowseUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_FILEBROWSE_UI_H_ diff --git a/chrome/browser/ui/webui/flags_ui.cc b/chrome/browser/ui/webui/flags_ui.cc new file mode 100644 index 0000000..8724388 --- /dev/null +++ b/chrome/browser/ui/webui/flags_ui.cc @@ -0,0 +1,204 @@ +// 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/ui/webui/flags_ui.h" + +#include <string> + +#include "base/singleton.h" +#include "base/values.h" +#include "chrome/browser/about_flags.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// +// FlagsUIHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +class FlagsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + FlagsUIHTMLSource() + : DataSource(chrome::kChromeUIFlagsHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~FlagsUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(FlagsUIHTMLSource); +}; + +void FlagsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString("flagsLongTitle", + l10n_util::GetStringUTF16(IDS_FLAGS_LONG_TITLE)); + localized_strings.SetString("flagsTableTitle", + l10n_util::GetStringUTF16(IDS_FLAGS_TABLE_TITLE)); + localized_strings.SetString("flagsNoExperimentsAvailable", + l10n_util::GetStringUTF16(IDS_FLAGS_NO_EXPERIMENTS_AVAILABLE)); + localized_strings.SetString("flagsWarningHeader", l10n_util::GetStringUTF16( + IDS_FLAGS_WARNING_HEADER)); + localized_strings.SetString("flagsBlurb", l10n_util::GetStringUTF16( + IDS_FLAGS_WARNING_TEXT)); + localized_strings.SetString("flagsRestartNotice", l10n_util::GetStringFUTF16( + IDS_FLAGS_RELAUNCH_NOTICE, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + localized_strings.SetString("flagsRestartButton", + l10n_util::GetStringUTF16(IDS_FLAGS_RELAUNCH_BUTTON)); + localized_strings.SetString("disable", + l10n_util::GetStringUTF16(IDS_FLAGS_DISABLE)); + localized_strings.SetString("enable", + l10n_util::GetStringUTF16(IDS_FLAGS_ENABLE)); + + ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece flags_html( + ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_FLAGS_HTML)); + std::string full_html(flags_html.data(), flags_html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// FlagsDOMHandler +// +//////////////////////////////////////////////////////////////////////////////// + +// The handler for Javascript messages for the about:flags page. +class FlagsDOMHandler : public WebUIMessageHandler { + public: + FlagsDOMHandler() {} + virtual ~FlagsDOMHandler() {} + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for the "requestFlagsExperiments" message. + void HandleRequestFlagsExperiments(const ListValue* args); + + // Callback for the "enableFlagsExperiment" message. + void HandleEnableFlagsExperimentMessage(const ListValue* args); + + // Callback for the "restartBrowser" message. Restores all tabs on restart. + void HandleRestartBrowser(const ListValue* args); + + private: + DISALLOW_COPY_AND_ASSIGN(FlagsDOMHandler); +}; + +void FlagsDOMHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("requestFlagsExperiments", + NewCallback(this, &FlagsDOMHandler::HandleRequestFlagsExperiments)); + web_ui_->RegisterMessageCallback("enableFlagsExperiment", + NewCallback(this, &FlagsDOMHandler::HandleEnableFlagsExperimentMessage)); + web_ui_->RegisterMessageCallback("restartBrowser", + NewCallback(this, &FlagsDOMHandler::HandleRestartBrowser)); +} + +void FlagsDOMHandler::HandleRequestFlagsExperiments(const ListValue* args) { + DictionaryValue results; + results.Set("flagsExperiments", + about_flags::GetFlagsExperimentsData( + g_browser_process->local_state())); + results.SetBoolean("needsRestart", + about_flags::IsRestartNeededToCommitChanges()); + web_ui_->CallJavascriptFunction(L"returnFlagsExperiments", results); +} + +void FlagsDOMHandler::HandleEnableFlagsExperimentMessage( + const ListValue* args) { + DCHECK_EQ(2u, args->GetSize()); + if (args->GetSize() != 2) + return; + + std::string experiment_internal_name; + std::string enable_str; + if (!args->GetString(0, &experiment_internal_name) || + !args->GetString(1, &enable_str)) + return; + + about_flags::SetExperimentEnabled( + g_browser_process->local_state(), + experiment_internal_name, + enable_str == "true"); +} + +void FlagsDOMHandler::HandleRestartBrowser(const ListValue* args) { +#if !defined(OS_CHROMEOS) + // Set the flag to restore state after the restart. + PrefService* pref_service = g_browser_process->local_state(); + pref_service->SetBoolean(prefs::kRestartLastSessionOnShutdown, true); + BrowserList::CloseAllBrowsersAndExit(); +#else + // For CrOS instead of browser restart (which is not supported) perform a full + // sign out. Session will be only restored is user has that setting set. + // Same session restore behavior happens in case of full restart after update. + BrowserList::GetLastActive()->Exit(); +#endif +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// FlagsUI +// +/////////////////////////////////////////////////////////////////////////////// + +FlagsUI::FlagsUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new FlagsDOMHandler())->Attach(this)); + + FlagsUIHTMLSource* html_source = new FlagsUIHTMLSource(); + + // Set up the about:flags source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +RefCountedMemory* FlagsUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_FLAGS); +} + +// static +void FlagsUI::RegisterPrefs(PrefService* prefs) { + prefs->RegisterListPref(prefs::kEnabledLabsExperiments); +} diff --git a/chrome/browser/ui/webui/flags_ui.h b/chrome/browser/ui/webui/flags_ui.h new file mode 100644 index 0000000..97c5fec --- /dev/null +++ b/chrome/browser/ui/webui/flags_ui.h @@ -0,0 +1,25 @@ +// 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 CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class PrefService; +class RefCountedMemory; + +class FlagsUI : public WebUI { + public: + explicit FlagsUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + static void RegisterPrefs(PrefService* prefs); + + private: + DISALLOW_COPY_AND_ASSIGN(FlagsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_FLAGS_UI_H_ diff --git a/chrome/browser/ui/webui/gpu_internals_ui.cc b/chrome/browser/ui/webui/gpu_internals_ui.cc new file mode 100644 index 0000000..69cc2b7 --- /dev/null +++ b/chrome/browser/ui/webui/gpu_internals_ui.cc @@ -0,0 +1,405 @@ +// 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/ui/webui/gpu_internals_ui.h" + +#include <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/gpu_data_manager.h" +#include "chrome/browser/gpu_process_host.h" +#include "chrome/browser/gpu_process_host_ui_shim.h" +#include "chrome/browser/io_thread.h" +#include "chrome/browser/net/chrome_net_log.h" +#include "chrome/browser/net/connection_tester.h" +#include "chrome/browser/net/passive_log_collector.h" +#include "chrome/browser/net/url_fixer_upper.h" +#include "chrome/browser/platform_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_version_info.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" +#include "net/base/escape.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +class GpuHTMLSource : public ChromeURLDataManager::DataSource { + public: + GpuHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + ~GpuHTMLSource() {} + DISALLOW_COPY_AND_ASSIGN(GpuHTMLSource); +}; + +// This class receives javascript messages from the renderer. +// Note that the WebUI infrastructure runs on the UI thread, therefore all of +// this class's methods are expected to run on the UI thread. +class GpuMessageHandler + : public WebUIMessageHandler, + public base::SupportsWeakPtr<GpuMessageHandler> { + public: + GpuMessageHandler(); + virtual ~GpuMessageHandler(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Mesages + void OnCallAsync(const ListValue* list); + void OnRequestGpuInfo(const ListValue* list); + + // Submessages dispatched from OnCallAsync + Value* OnRequestClientInfo(const ListValue* list); + Value* OnRequestLogMessages(const ListValue* list); + + // GPUInfo collected callback. + void OnGpuInfoCollected(); + + // Executes the javascript function |function_name| in the renderer, passing + // it the argument |value|. + void CallJavascriptFunction(const std::wstring& function_name, + const Value* value); + + private: + DISALLOW_COPY_AND_ASSIGN(GpuMessageHandler); + + // Cache the Singleton for efficiency. + GpuDataManager* gpu_data_manager_; + + Callback0::Type* gpu_info_update_callback_; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// GpuHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +GpuHTMLSource::GpuHTMLSource() + : DataSource(chrome::kChromeUIGpuInternalsHost, MessageLoop::current()) { +} + +void GpuHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + SetFontAndTextDirection(&localized_strings); + + base::StringPiece gpu_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_GPU_INTERNALS_HTML)); + std::string full_html(gpu_html.data(), gpu_html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + + 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); +} + +std::string GpuHTMLSource::GetMimeType(const std::string&) const { + return "text/html"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// GpuMessageHandler +// +//////////////////////////////////////////////////////////////////////////////// + +GpuMessageHandler::GpuMessageHandler() : gpu_info_update_callback_(NULL) { + gpu_data_manager_ = GpuDataManager::GetInstance(); + DCHECK(gpu_data_manager_); +} + +GpuMessageHandler::~GpuMessageHandler() { + if (gpu_info_update_callback_) { + gpu_data_manager_->RemoveGpuInfoUpdateCallback(gpu_info_update_callback_); + delete gpu_info_update_callback_; + } +} + +WebUIMessageHandler* GpuMessageHandler::Attach(WebUI* web_ui) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui); + return result; +} + +/* BrowserBridge.callAsync prepends a requestID to these messages. */ +void GpuMessageHandler::RegisterMessages() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + web_ui_->RegisterMessageCallback( + "callAsync", + NewCallback(this, &GpuMessageHandler::OnCallAsync)); + web_ui_->RegisterMessageCallback( + "requestGpuInfo", + NewCallback(this, &GpuMessageHandler::OnRequestGpuInfo)); +} + +void GpuMessageHandler::OnCallAsync(const ListValue* args) { + DCHECK_GE(args->GetSize(), static_cast<size_t>(2)); + // unpack args into requestId, submessage and submessageArgs + bool ok; + Value* requestId; + ok = args->Get(0, &requestId); + DCHECK(ok); + + std::string submessage; + ok = args->GetString(1, &submessage); + DCHECK(ok); + + ListValue* submessageArgs = new ListValue(); + for (size_t i = 2; i < args->GetSize(); ++i) { + Value* arg; + ok = args->Get(i, &arg); + DCHECK(ok); + + Value* argCopy = arg->DeepCopy(); + submessageArgs->Append(argCopy); + } + + // call the submessage handler + Value* ret = NULL; + if (submessage == "requestClientInfo") { + ret = OnRequestClientInfo(submessageArgs); + } else if (submessage == "requestLogMessages") { + ret = OnRequestLogMessages(submessageArgs); + } else { // unrecognized submessage + NOTREACHED(); + delete submessageArgs; + return; + } + delete submessageArgs; + + // call BrowserBridge.onCallAsyncReply with result + if (ret) { + web_ui_->CallJavascriptFunction(L"browserBridge.onCallAsyncReply", + *requestId, + *ret); + delete ret; + } else { + web_ui_->CallJavascriptFunction(L"browserBridge.onCallAsyncReply", + *requestId); + } +} + +void GpuMessageHandler::OnRequestGpuInfo(const ListValue* args) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (gpu_info_update_callback_ == NULL) { + // Add us to be called when GPUInfo changes and ask for updated GPUInfo. + gpu_info_update_callback_ = + NewCallback(this, &GpuMessageHandler::OnGpuInfoCollected); + gpu_data_manager_->AddGpuInfoUpdateCallback(gpu_info_update_callback_); + // Run callback immediately in case the info is ready and no update in the + // future. + OnGpuInfoCollected(); + } + GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::GetForRenderer(0); + if (ui_shim) + ui_shim->CollectGpuInfoAsynchronously(GPUInfo::kComplete); +} + +Value* GpuMessageHandler::OnRequestClientInfo(const ListValue* list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + DictionaryValue* dict = new DictionaryValue(); + + chrome::VersionInfo version_info; + + if (!version_info.is_valid()) { + DLOG(ERROR) << "Unable to create chrome::VersionInfo"; + } else { + // We have everything we need to send the right values. + dict->SetString("version", version_info.Version()); + dict->SetString("cl", version_info.LastChange()); + dict->SetString("version_mod", + platform_util::GetVersionStringModifier()); + dict->SetString("official", + l10n_util::GetStringUTF16( + version_info.IsOfficialBuild() ? + IDS_ABOUT_VERSION_OFFICIAL + : IDS_ABOUT_VERSION_UNOFFICIAL)); + + dict->SetString("command_line", + CommandLine::ForCurrentProcess()->command_line_string()); + } + + return dict; +} + +DictionaryValue* NewDescriptionValuePair(const std::string& desc, + const std::string& value) { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("description", desc); + dict->SetString("value", value); + return dict; +} + +DictionaryValue* NewDescriptionValuePair(const std::string& desc, + Value* value) { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("description", desc); + dict->Set("value", value); + return dict; +} + +#if defined(OS_WIN) +// Output DxDiagNode tree as nested array of {description,value} pairs +ListValue* DxDiagNodeToList(const DxDiagNode& node) { + ListValue* list = new ListValue(); + for (std::map<std::string, std::string>::const_iterator it = + node.values.begin(); + it != node.values.end(); + ++it) { + list->Append(NewDescriptionValuePair(it->first, it->second)); + } + + for (std::map<std::string, DxDiagNode>::const_iterator it = + node.children.begin(); + it != node.children.end(); + ++it) { + ListValue* sublist = DxDiagNodeToList(it->second); + list->Append(NewDescriptionValuePair(it->first, sublist)); + } + return list; +} + +#endif // OS_WIN + +std::string VersionNumberToString(uint32 value) { + int hi = (value >> 8) & 0xff; + int low = value & 0xff; + return base::IntToString(hi) + "." + base::IntToString(low); +} + +DictionaryValue* GpuInfoToDict(const GPUInfo& gpu_info) { + ListValue* basic_info = new ListValue(); + basic_info->Append(NewDescriptionValuePair("Initialization time", + base::Int64ToString(gpu_info.initialization_time().InMilliseconds()))); + basic_info->Append(NewDescriptionValuePair("Vendor Id", + base::StringPrintf("0x%04x", gpu_info.vendor_id()))); + basic_info->Append(NewDescriptionValuePair("Device Id", + base::StringPrintf("0x%04x", gpu_info.device_id()))); + basic_info->Append(NewDescriptionValuePair("Driver vendor", + gpu_info.driver_vendor())); + basic_info->Append(NewDescriptionValuePair("Driver version", + gpu_info.driver_version())); + basic_info->Append(NewDescriptionValuePair("Pixel shader version", + VersionNumberToString(gpu_info.pixel_shader_version()))); + basic_info->Append(NewDescriptionValuePair("Vertex shader version", + VersionNumberToString(gpu_info.vertex_shader_version()))); + basic_info->Append(NewDescriptionValuePair("GL version", + VersionNumberToString(gpu_info.gl_version()))); + basic_info->Append(NewDescriptionValuePair("GL_VENDOR", + gpu_info.gl_vendor())); + basic_info->Append(NewDescriptionValuePair("GL_RENDERER", + gpu_info.gl_renderer())); + basic_info->Append(NewDescriptionValuePair("GL_VERSION", + gpu_info.gl_version_string())); + basic_info->Append(NewDescriptionValuePair("GL_EXTENSIONS", + gpu_info.gl_extensions())); + + DictionaryValue* info = new DictionaryValue(); + info->Set("basic_info", basic_info); + + if (gpu_info.level() == GPUInfo::kPartial) { + info->SetString("level", "partial"); + } else if (gpu_info.level() == GPUInfo::kCompleting) { + info->SetString("level", "completing"); + } else if (gpu_info.level() == GPUInfo::kComplete) { + info->SetString("level", "complete"); + } else { + DCHECK(false) << "Unrecognized GPUInfo::Level value"; + info->SetString("level", ""); + } + +#if defined(OS_WIN) + if (gpu_info.level() == GPUInfo::kComplete) { + ListValue* dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics()); + info->Set("diagnostics", dx_info); + } +#endif + + return info; +} + +Value* GpuMessageHandler::OnRequestLogMessages(const ListValue*) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::GetForRenderer(0); + if (!ui_shim) + return NULL; + + return ui_shim->log_messages(); +} + +void GpuMessageHandler::OnGpuInfoCollected() { + // Get GPU Info. + const GPUInfo& gpu_info = gpu_data_manager_->gpu_info(); + + if (gpu_info.level() == GPUInfo::kUninitialized) + return; + + // Send GPU Info to javascript. + Value* gpuInfoVal = GpuInfoToDict(gpu_info); + web_ui_->CallJavascriptFunction(L"browserBridge.onGpuInfoUpdated", + *gpuInfoVal); + + delete gpuInfoVal; +} + +} // namespace + + +//////////////////////////////////////////////////////////////////////////////// +// +// GpuInternalsUI +// +//////////////////////////////////////////////////////////////////////////////// + +GpuInternalsUI::GpuInternalsUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new GpuMessageHandler())->Attach(this)); + + GpuHTMLSource* html_source = new GpuHTMLSource(); + + // Set up the chrome://gpu/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + diff --git a/chrome/browser/ui/webui/gpu_internals_ui.h b/chrome/browser/ui/webui/gpu_internals_ui.h new file mode 100644 index 0000000..5e7d841 --- /dev/null +++ b/chrome/browser/ui/webui/gpu_internals_ui.h @@ -0,0 +1,20 @@ +// 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 CHROME_BROWSER_UI_WEBUI_GPU_INTERNALS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_GPU_INTERNALS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class GpuInternalsUI : public WebUI { + public: + explicit GpuInternalsUI(TabContents* contents); + + private: + DISALLOW_COPY_AND_ASSIGN(GpuInternalsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_GPU_INTERNALS_UI_H_ + diff --git a/chrome/browser/ui/webui/history2_ui.cc b/chrome/browser/ui/webui/history2_ui.cc new file mode 100644 index 0000000..fcdce94 --- /dev/null +++ b/chrome/browser/ui/webui/history2_ui.cc @@ -0,0 +1,413 @@ +// 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/ui/webui/history2_ui.h" + +#include <algorithm> +#include <set> + +#include "base/callback.h" +#include "base/i18n/time_formatting.h" +#include "base/message_loop.h" +#include "base/singleton.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/webui/favicon_source.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/time_format.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "grit/theme_resources.h" +#include "net/base/escape.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +// Maximum number of search results to return in a given search. We should +// eventually remove this. +static const int kMaxSearchResults = 100; + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUIHTMLSource2::HistoryUIHTMLSource2() + : DataSource(chrome::kChromeUIHistory2Host, MessageLoop::current()) { +} + +void HistoryUIHTMLSource2::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString("loading", + l10n_util::GetStringUTF16(IDS_HISTORY_LOADING)); + localized_strings.SetString("title", + l10n_util::GetStringUTF16(IDS_HISTORY_TITLE)); + localized_strings.SetString("loading", + l10n_util::GetStringUTF16(IDS_HISTORY_LOADING)); + localized_strings.SetString("newest", + l10n_util::GetStringUTF16(IDS_HISTORY_NEWEST)); + localized_strings.SetString("newer", + l10n_util::GetStringUTF16(IDS_HISTORY_NEWER)); + localized_strings.SetString("older", + l10n_util::GetStringUTF16(IDS_HISTORY_OLDER)); + localized_strings.SetString("searchresultsfor", + l10n_util::GetStringUTF16(IDS_HISTORY_SEARCHRESULTSFOR)); + localized_strings.SetString("history", + l10n_util::GetStringUTF16(IDS_HISTORY_BROWSERESULTS)); + localized_strings.SetString("cont", + l10n_util::GetStringUTF16(IDS_HISTORY_CONTINUED)); + localized_strings.SetString("searchbutton", + l10n_util::GetStringUTF16(IDS_HISTORY_SEARCH_BUTTON)); + localized_strings.SetString("noresults", + l10n_util::GetStringUTF16(IDS_HISTORY_NO_RESULTS)); + localized_strings.SetString("noitems", + l10n_util::GetStringUTF16(IDS_HISTORY_NO_ITEMS)); + localized_strings.SetString("edithistory", + l10n_util::GetStringUTF16(IDS_HISTORY_START_EDITING_HISTORY)); + localized_strings.SetString("doneediting", + l10n_util::GetStringUTF16(IDS_HISTORY_STOP_EDITING_HISTORY)); + localized_strings.SetString("removeselected", + l10n_util::GetStringUTF16(IDS_HISTORY_REMOVE_SELECTED_ITEMS)); + localized_strings.SetString("clearallhistory", + l10n_util::GetStringUTF16(IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG)); + localized_strings.SetString("deletewarning", + l10n_util::GetStringUTF16(IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING)); + + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece history_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_HISTORY2_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + history_html, &localized_strings); + + 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); +} + +std::string HistoryUIHTMLSource2::GetMimeType(const std::string&) const { + return "text/html"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHandler +// +//////////////////////////////////////////////////////////////////////////////// +BrowsingHistoryHandler2::BrowsingHistoryHandler2() + : search_text_() { +} + +BrowsingHistoryHandler2::~BrowsingHistoryHandler2() { + cancelable_search_consumer_.CancelAllRequests(); + cancelable_delete_consumer_.CancelAllRequests(); +} + +WebUIMessageHandler* BrowsingHistoryHandler2::Attach(WebUI* web_ui) { + // Create our favicon data source. + Profile* profile = web_ui->GetProfile(); + profile->GetChromeURLDataManager()->AddDataSource( + new FavIconSource(profile)); + + // Get notifications when history is cleared. + registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, + Source<Profile>(web_ui->GetProfile()->GetOriginalProfile())); + return WebUIMessageHandler::Attach(web_ui); +} + +void BrowsingHistoryHandler2::RegisterMessages() { + web_ui_->RegisterMessageCallback("getHistory", + NewCallback(this, &BrowsingHistoryHandler2::HandleGetHistory)); + web_ui_->RegisterMessageCallback("searchHistory", + NewCallback(this, &BrowsingHistoryHandler2::HandleSearchHistory)); + web_ui_->RegisterMessageCallback("removeURLsOnOneDay", + NewCallback(this, &BrowsingHistoryHandler2::HandleRemoveURLsOnOneDay)); + web_ui_->RegisterMessageCallback("clearBrowsingData", + NewCallback(this, &BrowsingHistoryHandler2::HandleClearBrowsingData)); +} + +void BrowsingHistoryHandler2::HandleGetHistory(const ListValue* args) { + // Anything in-flight is invalid. + cancelable_search_consumer_.CancelAllRequests(); + + // Get arguments (if any). + int day = 0; + ExtractIntegerValue(args, &day); + + // Set our query options. + history::QueryOptions options; + options.begin_time = base::Time::Now().LocalMidnight(); + options.begin_time -= base::TimeDelta::FromDays(day); + options.end_time = base::Time::Now().LocalMidnight(); + options.end_time -= base::TimeDelta::FromDays(day - 1); + + // Need to remember the query string for our results. + search_text_ = string16(); + + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QueryHistory(search_text_, + options, + &cancelable_search_consumer_, + NewCallback(this, &BrowsingHistoryHandler2::QueryComplete)); +} + +void BrowsingHistoryHandler2::HandleSearchHistory(const ListValue* args) { + // Anything in-flight is invalid. + cancelable_search_consumer_.CancelAllRequests(); + + // Get arguments (if any). + int month = 0; + string16 query; + ExtractSearchHistoryArguments(args, &month, &query); + + // Set the query ranges for the given month. + history::QueryOptions options = CreateMonthQueryOptions(month); + + // When searching, limit the number of results returned. + options.max_count = kMaxSearchResults; + + // Need to remember the query string for our results. + search_text_ = query; + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QueryHistory(search_text_, + options, + &cancelable_search_consumer_, + NewCallback(this, &BrowsingHistoryHandler2::QueryComplete)); +} + +void BrowsingHistoryHandler2::HandleRemoveURLsOnOneDay(const ListValue* args) { + if (cancelable_delete_consumer_.HasPendingRequests()) { + web_ui_->CallJavascriptFunction(L"deleteFailed"); + return; + } + + // Get day to delete data from. + int visit_time = 0; + ExtractIntegerValue(args, &visit_time); + base::Time::Exploded exploded; + base::Time::FromTimeT( + static_cast<time_t>(visit_time)).LocalExplode(&exploded); + exploded.hour = exploded.minute = exploded.second = exploded.millisecond = 0; + base::Time begin_time = base::Time::FromLocalExploded(exploded); + base::Time end_time = begin_time + base::TimeDelta::FromDays(1); + + // Get URLs. + std::set<GURL> urls; + for (ListValue::const_iterator v = args->begin() + 1; + v != args->end(); ++v) { + if ((*v)->GetType() != Value::TYPE_STRING) + continue; + const StringValue* string_value = static_cast<const StringValue*>(*v); + string16 string16_value; + if (!string_value->GetAsString(&string16_value)) + continue; + urls.insert(GURL(string16_value)); + } + + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->ExpireHistoryBetween( + urls, begin_time, end_time, &cancelable_delete_consumer_, + NewCallback(this, &BrowsingHistoryHandler2::RemoveComplete)); +} + +void BrowsingHistoryHandler2::HandleClearBrowsingData(const ListValue* args) { + // TODO(beng): This is an improper direct dependency on Browser. Route this + // through some sort of delegate. + Browser* browser = BrowserList::FindBrowserWithProfile(web_ui_->GetProfile()); + if (browser) + browser->OpenClearBrowsingDataDialog(); +} + +void BrowsingHistoryHandler2::QueryComplete( + HistoryService::Handle request_handle, + history::QueryResults* results) { + + ListValue results_value; + base::Time midnight_today = base::Time::Now().LocalMidnight(); + + for (size_t i = 0; i < results->size(); ++i) { + history::URLResult const &page = (*results)[i]; + DictionaryValue* page_value = new DictionaryValue(); + SetURLAndTitle(page_value, page.title(), page.url()); + + // Need to pass the time in epoch time (fastest JS conversion). + page_value->SetInteger("time", + static_cast<int>(page.visit_time().ToTimeT())); + + // Until we get some JS i18n infrastructure, we also need to + // pass the dates in as strings. This could use some + // optimization. + + // Only pass in the strings we need (search results need a shortdate + // and snippet, browse results need day and time information). + if (search_text_.empty()) { + // Figure out the relative date string. + string16 date_str = TimeFormat::RelativeDate(page.visit_time(), + &midnight_today); + if (date_str.empty()) { + date_str = base::TimeFormatFriendlyDate(page.visit_time()); + } else { + date_str = l10n_util::GetStringFUTF16( + IDS_HISTORY_DATE_WITH_RELATIVE_TIME, + date_str, + base::TimeFormatFriendlyDate(page.visit_time())); + } + page_value->SetString("dateRelativeDay", date_str); + page_value->SetString("dateTimeOfDay", + base::TimeFormatTimeOfDay(page.visit_time())); + } else { + page_value->SetString("dateShort", + base::TimeFormatShortDate(page.visit_time())); + page_value->SetString("snippet", page.snippet().text()); + } + page_value->SetBoolean("starred", + web_ui_->GetProfile()->GetBookmarkModel()->IsBookmarked(page.url())); + results_value.Append(page_value); + } + + DictionaryValue info_value; + info_value.SetString("term", search_text_); + info_value.SetBoolean("finished", results->reached_beginning()); + + web_ui_->CallJavascriptFunction(L"historyResult", info_value, results_value); +} + +void BrowsingHistoryHandler2::RemoveComplete() { + // Some Visits were deleted from history. Reload the list. + web_ui_->CallJavascriptFunction(L"deleteComplete"); +} + +void BrowsingHistoryHandler2::ExtractSearchHistoryArguments( + const ListValue* args, + int* month, + string16* query) { + *month = 0; + Value* list_member; + + // Get search string. + if (args->Get(0, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + string_value->GetAsString(query); + } + + // Get search month. + if (args->Get(1, &list_member) && + list_member->GetType() == Value::TYPE_STRING) { + const StringValue* string_value = + static_cast<const StringValue*>(list_member); + string16 string16_value; + string_value->GetAsString(&string16_value); + base::StringToInt(string16_value, month); + } +} + +history::QueryOptions BrowsingHistoryHandler2::CreateMonthQueryOptions( + int month) { + history::QueryOptions options; + + // Configure the begin point of the search to the start of the + // current month. + base::Time::Exploded exploded; + base::Time::Now().LocalMidnight().LocalExplode(&exploded); + exploded.day_of_month = 1; + + if (month == 0) { + options.begin_time = base::Time::FromLocalExploded(exploded); + + // Set the end time of this first search to null (which will + // show results from the future, should the user's clock have + // been set incorrectly). + options.end_time = base::Time(); + } else { + // Set the end-time of this search to the end of the month that is + // |depth| months before the search end point. The end time is not + // inclusive, so we should feel free to set it to midnight on the + // first day of the following month. + exploded.month -= month - 1; + while (exploded.month < 1) { + exploded.month += 12; + exploded.year--; + } + options.end_time = base::Time::FromLocalExploded(exploded); + + // Set the begin-time of the search to the start of the month + // that is |depth| months prior to search_start_. + if (exploded.month > 1) { + exploded.month--; + } else { + exploded.month = 12; + exploded.year--; + } + options.begin_time = base::Time::FromLocalExploded(exploded); + } + + return options; +} + +void BrowsingHistoryHandler2::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type != NotificationType::HISTORY_URLS_DELETED) { + NOTREACHED(); + return; + } + + // Some URLs were deleted from history. Reload the list. + web_ui_->CallJavascriptFunction(L"historyDeleted"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryUIContents +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUI2::HistoryUI2(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new BrowsingHistoryHandler2())->Attach(this)); + + HistoryUIHTMLSource2* html_source = new HistoryUIHTMLSource2(); + + // Set up the chrome://history2/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +const GURL HistoryUI2::GetHistoryURLWithSearchText(const string16& text) { + return GURL(std::string(chrome::kChromeUIHistory2URL) + "#q=" + + EscapeQueryParamValue(UTF16ToUTF8(text), true)); +} + +// static +RefCountedMemory* HistoryUI2::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_HISTORY_FAVICON); +} diff --git a/chrome/browser/ui/webui/history2_ui.h b/chrome/browser/ui/webui/history2_ui.h new file mode 100644 index 0000000..cf99253 --- /dev/null +++ b/chrome/browser/ui/webui/history2_ui.h @@ -0,0 +1,112 @@ +// 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 CHROME_BROWSER_UI_WEBUI_HISTORY2_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_HISTORY2_UI_H_ +#pragma once + +#include <string> + +#include "base/string16.h" +#include "chrome/browser/cancelable_request.h" +#include "chrome/browser/history/history.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/notification_registrar.h" +#include "content/browser/webui/web_ui.h" + +class GURL; + +// Temporary fork for development of new history UI. +// TODO(pamg): merge back in when new UI is complete. + +class HistoryUIHTMLSource2 : public ChromeURLDataManager::DataSource { + public: + HistoryUIHTMLSource2(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + + virtual std::string GetMimeType(const std::string&) const; + + private: + ~HistoryUIHTMLSource2() {} + + DISALLOW_COPY_AND_ASSIGN(HistoryUIHTMLSource2); +}; + +// The handler for Javascript messages related to the "history" view. +class BrowsingHistoryHandler2 : public WebUIMessageHandler, + public NotificationObserver { + public: + BrowsingHistoryHandler2(); + virtual ~BrowsingHistoryHandler2(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Callback for the "getHistory" message. + void HandleGetHistory(const ListValue* args); + + // Callback for the "searchHistory" message. + void HandleSearchHistory(const ListValue* args); + + // Callback for the "removeURLsOnOneDay" message. + void HandleRemoveURLsOnOneDay(const ListValue* args); + + // Handle for "clearBrowsingData" message. + void HandleClearBrowsingData(const ListValue* args); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // Callback from the history system when the history list is available. + void QueryComplete(HistoryService::Handle request_handle, + history::QueryResults* results); + + // Callback from the history system when visits were deleted. + void RemoveComplete(); + + // Extract the arguments from the call to HandleSearchHistory. + void ExtractSearchHistoryArguments(const ListValue* args, + int* month, + string16* query); + + // Figure out the query options for a month-wide query. + history::QueryOptions CreateMonthQueryOptions(int month); + + NotificationRegistrar registrar_; + + // Current search text. + string16 search_text_; + + // Our consumer for search requests to the history service. + CancelableRequestConsumerT<int, 0> cancelable_search_consumer_; + + // Our consumer for delete requests to the history service. + CancelableRequestConsumerT<int, 0> cancelable_delete_consumer_; + + DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryHandler2); +}; + +class HistoryUI2 : public WebUI { + public: + explicit HistoryUI2(TabContents* contents); + + // Return the URL for a given search term. + static const GURL GetHistoryURLWithSearchText(const string16& text); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(HistoryUI2); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_HISTORY2_UI_H_ diff --git a/chrome/browser/ui/webui/history_ui.cc b/chrome/browser/ui/webui/history_ui.cc new file mode 100644 index 0000000..5cc50da8 --- /dev/null +++ b/chrome/browser/ui/webui/history_ui.cc @@ -0,0 +1,401 @@ +// 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/ui/webui/history_ui.h" + +#include <algorithm> +#include <set> + +#include "base/callback.h" +#include "base/i18n/time_formatting.h" +#include "base/message_loop.h" +#include "base/singleton.h" +#include "base/string16.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_delegate.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/webui/favicon_source.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/time_format.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "grit/theme_resources.h" +#include "net/base/escape.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +// Maximum number of search results to return in a given search. We should +// eventually remove this. +static const int kMaxSearchResults = 100; + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUIHTMLSource::HistoryUIHTMLSource() + : DataSource(chrome::kChromeUIHistoryHost, MessageLoop::current()) { +} + +void HistoryUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + localized_strings.SetString("loading", + l10n_util::GetStringUTF16(IDS_HISTORY_LOADING)); + localized_strings.SetString("title", + l10n_util::GetStringUTF16(IDS_HISTORY_TITLE)); + localized_strings.SetString("loading", + l10n_util::GetStringUTF16(IDS_HISTORY_LOADING)); + localized_strings.SetString("newest", + l10n_util::GetStringUTF16(IDS_HISTORY_NEWEST)); + localized_strings.SetString("newer", + l10n_util::GetStringUTF16(IDS_HISTORY_NEWER)); + localized_strings.SetString("older", + l10n_util::GetStringUTF16(IDS_HISTORY_OLDER)); + localized_strings.SetString("searchresultsfor", + l10n_util::GetStringUTF16(IDS_HISTORY_SEARCHRESULTSFOR)); + localized_strings.SetString("history", + l10n_util::GetStringUTF16(IDS_HISTORY_BROWSERESULTS)); + localized_strings.SetString("cont", + l10n_util::GetStringUTF16(IDS_HISTORY_CONTINUED)); + localized_strings.SetString("searchbutton", + l10n_util::GetStringUTF16(IDS_HISTORY_SEARCH_BUTTON)); + localized_strings.SetString("noresults", + l10n_util::GetStringUTF16(IDS_HISTORY_NO_RESULTS)); + localized_strings.SetString("noitems", + l10n_util::GetStringUTF16(IDS_HISTORY_NO_ITEMS)); + localized_strings.SetString("edithistory", + l10n_util::GetStringUTF16(IDS_HISTORY_START_EDITING_HISTORY)); + localized_strings.SetString("doneediting", + l10n_util::GetStringUTF16(IDS_HISTORY_STOP_EDITING_HISTORY)); + localized_strings.SetString("removeselected", + l10n_util::GetStringUTF16(IDS_HISTORY_REMOVE_SELECTED_ITEMS)); + localized_strings.SetString("clearallhistory", + l10n_util::GetStringUTF16(IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG)); + localized_strings.SetString("deletewarning", + l10n_util::GetStringUTF16(IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING)); + + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece history_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_HISTORY_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + history_html, &localized_strings); + + 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); +} + +std::string HistoryUIHTMLSource::GetMimeType(const std::string&) const { + return "text/html"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryHandler +// +//////////////////////////////////////////////////////////////////////////////// +BrowsingHistoryHandler::BrowsingHistoryHandler() + : search_text_() { +} + +BrowsingHistoryHandler::~BrowsingHistoryHandler() { + cancelable_search_consumer_.CancelAllRequests(); + cancelable_delete_consumer_.CancelAllRequests(); +} + +WebUIMessageHandler* BrowsingHistoryHandler::Attach(WebUI* web_ui) { + // Create our favicon data source. + Profile* profile = web_ui->GetProfile(); + profile->GetChromeURLDataManager()->AddDataSource( + new FavIconSource(profile)); + + // Get notifications when history is cleared. + registrar_.Add(this, NotificationType::HISTORY_URLS_DELETED, + Source<Profile>(profile->GetOriginalProfile())); + return WebUIMessageHandler::Attach(web_ui); +} + +void BrowsingHistoryHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("getHistory", + NewCallback(this, &BrowsingHistoryHandler::HandleGetHistory)); + web_ui_->RegisterMessageCallback("searchHistory", + NewCallback(this, &BrowsingHistoryHandler::HandleSearchHistory)); + web_ui_->RegisterMessageCallback("removeURLsOnOneDay", + NewCallback(this, &BrowsingHistoryHandler::HandleRemoveURLsOnOneDay)); + web_ui_->RegisterMessageCallback("clearBrowsingData", + NewCallback(this, &BrowsingHistoryHandler::HandleClearBrowsingData)); +} + +void BrowsingHistoryHandler::HandleGetHistory(const ListValue* args) { + // Anything in-flight is invalid. + cancelable_search_consumer_.CancelAllRequests(); + + // Get arguments (if any). + int day = 0; + ExtractIntegerValue(args, &day); + + // Set our query options. + history::QueryOptions options; + options.begin_time = base::Time::Now().LocalMidnight(); + options.begin_time -= base::TimeDelta::FromDays(day); + options.end_time = base::Time::Now().LocalMidnight(); + options.end_time -= base::TimeDelta::FromDays(day - 1); + + // Need to remember the query string for our results. + search_text_ = string16(); + + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QueryHistory(search_text_, + options, + &cancelable_search_consumer_, + NewCallback(this, &BrowsingHistoryHandler::QueryComplete)); +} + +void BrowsingHistoryHandler::HandleSearchHistory(const ListValue* args) { + // Anything in-flight is invalid. + cancelable_search_consumer_.CancelAllRequests(); + + // Get arguments (if any). + int month = 0; + string16 query; + ExtractSearchHistoryArguments(args, &month, &query); + + // Set the query ranges for the given month. + history::QueryOptions options = CreateMonthQueryOptions(month); + + // When searching, limit the number of results returned. + options.max_count = kMaxSearchResults; + + // Need to remember the query string for our results. + search_text_ = query; + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->QueryHistory(search_text_, + options, + &cancelable_search_consumer_, + NewCallback(this, &BrowsingHistoryHandler::QueryComplete)); +} + +void BrowsingHistoryHandler::HandleRemoveURLsOnOneDay(const ListValue* args) { + if (cancelable_delete_consumer_.HasPendingRequests()) { + web_ui_->CallJavascriptFunction(L"deleteFailed"); + return; + } + + // Get day to delete data from. + int visit_time = 0; + ExtractIntegerValue(args, &visit_time); + base::Time::Exploded exploded; + base::Time::FromTimeT( + static_cast<time_t>(visit_time)).LocalExplode(&exploded); + exploded.hour = exploded.minute = exploded.second = exploded.millisecond = 0; + base::Time begin_time = base::Time::FromLocalExploded(exploded); + base::Time end_time = begin_time + base::TimeDelta::FromDays(1); + + // Get URLs. + std::set<GURL> urls; + for (ListValue::const_iterator v = args->begin() + 1; + v != args->end(); ++v) { + if ((*v)->GetType() != Value::TYPE_STRING) + continue; + const StringValue* string_value = static_cast<const StringValue*>(*v); + string16 string16_value; + if (!string_value->GetAsString(&string16_value)) + continue; + urls.insert(GURL(string16_value)); + } + + HistoryService* hs = + web_ui_->GetProfile()->GetHistoryService(Profile::EXPLICIT_ACCESS); + hs->ExpireHistoryBetween( + urls, begin_time, end_time, &cancelable_delete_consumer_, + NewCallback(this, &BrowsingHistoryHandler::RemoveComplete)); +} + +void BrowsingHistoryHandler::HandleClearBrowsingData(const ListValue* args) { + // TODO(beng): This is an improper direct dependency on Browser. Route this + // through some sort of delegate. + Browser* browser = BrowserList::FindBrowserWithProfile(web_ui_->GetProfile()); + if (browser) + browser->OpenClearBrowsingDataDialog(); +} + +void BrowsingHistoryHandler::QueryComplete( + HistoryService::Handle request_handle, + history::QueryResults* results) { + + ListValue results_value; + base::Time midnight_today = base::Time::Now().LocalMidnight(); + + for (size_t i = 0; i < results->size(); ++i) { + history::URLResult const &page = (*results)[i]; + DictionaryValue* page_value = new DictionaryValue(); + SetURLAndTitle(page_value, page.title(), page.url()); + + // Need to pass the time in epoch time (fastest JS conversion). + page_value->SetInteger("time", + static_cast<int>(page.visit_time().ToTimeT())); + + // Until we get some JS i18n infrastructure, we also need to + // pass the dates in as strings. This could use some + // optimization. + + // Only pass in the strings we need (search results need a shortdate + // and snippet, browse results need day and time information). + if (search_text_.empty()) { + // Figure out the relative date string. + string16 date_str = TimeFormat::RelativeDate(page.visit_time(), + &midnight_today); + if (date_str.empty()) { + date_str = base::TimeFormatFriendlyDate(page.visit_time()); + } else { + date_str = l10n_util::GetStringFUTF16( + IDS_HISTORY_DATE_WITH_RELATIVE_TIME, + date_str, + base::TimeFormatFriendlyDate(page.visit_time())); + } + page_value->SetString("dateRelativeDay", date_str); + page_value->SetString("dateTimeOfDay", + base::TimeFormatTimeOfDay(page.visit_time())); + } else { + page_value->SetString("dateShort", + base::TimeFormatShortDate(page.visit_time())); + page_value->SetString("snippet", page.snippet().text()); + } + page_value->SetBoolean("starred", + web_ui_->GetProfile()->GetBookmarkModel()->IsBookmarked(page.url())); + results_value.Append(page_value); + } + + DictionaryValue info_value; + info_value.SetString("term", search_text_); + info_value.SetBoolean("finished", results->reached_beginning()); + + web_ui_->CallJavascriptFunction(L"historyResult", info_value, results_value); +} + +void BrowsingHistoryHandler::RemoveComplete() { + // Some Visits were deleted from history. Reload the list. + web_ui_->CallJavascriptFunction(L"deleteComplete"); +} + +void BrowsingHistoryHandler::ExtractSearchHistoryArguments( + const ListValue* args, + int* month, + string16* query) { + CHECK(args->GetSize() == 2); + query->clear(); + CHECK(args->GetString(0, query)); + + string16 string16_value; + CHECK(args->GetString(1, &string16_value)); + *month = 0; + base::StringToInt(string16_value, month); +} + +history::QueryOptions BrowsingHistoryHandler::CreateMonthQueryOptions( + int month) { + history::QueryOptions options; + + // Configure the begin point of the search to the start of the + // current month. + base::Time::Exploded exploded; + base::Time::Now().LocalMidnight().LocalExplode(&exploded); + exploded.day_of_month = 1; + + if (month == 0) { + options.begin_time = base::Time::FromLocalExploded(exploded); + + // Set the end time of this first search to null (which will + // show results from the future, should the user's clock have + // been set incorrectly). + options.end_time = base::Time(); + } else { + // Set the end-time of this search to the end of the month that is + // |depth| months before the search end point. The end time is not + // inclusive, so we should feel free to set it to midnight on the + // first day of the following month. + exploded.month -= month - 1; + while (exploded.month < 1) { + exploded.month += 12; + exploded.year--; + } + options.end_time = base::Time::FromLocalExploded(exploded); + + // Set the begin-time of the search to the start of the month + // that is |depth| months prior to search_start_. + if (exploded.month > 1) { + exploded.month--; + } else { + exploded.month = 12; + exploded.year--; + } + options.begin_time = base::Time::FromLocalExploded(exploded); + } + + return options; +} + +void BrowsingHistoryHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type != NotificationType::HISTORY_URLS_DELETED) { + NOTREACHED(); + return; + } + + // Some URLs were deleted from history. Reload the list. + web_ui_->CallJavascriptFunction(L"historyDeleted"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// HistoryUIContents +// +//////////////////////////////////////////////////////////////////////////////// + +HistoryUI::HistoryUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new BrowsingHistoryHandler())->Attach(this)); + + HistoryUIHTMLSource* html_source = new HistoryUIHTMLSource(); + + // Set up the chrome://history/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +// static +const GURL HistoryUI::GetHistoryURLWithSearchText(const string16& text) { + return GURL(std::string(chrome::kChromeUIHistoryURL) + "#q=" + + EscapeQueryParamValue(UTF16ToUTF8(text), true)); +} + +// static +RefCountedMemory* HistoryUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_HISTORY_FAVICON); +} diff --git a/chrome/browser/ui/webui/history_ui.h b/chrome/browser/ui/webui/history_ui.h new file mode 100644 index 0000000..0513fbe --- /dev/null +++ b/chrome/browser/ui/webui/history_ui.h @@ -0,0 +1,108 @@ +// 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 CHROME_BROWSER_UI_WEBUI_HISTORY_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_HISTORY_UI_H_ +#pragma once + +#include <string> + +#include "base/string16.h" +#include "chrome/browser/cancelable_request.h" +#include "chrome/browser/history/history.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/notification_registrar.h" +#include "content/browser/webui/web_ui.h" + +class GURL; + +class HistoryUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + HistoryUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + ~HistoryUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(HistoryUIHTMLSource); +}; + +// The handler for Javascript messages related to the "history" view. +class BrowsingHistoryHandler : public WebUIMessageHandler, + public NotificationObserver { + public: + BrowsingHistoryHandler(); + virtual ~BrowsingHistoryHandler(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Callback for the "getHistory" message. + void HandleGetHistory(const ListValue* args); + + // Callback for the "searchHistory" message. + void HandleSearchHistory(const ListValue* args); + + // Callback for the "removeURLsOnOneDay" message. + void HandleRemoveURLsOnOneDay(const ListValue* args); + + // Handle for "clearBrowsingData" message. + void HandleClearBrowsingData(const ListValue* args); + + // NotificationObserver implementation. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // Callback from the history system when the history list is available. + void QueryComplete(HistoryService::Handle request_handle, + history::QueryResults* results); + + // Callback from the history system when visits were deleted. + void RemoveComplete(); + + // Extract the arguments from the call to HandleSearchHistory. + void ExtractSearchHistoryArguments(const ListValue* args, + int* month, + string16* query); + + // Figure out the query options for a month-wide query. + history::QueryOptions CreateMonthQueryOptions(int month); + + NotificationRegistrar registrar_; + + // Current search text. + string16 search_text_; + + // Our consumer for search requests to the history service. + CancelableRequestConsumerT<int, 0> cancelable_search_consumer_; + + // Our consumer for delete requests to the history service. + CancelableRequestConsumerT<int, 0> cancelable_delete_consumer_; + + DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryHandler); +}; + +class HistoryUI : public WebUI { + public: + explicit HistoryUI(TabContents* contents); + + // Return the URL for a given search term. + static const GURL GetHistoryURLWithSearchText(const string16& text); + + static RefCountedMemory* GetFaviconResourceBytes(); + + private: + DISALLOW_COPY_AND_ASSIGN(HistoryUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_HISTORY_UI_H_ diff --git a/chrome/browser/ui/webui/html_dialog_ui.cc b/chrome/browser/ui/webui/html_dialog_ui.cc new file mode 100644 index 0000000..ff07d7f --- /dev/null +++ b/chrome/browser/ui/webui/html_dialog_ui.cc @@ -0,0 +1,88 @@ +// 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/ui/webui/html_dialog_ui.h" + +#include "base/callback.h" +#include "base/lazy_instance.h" +#include "base/values.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/bindings_policy.h" +#include "content/browser/webui/web_ui_util.h" + +static base::LazyInstance<PropertyAccessor<HtmlDialogUIDelegate*> > + g_html_dialog_ui_property_accessor(base::LINKER_INITIALIZED); + +HtmlDialogUI::HtmlDialogUI(TabContents* tab_contents) : WebUI(tab_contents) { +} + +HtmlDialogUI::~HtmlDialogUI() { + // Don't unregister our property. During the teardown of the TabContents, + // this will be deleted, but the TabContents will already be destroyed. + // + // This object is owned indirectly by the TabContents. WebUIs can change, so + // it's scary if this WebUI is changed out and replaced with something else, + // since the property will still point to the old delegate. But the delegate + // is itself the owner of the TabContents for a dialog so will be in scope, + // and the HTML dialogs won't swap WebUIs anyway since they don't navigate. +} + +// static +PropertyAccessor<HtmlDialogUIDelegate*>& HtmlDialogUI::GetPropertyAccessor() { + return g_html_dialog_ui_property_accessor.Get(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Private: + +void HtmlDialogUI::RenderViewCreated(RenderViewHost* render_view_host) { + // 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, &HtmlDialogUI::OnDialogClosed)); + + // Pass the arguments to the renderer supplied by the delegate. + std::string dialog_args; + std::vector<WebUIMessageHandler*> handlers; + HtmlDialogUIDelegate** delegate = GetPropertyAccessor().GetProperty( + tab_contents()->property_bag()); + if (delegate) { + dialog_args = (*delegate)->GetDialogArgs(); + (*delegate)->GetWebUIMessageHandlers(&handlers); + } + + if (0 != (bindings_ & BindingsPolicy::WEB_UI)) + render_view_host->SetWebUIProperty("dialogArguments", dialog_args); + for (std::vector<WebUIMessageHandler*>::iterator it = handlers.begin(); + it != handlers.end(); ++it) { + (*it)->Attach(this); + AddMessageHandler(*it); + } +} + +void HtmlDialogUI::OnDialogClosed(const ListValue* args) { + HtmlDialogUIDelegate** delegate = GetPropertyAccessor().GetProperty( + tab_contents()->property_bag()); + if (delegate) { + (*delegate)->OnDialogClosed( + web_ui_util::GetJsonResponseFromFirstArgumentInList(args)); + } +} + +ExternalHtmlDialogUI::ExternalHtmlDialogUI(TabContents* tab_contents) + : HtmlDialogUI(tab_contents) { + // Non-file based UI needs to not have access to the Web UI bindings + // for security reasons. The code hosting the dialog should provide + // dialog specific functionality through other bindings and methods + // that are scoped in duration to the dialogs existence. + bindings_ &= ~BindingsPolicy::WEB_UI; +} + +ExternalHtmlDialogUI::~ExternalHtmlDialogUI() { +} + +bool HtmlDialogUIDelegate::HandleContextMenu(const ContextMenuParams& params) { + return false; +} diff --git a/chrome/browser/ui/webui/html_dialog_ui.h b/chrome/browser/ui/webui/html_dialog_ui.h new file mode 100644 index 0000000..6ffa4f6 --- /dev/null +++ b/chrome/browser/ui/webui/html_dialog_ui.h @@ -0,0 +1,120 @@ +// 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 CHROME_BROWSER_UI_WEBUI_HTML_DIALOG_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_HTML_DIALOG_UI_H_ +#pragma once + +#include <string> +#include <vector> + +#include "chrome/common/property_bag.h" +#include "content/browser/webui/web_ui.h" +#include "googleurl/src/gurl.h" + +namespace gfx { +class Size; +} + +struct ContextMenuParams; + +// Implement this class to receive notifications. +class HtmlDialogUIDelegate { + public: + // Returns true if the contents needs to be run in a modal dialog. + virtual bool IsDialogModal() const = 0; + + // Returns the title of the dialog. + virtual std::wstring GetDialogTitle() const = 0; + + // Get the HTML file path for the content to load in the dialog. + virtual GURL GetDialogContentURL() const = 0; + + // Get WebUIMessageHandler objects to handle messages from the HTML/JS page. + // The handlers are used to send and receive messages from the page while it + // is still open. Ownership of each handler is taken over by the WebUI + // hosting the page. + virtual void GetWebUIMessageHandlers( + std::vector<WebUIMessageHandler*>* handlers) const = 0; + + // Get the size of the dialog. + virtual void GetDialogSize(gfx::Size* 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; + + // A callback to notify the delegate that the contents have gone + // away. Only relevant if your dialog hosts code that calls + // windows.close() and you've allowed that. If the output parameter + // is set to true, then the dialog is closed. The default is false. + virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) = 0; + + // A callback to allow the delegate to dictate that the window should not + // have a title bar. This is useful when presenting branded interfaces. + virtual bool ShouldShowDialogTitle() const = 0; + + // A callback to allow the delegate to inhibit context menu or show + // customized menu. + virtual bool HandleContextMenu(const ContextMenuParams& params); + + protected: + virtual ~HtmlDialogUIDelegate() {} +}; + +// Displays file URL contents inside a modal HTML dialog. +// +// This application really should not use TabContents + WebUI. It should instead +// just embed a RenderView in a dialog and be done with it. +// +// Before loading a URL corresponding to this WebUI, the caller should set its +// delegate as a property on the TabContents. This WebUI will pick it up from +// there and call it back. This is a bit of a hack to allow the dialog to pass +// its delegate to the Web UI without having nasty accessors on the TabContents. +// The correct design using RVH directly would avoid all of this. +class HtmlDialogUI : public WebUI { + 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; + }; + + // When created, the property should already be set on the TabContents. + explicit HtmlDialogUI(TabContents* tab_contents); + virtual ~HtmlDialogUI(); + + // Returns the PropertyBag accessor object used to write the delegate pointer + // into the TabContents (see class-level comment above). + static PropertyAccessor<HtmlDialogUIDelegate*>& GetPropertyAccessor(); + + private: + // WebUI + virtual void RenderViewCreated(RenderViewHost* render_view_host); + + // JS message handler. + void OnDialogClosed(const ListValue* args); + + DISALLOW_COPY_AND_ASSIGN(HtmlDialogUI); +}; + +// Displays external URL contents inside a modal HTML dialog. +// +// Intended to be the place to collect the settings and lockdowns +// necessary for running external UI conponents securely (e.g., the +// cloud print dialog). +class ExternalHtmlDialogUI : public HtmlDialogUI { + public: + explicit ExternalHtmlDialogUI(TabContents* tab_contents); + virtual ~ExternalHtmlDialogUI(); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_HTML_DIALOG_UI_H_ diff --git a/chrome/browser/ui/webui/keyboard_ui.cc b/chrome/browser/ui/webui/keyboard_ui.cc new file mode 100644 index 0000000..af7445c --- /dev/null +++ b/chrome/browser/ui/webui/keyboard_ui.cc @@ -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. + +#include "chrome/browser/ui/webui/keyboard_ui.h" + +#include "base/ref_counted_memory.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/url_constants.h" +#include "ui/base/resource/resource_bundle.h" + +/////////////////////////////////////////////////////////////////////////////// +// KeyboardUI + +KeyboardUI::KeyboardUI(TabContents* contents) + : WebUI(contents) { + KeyboardHTMLSource* html_source = new KeyboardHTMLSource(); + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + +KeyboardUI::~KeyboardUI() { +} + +/////////////////////////////////////////////////////////////////////////////// +// KeyboardHTMLSource + +KeyboardUI::KeyboardHTMLSource::KeyboardHTMLSource() + : DataSource(chrome::kChromeUIKeyboardHost, MessageLoop::current()) { +} + +void KeyboardUI::KeyboardHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + NOTREACHED() << "We should never get here since the extension should have" + << "been triggered"; + SendResponse(request_id, NULL); +} + +std::string KeyboardUI::KeyboardHTMLSource::GetMimeType( + const std::string&) const { + NOTREACHED() << "We should never get here since the extension should have" + << "been triggered"; + return "text/html"; +} diff --git a/chrome/browser/ui/webui/keyboard_ui.h b/chrome/browser/ui/webui/keyboard_ui.h new file mode 100644 index 0000000..980e28b --- /dev/null +++ b/chrome/browser/ui/webui/keyboard_ui.h @@ -0,0 +1,42 @@ +// 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 CHROME_BROWSER_UI_WEBUI_KEYBOARD_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_KEYBOARD_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "content/browser/webui/web_ui.h" + +class Profile; + +// The TabContents used for the keyboard page. +class KeyboardUI : public WebUI { + public: + explicit KeyboardUI(TabContents* manager); + ~KeyboardUI(); + + class KeyboardHTMLSource : public ChromeURLDataManager::DataSource { + public: + KeyboardHTMLSource(); + + // Overrides from DataSource + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + virtual ~KeyboardHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(KeyboardHTMLSource); + }; + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_KEYBOARD_UI_H_ diff --git a/chrome/browser/ui/webui/mediaplayer_ui.cc b/chrome/browser/ui/webui/mediaplayer_ui.cc new file mode 100644 index 0000000..d43930f --- /dev/null +++ b/chrome/browser/ui/webui/mediaplayer_ui.cc @@ -0,0 +1,613 @@ +// 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/ui/webui/mediaplayer_ui.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/values.h" +#include "base/weak_ptr.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/download/download_manager.h" +#include "chrome/browser/download/download_util.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/webui/favicon_source.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/time_format.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "net/base/escape.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_job.h" +#include "ui/base/resource/resource_bundle.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/frame/panel_browser_view.h" +#endif + +static const char kPropertyPath[] = "path"; +static const char kPropertyForce[] = "force"; +static const char kPropertyOffset[] = "currentOffset"; +static const char kPropertyError[] = "error"; + +static const char* kMediaplayerURL = "chrome://mediaplayer"; +static const char* kMediaplayerPlaylistURL = "chrome://mediaplayer#playlist"; +static const int kPopupLeft = 0; +static const int kPopupTop = 0; +static const int kPopupWidth = 350; +static const int kPopupHeight = 300; + +class MediaplayerUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + explicit MediaplayerUIHTMLSource(bool is_playlist); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~MediaplayerUIHTMLSource() {} + bool is_playlist_; + + DISALLOW_COPY_AND_ASSIGN(MediaplayerUIHTMLSource); +}; + +// The handler for Javascript messages related to the "mediaplayer" view. +class MediaplayerHandler : public WebUIMessageHandler, + public base::SupportsWeakPtr<MediaplayerHandler> { + public: + + struct MediaUrl { + MediaUrl() {} + explicit MediaUrl(const GURL& newurl) + : url(newurl), + haderror(false) {} + GURL url; + bool haderror; + }; + typedef std::vector<MediaUrl> UrlVector; + + explicit MediaplayerHandler(bool is_playlist); + + virtual ~MediaplayerHandler(); + + // Init work after Attach. + void Init(bool is_playlist, TabContents* contents); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Callback for the "currentOffsetChanged" message. + void HandleCurrentOffsetChanged(const ListValue* args); + + void FirePlaylistChanged(const std::string& path, + bool force, + int offset); + + void PlaybackMediaFile(const GURL& url); + + void EnqueueMediaFile(const GURL& url); + + void GetPlaylistValue(ListValue& args); + + // Callback for the "playbackError" message. + void HandlePlaybackError(const ListValue* args); + + // Callback for the "getCurrentPlaylist" message. + void HandleGetCurrentPlaylist(const ListValue* args); + + void HandleTogglePlaylist(const ListValue* args); + void HandleShowPlaylist(const ListValue* args); + void HandleSetCurrentPlaylistOffset(const ListValue* args); + void HandleToggleFullscreen(const ListValue* args); + + const UrlVector& GetCurrentPlaylist(); + + int GetCurrentPlaylistOffset(); + void SetCurrentPlaylistOffset(int offset); + // Sets the playlist for playlist views, since the playlist is + // maintained by the mediaplayer itself. Offset is the item in the + // playlist which is either now playing, or should be played. + void SetCurrentPlaylist(const UrlVector& playlist, int offset); + + private: + // The current playlist of urls. + UrlVector current_playlist_; + // The offset into the current_playlist_ of the currently playing item. + int current_offset_; + // Indicator of if this handler is a playlist or a mediaplayer. + bool is_playlist_; + DISALLOW_COPY_AND_ASSIGN(MediaplayerHandler); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// MediaplayerHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +MediaplayerUIHTMLSource::MediaplayerUIHTMLSource(bool is_playlist) + : DataSource(chrome::kChromeUIMediaplayerHost, MessageLoop::current()) { + is_playlist_ = is_playlist; +} + +void MediaplayerUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + // TODO(dhg): Fix the strings that are currently hardcoded so they + // use the localized versions. + localized_strings.SetString("errorstring", "Error Playing Back"); + + SetFontAndTextDirection(&localized_strings); + + std::string full_html; + + static const base::StringPiece mediaplayer_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_MEDIAPLAYER_HTML)); + + static const base::StringPiece playlist_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_MEDIAPLAYERPLAYLIST_HTML)); + + if (is_playlist_) { + full_html = jstemplate_builder::GetI18nTemplateHtml( + playlist_html, &localized_strings); + } else { + full_html = jstemplate_builder::GetI18nTemplateHtml( + mediaplayer_html, &localized_strings); + } + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// MediaplayerHandler +// +//////////////////////////////////////////////////////////////////////////////// +MediaplayerHandler::MediaplayerHandler(bool is_playlist) + : current_offset_(0), + is_playlist_(is_playlist) { +} + +MediaplayerHandler::~MediaplayerHandler() { +} + +WebUIMessageHandler* MediaplayerHandler::Attach(WebUI* web_ui) { + // Create our favicon data source. + Profile* profile = web_ui->GetProfile(); + profile->GetChromeURLDataManager()->AddDataSource( + new FavIconSource(profile)); + + return WebUIMessageHandler::Attach(web_ui); +} + +void MediaplayerHandler::Init(bool is_playlist, TabContents* contents) { + MediaPlayer* player = MediaPlayer::GetInstance(); + if (!is_playlist) { + player->SetNewHandler(this, contents); + } else { + player->RegisterNewPlaylistHandler(this, contents); + } +} + +void MediaplayerHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("currentOffsetChanged", + NewCallback(this, &MediaplayerHandler::HandleCurrentOffsetChanged)); + web_ui_->RegisterMessageCallback("playbackError", + NewCallback(this, &MediaplayerHandler::HandlePlaybackError)); + web_ui_->RegisterMessageCallback("getCurrentPlaylist", + NewCallback(this, &MediaplayerHandler::HandleGetCurrentPlaylist)); + web_ui_->RegisterMessageCallback("togglePlaylist", + NewCallback(this, &MediaplayerHandler::HandleTogglePlaylist)); + web_ui_->RegisterMessageCallback("setCurrentPlaylistOffset", + NewCallback(this, &MediaplayerHandler::HandleSetCurrentPlaylistOffset)); + web_ui_->RegisterMessageCallback("toggleFullscreen", + NewCallback(this, &MediaplayerHandler::HandleToggleFullscreen)); + web_ui_->RegisterMessageCallback("showPlaylist", + NewCallback(this, &MediaplayerHandler::HandleShowPlaylist)); +} + +void MediaplayerHandler::GetPlaylistValue(ListValue& urls) { + for (size_t x = 0; x < current_playlist_.size(); x++) { + DictionaryValue* url_value = new DictionaryValue(); + url_value->SetString(kPropertyPath, current_playlist_[x].url.spec()); + url_value->SetBoolean(kPropertyError, current_playlist_[x].haderror); + urls.Append(url_value); + } +} + +void MediaplayerHandler::PlaybackMediaFile(const GURL& url) { + current_playlist_.clear(); + current_playlist_.push_back(MediaplayerHandler::MediaUrl(url)); + FirePlaylistChanged(url.spec(), true, 0); + MediaPlayer::GetInstance()->NotifyPlaylistChanged(); +} + +const MediaplayerHandler::UrlVector& MediaplayerHandler::GetCurrentPlaylist() { + return current_playlist_; +} + +int MediaplayerHandler::GetCurrentPlaylistOffset() { + return current_offset_; +} + +void MediaplayerHandler::HandleToggleFullscreen(const ListValue* args) { + MediaPlayer::GetInstance()->ToggleFullscreen(); +} + +void MediaplayerHandler::HandleSetCurrentPlaylistOffset(const ListValue* args) { + int id; + CHECK(ExtractIntegerValue(args, &id)); + MediaPlayer::GetInstance()->SetPlaylistOffset(id); +} + +void MediaplayerHandler::FirePlaylistChanged(const std::string& path, + bool force, + int offset) { + DictionaryValue info_value; + ListValue urls; + GetPlaylistValue(urls); + info_value.SetString(kPropertyPath, path); + info_value.SetBoolean(kPropertyForce, force); + info_value.SetInteger(kPropertyOffset, offset); + web_ui_->CallJavascriptFunction(L"playlistChanged", info_value, urls); +} + +void MediaplayerHandler::SetCurrentPlaylistOffset(int offset) { + current_offset_ = offset; + FirePlaylistChanged(std::string(), true, current_offset_); +} + +void MediaplayerHandler::SetCurrentPlaylist( + const MediaplayerHandler::UrlVector& playlist, int offset) { + current_playlist_ = playlist; + current_offset_ = offset; + FirePlaylistChanged(std::string(), false, current_offset_); +} + +void MediaplayerHandler::EnqueueMediaFile(const GURL& url) { + current_playlist_.push_back(MediaplayerHandler::MediaUrl(url)); + FirePlaylistChanged(url.spec(), false, current_offset_); + MediaPlayer::GetInstance()->NotifyPlaylistChanged(); +} + +void MediaplayerHandler::HandleCurrentOffsetChanged(const ListValue* args) { + CHECK(ExtractIntegerValue(args, ¤t_offset_)); + MediaPlayer::GetInstance()->NotifyPlaylistChanged(); +} + +void MediaplayerHandler::HandlePlaybackError(const ListValue* args) { + std::string error; + std::string url; + // Get path string. + if (args->GetString(0, &error)) + LOG(ERROR) << "Playback error" << error; + if (args->GetString(1, &url)) { + for (size_t x = 0; x < current_playlist_.size(); x++) { + if (current_playlist_[x].url == GURL(url)) { + current_playlist_[x].haderror = true; + } + } + FirePlaylistChanged(std::string(), false, current_offset_); + } +} + +void MediaplayerHandler::HandleGetCurrentPlaylist(const ListValue* args) { + FirePlaylistChanged(std::string(), false, current_offset_); +} + +void MediaplayerHandler::HandleTogglePlaylist(const ListValue* args) { + MediaPlayer::GetInstance()->TogglePlaylistWindowVisible(); +} + +void MediaplayerHandler::HandleShowPlaylist(const ListValue* args) { + MediaPlayer::GetInstance()->ShowPlaylistWindow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Mediaplayer +// +//////////////////////////////////////////////////////////////////////////////// + +// Allows InvokeLater without adding refcounting. This class is a Singleton and +// won't be deleted until it's last InvokeLater is run. +DISABLE_RUNNABLE_METHOD_REFCOUNT(MediaPlayer); + +MediaPlayer::~MediaPlayer() { +} + +// static +MediaPlayer* MediaPlayer::GetInstance() { + return Singleton<MediaPlayer>::get(); +} + +void MediaPlayer::EnqueueMediaURL(const GURL& url, Browser* creator) { + if (!Enabled()) { + return; + } + if (handler_ == NULL) { + unhandled_urls_.push_back(url); + PopupMediaPlayer(creator); + } else { + handler_->EnqueueMediaFile(url); + } +} + +void MediaPlayer::ForcePlayMediaURL(const GURL& url, Browser* creator) { + if (!Enabled()) { + return; + } + if (handler_ == NULL) { + unhandled_urls_.push_back(url); + PopupMediaPlayer(creator); + } else { + handler_->PlaybackMediaFile(url); + } +} + +bool MediaPlayer::Enabled() { +#if defined(OS_CHROMEOS) + return CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMediaPlayer); +#else + return true; +#endif +} + +void MediaPlayer::TogglePlaylistWindowVisible() { + if (playlist_browser_) { + ClosePlaylistWindow(); + } else { + ShowPlaylistWindow(); + } +} + +void MediaPlayer::ShowPlaylistWindow() { + if (playlist_browser_ == NULL) { + PopupPlaylist(NULL); + } +} + +void MediaPlayer::ClosePlaylistWindow() { + if (playlist_browser_ != NULL) { + playlist_browser_->window()->Close(); + } +} + +void MediaPlayer::SetPlaylistOffset(int offset) { + if (handler_) { + handler_->SetCurrentPlaylistOffset(offset); + } + if (playlist_) { + playlist_->SetCurrentPlaylistOffset(offset); + } +} + +void MediaPlayer::SetNewHandler(MediaplayerHandler* handler, + TabContents* contents) { + handler_ = handler; + mediaplayer_tab_ = contents; + RegisterListeners(); + for (size_t x = 0; x < unhandled_urls_.size(); x++) { + handler_->EnqueueMediaFile(unhandled_urls_[x]); + } + unhandled_urls_.clear(); +} + +void MediaPlayer::RegisterListeners() { + registrar_.RemoveAll(); + if (playlist_tab_) { + registrar_.Add(this, + NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(playlist_tab_)); + } + if (mediaplayer_tab_) { + registrar_.Add(this, + NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(mediaplayer_tab_)); + } +}; + +void MediaPlayer::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); + if (Source<TabContents>(source).ptr() == mediaplayer_tab_) { + RemoveHandler(handler_); + RegisterListeners(); + ClosePlaylistWindow(); + } else if (Source<TabContents>(source).ptr() == playlist_tab_) { + RemovePlaylistHandler(playlist_); + RegisterListeners(); + } +} + +void MediaPlayer::RegisterNewPlaylistHandler(MediaplayerHandler* handler, + TabContents* contents) { + playlist_ = handler; + playlist_tab_ = contents; + RegisterListeners(); + NotifyPlaylistChanged(); +} + +void MediaPlayer::RemovePlaylistHandler(MediaplayerHandler* handler) { + if (handler == playlist_) { + playlist_ = NULL; + playlist_browser_ = NULL; + playlist_tab_ = NULL; + } +} + +void MediaPlayer::NotifyPlaylistChanged() { + if (handler_ && playlist_) { + playlist_->SetCurrentPlaylist(handler_->GetCurrentPlaylist(), + handler_->GetCurrentPlaylistOffset()); + } +} + +void MediaPlayer::ToggleFullscreen() { + if (handler_ && mediaplayer_browser_) { + mediaplayer_browser_->ToggleFullscreenMode(); + } +} + +void MediaPlayer::RemoveHandler(MediaplayerHandler* handler) { + if (handler == handler_) { + handler_ = NULL; + mediaplayer_browser_ = NULL; + mediaplayer_tab_ = NULL; + } +} + +void MediaPlayer::PopupPlaylist(Browser* creator) { + Profile* profile = BrowserList::GetLastActive()->profile(); + playlist_browser_ = Browser::CreateForType(Browser::TYPE_APP_PANEL, + profile); + playlist_browser_->AddSelectedTabWithURL(GURL(kMediaplayerPlaylistURL), + PageTransition::LINK); + playlist_browser_->window()->SetBounds(gfx::Rect(kPopupLeft, + kPopupTop, + kPopupWidth, + kPopupHeight)); + playlist_browser_->window()->Show(); +} + +void MediaPlayer::PopupMediaPlayer(Browser* creator) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &MediaPlayer::PopupMediaPlayer, + static_cast<Browser*>(NULL))); + return; + } + Profile* profile = BrowserList::GetLastActive()->profile(); + mediaplayer_browser_ = Browser::CreateForType(Browser::TYPE_APP_PANEL, + profile); +#if defined(OS_CHROMEOS) + // Since we are on chromeos, popups should be a PanelBrowserView, + // so we can just cast it. + if (creator) { + chromeos::PanelBrowserView* creatorview = + static_cast<chromeos::PanelBrowserView*>(creator->window()); + chromeos::PanelBrowserView* view = + static_cast<chromeos::PanelBrowserView*>( + mediaplayer_browser_->window()); + view->SetCreatorView(creatorview); + } +#endif + mediaplayer_browser_->AddSelectedTabWithURL(GURL(kMediaplayerURL), + PageTransition::LINK); + mediaplayer_browser_->window()->SetBounds(gfx::Rect(kPopupLeft, + kPopupTop, + kPopupWidth, + kPopupHeight)); + mediaplayer_browser_->window()->Show(); +} + +net::URLRequestJob* MediaPlayer::MaybeIntercept(net::URLRequest* request) { + // Don't attempt to intercept here as we want to wait until the mime + // type is fully determined. + return NULL; +} + +// This is the list of mime types currently supported by the Google +// Document Viewer. +static const char* const supported_mime_type_list[] = { + "audio/mpeg", + "video/mp4", + "audio/mp3" +}; + +net::URLRequestJob* MediaPlayer::MaybeInterceptResponse( + net::URLRequest* request) { + // Do not intercept this request if it is a download. + if (request->load_flags() & net::LOAD_IS_DOWNLOAD) { + return NULL; + } + + std::string mime_type; + request->GetMimeType(&mime_type); + // If it is in our list of known URLs, enqueue the url then + // Cancel the request so the mediaplayer can handle it when + // it hits it in the playlist. + if (supported_mime_types_.find(mime_type) != supported_mime_types_.end()) { + if (request->referrer() != chrome::kChromeUIMediaplayerURL && + !request->referrer().empty()) { + EnqueueMediaURL(request->url(), NULL); + request->Cancel(); + } + } + return NULL; +} + +MediaPlayer::MediaPlayer() + : handler_(NULL), + playlist_(NULL), + playlist_browser_(NULL), + mediaplayer_browser_(NULL), + mediaplayer_tab_(NULL), + playlist_tab_(NULL) { + for (size_t i = 0; i < arraysize(supported_mime_type_list); ++i) { + supported_mime_types_.insert(supported_mime_type_list[i]); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// MediaplayerUIContents +// +//////////////////////////////////////////////////////////////////////////////// + +MediaplayerUI::MediaplayerUI(TabContents* contents) : WebUI(contents) { + const GURL& url = contents->GetURL(); + bool is_playlist = (url.ref() == "playlist"); + MediaplayerHandler* handler = new MediaplayerHandler(is_playlist); + AddMessageHandler(handler->Attach(this)); + if (is_playlist) { + handler->Init(true, contents); + } else { + handler->Init(false, contents); + } + + MediaplayerUIHTMLSource* html_source = + new MediaplayerUIHTMLSource(is_playlist); + + // Set up the chrome://mediaplayer/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} diff --git a/chrome/browser/ui/webui/mediaplayer_ui.h b/chrome/browser/ui/webui/mediaplayer_ui.h new file mode 100644 index 0000000..5b5afcf --- /dev/null +++ b/chrome/browser/ui/webui/mediaplayer_ui.h @@ -0,0 +1,161 @@ +// 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 CHROME_BROWSER_UI_WEBUI_MEDIAPLAYER_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_MEDIAPLAYER_UI_H_ +#pragma once + +#include <set> +#include <vector> + +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "content/browser/webui/web_ui.h" +#include "net/base/directory_lister.h" +#include "net/url_request/url_request.h" + +template <typename T> struct DefaultSingletonTraits; +class GURL; +class MediaplayerHandler; +class Browser; + +class MediaPlayer : public NotificationObserver, + public net::URLRequest::Interceptor { + public: + ~MediaPlayer(); + + // Enqueues this url into the current playlist. If the mediaplayer is + // not currently visible, show it, and play the given url. + void EnqueueMediaURL(const GURL& url, Browser* creator); + + // Clears out the current playlist, and start playback of the given url. + // If there is no mediaplayer currently, show it, and play the given url. + void ForcePlayMediaURL(const GURL& url, Browser* creator); + + // Toggle the visibility of the playlist window. + void TogglePlaylistWindowVisible(); + + // Force the playlist window to be shown. + void ShowPlaylistWindow(); + + // Toggle the mediaplayer between fullscreen and windowed. + void ToggleFullscreen(); + + // Force the playlist window to be closed. + void ClosePlaylistWindow(); + + // Sets the currently playing element to the given offset. + void SetPlaylistOffset(int offset); + + // Set a new playback handler to give events to, along with the + // tab contents of the page which holds the mediaplayer. it is expected + // That only one of these will exist at any given time. + void SetNewHandler(MediaplayerHandler* handler, + TabContents* contents); + + // Removes the handler. + void RemoveHandler(MediaplayerHandler* handler); + + // Registers a new playlist handler which receives events from the + // mediaplayer, along with the tab contents which has the playlist in it. + void RegisterNewPlaylistHandler(MediaplayerHandler* handler, + TabContents* contents); + + // Removes the playlist handler. + void RemovePlaylistHandler(MediaplayerHandler* handler); + + // Notfiys the mediaplayer that the playlist changed. This could be + // called from the mediaplayer itself for example. + void NotifyPlaylistChanged(); + + // Always returns NULL because we don't want to attempt a redirect + // before seeing the detected mime type of the request. + // Implementation of net::URLRequest::Interceptor. + virtual net::URLRequestJob* MaybeIntercept(net::URLRequest* request); + + // Determines if the requested document can be viewed by the + // MediaPlayer. If it can, returns a net::URLRequestJob that + // redirects the browser to the view URL. + // Implementation of net::URLRequest::Interceptor. + virtual net::URLRequestJob* MaybeInterceptResponse(net::URLRequest* request); + + // Used to detect when the mediaplayer is closed. + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Getter for the singleton. + static MediaPlayer* GetInstance(); + + private: + friend struct DefaultSingletonTraits<MediaPlayer>; + + MediaPlayer(); + + // Popup the mediaplayer, this shows the browser, and sets up its + // locations correctly. + void PopupMediaPlayer(Browser* creator); + + // Checks to see the the mediaplayer is currently enabled + bool Enabled(); + + // Popup the playlist. Shows the browser, sets it up to point at + // chrome://mediaplayer#playlist + void PopupPlaylist(Browser* creator); + + // Registers the listeners for the close events on the browser windows. + void RegisterListeners(); + + // Set when the register handler is called. When the media player is + // closed, this pointer is set back to NULL. + MediaplayerHandler* handler_; + + // Set when the register playlist handler is called. When the playlist + // is closed, this pointer is set back to NULL. + MediaplayerHandler* playlist_; + + // Browser containing the playlist. Used to force closes. This is created + // By the PopupPlaylist call, and is NULLed out when the window is closed. + Browser* playlist_browser_; + + // Browser containing the Mediaplayer. Used to force closes. This is + // created by the PopupMediaplayer call, and is NULLed out when the window + // is closed. + Browser* mediaplayer_browser_; + + // List of URLs that were enqueued during the time that the mediaplayer + // had not poped up yet. This is claered out after the mediaplayer pops up. + std::vector<GURL> unhandled_urls_; + + // Used to register for events on the windows, like to listen for closes. + NotificationRegistrar registrar_; + + // Tab contents of the mediaplayer. Used to listen for events + // which would cause the mediaplayer to be closed. These are cleared out + // when the mediaplayer is closed. + TabContents* mediaplayer_tab_; + + // Tab contents of the playlist tab. used to listen for events which would + // cause the mediaplayer to be closed. These are cleared out when the + // playlist is closed. + TabContents* playlist_tab_; + + // List of mimetypes that the mediaplayer should listen to. Used for + // interceptions of url GETs. + std::set<std::string> supported_mime_types_; + DISALLOW_COPY_AND_ASSIGN(MediaPlayer); +}; + +class MediaplayerUI : public WebUI { + public: + explicit MediaplayerUI(TabContents* contents); + + private: + DISALLOW_COPY_AND_ASSIGN(MediaplayerUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_MEDIAPLAYER_UI_H_ diff --git a/chrome/browser/ui/webui/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals_ui.cc new file mode 100644 index 0000000..4791c319 --- /dev/null +++ b/chrome/browser/ui/webui/net_internals_ui.cc @@ -0,0 +1,1315 @@ +// 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/ui/webui/net_internals_ui.h" + +#include <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/io_thread.h" +#include "chrome/browser/net/chrome_net_log.h" +#include "chrome/browser/net/connection_tester.h" +#include "chrome/browser/net/passive_log_collector.h" +#include "chrome/browser/net/url_fixer_upper.h" +#include "chrome/browser/platform_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/ui/shell_dialogs.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_version_info.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "chrome/common/url_constants.h" +#include "grit/generated_resources.h" +#include "grit/net_internals_resources.h" +#include "net/base/escape.h" +#include "net/base/host_resolver_impl.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/sys_addrinfo.h" +#include "net/disk_cache/disk_cache.h" +#include "net/http/http_alternate_protocols.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_layer.h" +#include "net/http/http_network_session.h" +#include "net/http/http_stream_factory.h" +#include "net/proxy/proxy_service.h" +#include "net/url_request/url_request_context.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +#ifdef OS_WIN +#include "chrome/browser/net/service_providers_win.h" +#endif + +namespace { + +// Delay between when an event occurs and when it is passed to the Javascript +// page. All events that occur during this period are grouped together and +// sent to the page at once, which reduces context switching and CPU usage. +const int kNetLogEventDelayMilliseconds = 100; + +// Returns the HostCache for |context|'s primary HostResolver, or NULL if +// there is none. +net::HostCache* GetHostResolverCache(net::URLRequestContext* context) { + net::HostResolverImpl* host_resolver_impl = + context->host_resolver()->GetAsHostResolverImpl(); + + if (!host_resolver_impl) + return NULL; + + return host_resolver_impl->cache(); +} + +// Returns the disk cache backend for |context| if there is one, or NULL. +disk_cache::Backend* GetDiskCacheBackend(net::URLRequestContext* context) { + if (!context->http_transaction_factory()) + return NULL; + + net::HttpCache* http_cache = context->http_transaction_factory()->GetCache(); + if (!http_cache) + return NULL; + + return http_cache->GetCurrentBackend(); +} + +// Returns the http network session for |context| if there is one. +// Otherwise, returns NULL. +net::HttpNetworkSession* GetHttpNetworkSession( + net::URLRequestContext* context) { + if (!context->http_transaction_factory()) + return NULL; + + return context->http_transaction_factory()->GetSession(); +} + +Value* ExperimentToValue(const ConnectionTester::Experiment& experiment) { + DictionaryValue* dict = new DictionaryValue(); + + if (experiment.url.is_valid()) + dict->SetString("url", experiment.url.spec()); + + dict->SetString("proxy_settings_experiment", + ConnectionTester::ProxySettingsExperimentDescription( + experiment.proxy_settings_experiment)); + dict->SetString("host_resolver_experiment", + ConnectionTester::HostResolverExperimentDescription( + experiment.host_resolver_experiment)); + return dict; +} + +class NetInternalsHTMLSource : public ChromeURLDataManager::DataSource { + public: + NetInternalsHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + ~NetInternalsHTMLSource() {} + DISALLOW_COPY_AND_ASSIGN(NetInternalsHTMLSource); +}; + +// This class receives javascript messages from the renderer. +// Note that the WebUI infrastructure runs on the UI thread, therefore all of +// this class's methods are expected to run on the UI thread. +// +// Since the network code we want to run lives on the IO thread, we proxy +// everything over to NetInternalsMessageHandler::IOThreadImpl, which runs +// on the IO thread. +// +// TODO(eroman): Can we start on the IO thread to begin with? +class NetInternalsMessageHandler + : public WebUIMessageHandler, + public SelectFileDialog::Listener, + public base::SupportsWeakPtr<NetInternalsMessageHandler> { + public: + NetInternalsMessageHandler(); + virtual ~NetInternalsMessageHandler(); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Executes the javascript function |function_name| in the renderer, passing + // it the argument |value|. + void CallJavascriptFunction(const std::wstring& function_name, + const Value* value); + + // SelectFileDialog::Listener implementation + virtual void FileSelected(const FilePath& path, int index, void* params); + virtual void FileSelectionCanceled(void* params); + + // The only callback handled on the UI thread. As it needs to access fields + // from |web_ui_|, it can't be called on the IO thread. + void OnLoadLogFile(const ListValue* list); + + private: + class IOThreadImpl; + + // Task run on the FILE thread to read the contents of a log file. The result + // is then passed to IOThreadImpl's CallJavascriptFunction, which sends it + // back to the web page. IOThreadImpl is used instead of the + // NetInternalsMessageHandler directly because it checks if the message + // handler has been destroyed in the meantime. + class ReadLogFileTask : public Task { + public: + ReadLogFileTask(IOThreadImpl* proxy, const FilePath& path); + + virtual void Run(); + + private: + // IOThreadImpl implements existence checks already. Simpler to reused them + // then to reimplement them. + scoped_refptr<IOThreadImpl> proxy_; + + // Path of the file to open. + const FilePath path_; + }; + + // This is the "real" message handler, which lives on the IO thread. + scoped_refptr<IOThreadImpl> proxy_; + + // Used for loading log files. + scoped_refptr<SelectFileDialog> select_log_file_dialog_; + + DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler); +}; + +// This class is the "real" message handler. It is allocated and destroyed on +// the UI thread. With the exception of OnAddEntry, OnWebUIDeleted, and +// CallJavascriptFunction, its methods are all expected to be called from the IO +// thread. OnAddEntry and CallJavascriptFunction can be called from any thread, +// and OnWebUIDeleted can only be called from the UI thread. +class NetInternalsMessageHandler::IOThreadImpl + : public base::RefCountedThreadSafe< + NetInternalsMessageHandler::IOThreadImpl, + BrowserThread::DeleteOnUIThread>, + public ChromeNetLog::ThreadSafeObserver, + public ConnectionTester::Delegate { + public: + // Type for methods that can be used as MessageHandler callbacks. + typedef void (IOThreadImpl::*MessageHandler)(const ListValue*); + + // Creates a proxy for |handler| that will live on the IO thread. + // |handler| is a weak pointer, since it is possible for the + // WebUIMessageHandler to be deleted on the UI thread while we were executing + // on the IO thread. |io_thread| is the global IOThread (it is passed in as + // an argument since we need to grab it from the UI thread). + IOThreadImpl( + const base::WeakPtr<NetInternalsMessageHandler>& handler, + IOThread* io_thread, + URLRequestContextGetter* context_getter); + + ~IOThreadImpl(); + + // Creates a callback that will run |method| on the IO thread. + // + // This can be used with WebUI::RegisterMessageCallback() to bind to a method + // on the IO thread. + WebUI::MessageCallback* CreateCallback(MessageHandler method); + + // Called once the WebUI has been deleted (i.e. renderer went away), on the + // IO thread. + void Detach(); + + // Sends all passive log entries in |passive_entries| to the Javascript + // handler, called on the IO thread. + void SendPassiveLogEntries(const ChromeNetLog::EntryList& passive_entries); + + // Called when the WebUI is deleted. Prevents calling Javascript functions + // afterwards. Called on UI thread. + void OnWebUIDeleted(); + + //-------------------------------- + // Javascript message handlers: + //-------------------------------- + + void OnRendererReady(const ListValue* list); + + void OnGetProxySettings(const ListValue* list); + void OnReloadProxySettings(const ListValue* list); + void OnGetBadProxies(const ListValue* list); + void OnClearBadProxies(const ListValue* list); + void OnGetHostResolverInfo(const ListValue* list); + void OnClearHostResolverCache(const ListValue* list); + void OnEnableIPv6(const ListValue* list); + void OnStartConnectionTests(const ListValue* list); + void OnHSTSQuery(const ListValue* list); + void OnHSTSAdd(const ListValue* list); + void OnHSTSDelete(const ListValue* list); + void OnGetHttpCacheInfo(const ListValue* list); + void OnGetSocketPoolInfo(const ListValue* list); + void OnGetSpdySessionInfo(const ListValue* list); + void OnGetSpdyStatus(const ListValue* list); + void OnGetSpdyAlternateProtocolMappings(const ListValue* list); +#ifdef OS_WIN + void OnGetServiceProviders(const ListValue* list); +#endif + + void OnSetLogLevel(const ListValue* list); + + // ChromeNetLog::ThreadSafeObserver implementation: + virtual void OnAddEntry(net::NetLog::EventType type, + const base::TimeTicks& time, + const net::NetLog::Source& source, + net::NetLog::EventPhase phase, + net::NetLog::EventParameters* params); + + // ConnectionTester::Delegate implementation: + virtual void OnStartConnectionTestSuite(); + virtual void OnStartConnectionTestExperiment( + const ConnectionTester::Experiment& experiment); + virtual void OnCompletedConnectionTestExperiment( + const ConnectionTester::Experiment& experiment, + int result); + virtual void OnCompletedConnectionTestSuite(); + + // Helper that executes |function_name| in the attached renderer. + // The function takes ownership of |arg|. Note that this can be called from + // any thread. + void CallJavascriptFunction(const std::wstring& function_name, Value* arg); + + private: + class CallbackHelper; + + // Helper that runs |method| with |arg|, and deletes |arg| on completion. + void DispatchToMessageHandler(ListValue* arg, MessageHandler method); + + // Adds |entry| to the queue of pending log entries to be sent to the page via + // Javascript. Must be called on the IO Thread. Also creates a delayed task + // that will call PostPendingEntries, if there isn't one already. + void AddEntryToQueue(Value* entry); + + // Sends all pending entries to the page via Javascript, and clears the list + // of pending entries. Sending multiple entries at once results in a + // significant reduction of CPU usage when a lot of events are happening. + // Must be called on the IO Thread. + void PostPendingEntries(); + + // Pointer to the UI-thread message handler. Only access this from + // the UI thread. + base::WeakPtr<NetInternalsMessageHandler> handler_; + + // The global IOThread, which contains the global NetLog to observer. + IOThread* io_thread_; + + scoped_refptr<URLRequestContextGetter> context_getter_; + + // Helper that runs the suite of connection tests. + scoped_ptr<ConnectionTester> connection_tester_; + + // True if the Web UI has been deleted. This is used to prevent calling + // Javascript functions after the Web UI is destroyed. On refresh, the + // messages can end up being sent to the refreshed page, causing duplicate + // or partial entries. + // + // This is only read and written to on the UI thread. + bool was_webui_deleted_; + + // True if we have attached an observer to the NetLog already. + bool is_observing_log_; + friend class base::RefCountedThreadSafe<IOThreadImpl>; + + // Log entries that have yet to be passed along to Javascript page. Non-NULL + // when and only when there is a pending delayed task to call + // PostPendingEntries. Read and written to exclusively on the IO Thread. + scoped_ptr<ListValue> pending_entries_; +}; + +// Helper class for a WebUI::MessageCallback which when excuted calls +// instance->*method(value) on the IO thread. +class NetInternalsMessageHandler::IOThreadImpl::CallbackHelper + : public WebUI::MessageCallback { + public: + CallbackHelper(IOThreadImpl* instance, IOThreadImpl::MessageHandler method) + : instance_(instance), + method_(method) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + } + + virtual void RunWithParams(const Tuple1<const ListValue*>& params) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // We need to make a copy of the value in order to pass it over to the IO + // thread. We will delete this in IOThreadImpl::DispatchMessageHandler(). + ListValue* list_copy = static_cast<ListValue*>( + params.a ? params.a->DeepCopy() : NULL); + + if (!BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(instance_.get(), + &IOThreadImpl::DispatchToMessageHandler, + list_copy, method_))) { + // Failed posting the task, avoid leaking |list_copy|. + delete list_copy; + } + } + + private: + scoped_refptr<IOThreadImpl> instance_; + IOThreadImpl::MessageHandler method_; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// NetInternalsHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsHTMLSource::NetInternalsHTMLSource() + : DataSource(chrome::kChromeUINetInternalsHost, MessageLoop::current()) { +} + +void NetInternalsHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + SetFontAndTextDirection(&localized_strings); + + // The provided "path" may contain a fragment, or query section. We only + // care about the path itself, and will disregard anything else. + std::string filename = + GURL(std::string("chrome://net/") + path).path().substr(1); + + // The source for the net internals page is flattened during compilation, so + // the only resource that should legitimately be requested is the main file. + // Note that users can type anything into the address bar, though, so we must + // handle arbitrary input. + if (filename.empty() || filename == "index.html") { + base::StringPiece html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_NET_INTERNALS_INDEX_HTML)); + std::string full_html(html.data(), html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + 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); + return; + } + + const std::string data_string("<p style='color:red'>Failed to read resource" + + EscapeForHTML(filename) + "</p>"); + scoped_refptr<RefCountedBytes> bytes(new RefCountedBytes); + bytes->data.resize(data_string.size()); + std::copy(data_string.begin(), data_string.end(), bytes->data.begin()); + SendResponse(request_id, bytes); +} + +std::string NetInternalsHTMLSource::GetMimeType(const std::string&) const { + return "text/html"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NetInternalsMessageHandler +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsMessageHandler::NetInternalsMessageHandler() {} + +NetInternalsMessageHandler::~NetInternalsMessageHandler() { + if (proxy_) { + proxy_.get()->OnWebUIDeleted(); + // Notify the handler on the IO thread that the renderer is gone. + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + NewRunnableMethod(proxy_.get(), &IOThreadImpl::Detach)); + } + if (select_log_file_dialog_) + select_log_file_dialog_->ListenerDestroyed(); +} + +WebUIMessageHandler* NetInternalsMessageHandler::Attach(WebUI* web_ui) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + proxy_ = new IOThreadImpl(this->AsWeakPtr(), g_browser_process->io_thread(), + web_ui->GetProfile()->GetRequestContext()); + WebUIMessageHandler* result = WebUIMessageHandler::Attach(web_ui); + return result; +} + +void NetInternalsMessageHandler::FileSelected( + const FilePath& path, int index, void* params) { + select_log_file_dialog_.release(); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + new ReadLogFileTask(proxy_.get(), path)); +} + +void NetInternalsMessageHandler::FileSelectionCanceled(void* params) { + select_log_file_dialog_.release(); +} + +void NetInternalsMessageHandler::OnLoadLogFile(const ListValue* list) { + // Only allow a single dialog at a time. + if (select_log_file_dialog_.get()) + return; + select_log_file_dialog_ = SelectFileDialog::Create(this); + select_log_file_dialog_->SelectFile( + SelectFileDialog::SELECT_OPEN_FILE, string16(), FilePath(), NULL, 0, + FILE_PATH_LITERAL(""), + web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); +} + +void NetInternalsMessageHandler::RegisterMessages() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Only callback handled on UI thread. + web_ui_->RegisterMessageCallback( + "loadLogFile", + NewCallback(this, &NetInternalsMessageHandler::OnLoadLogFile)); + + web_ui_->RegisterMessageCallback( + "notifyReady", + proxy_->CreateCallback(&IOThreadImpl::OnRendererReady)); + web_ui_->RegisterMessageCallback( + "getProxySettings", + proxy_->CreateCallback(&IOThreadImpl::OnGetProxySettings)); + web_ui_->RegisterMessageCallback( + "reloadProxySettings", + proxy_->CreateCallback(&IOThreadImpl::OnReloadProxySettings)); + web_ui_->RegisterMessageCallback( + "getBadProxies", + proxy_->CreateCallback(&IOThreadImpl::OnGetBadProxies)); + web_ui_->RegisterMessageCallback( + "clearBadProxies", + proxy_->CreateCallback(&IOThreadImpl::OnClearBadProxies)); + web_ui_->RegisterMessageCallback( + "getHostResolverInfo", + proxy_->CreateCallback(&IOThreadImpl::OnGetHostResolverInfo)); + web_ui_->RegisterMessageCallback( + "clearHostResolverCache", + proxy_->CreateCallback(&IOThreadImpl::OnClearHostResolverCache)); + web_ui_->RegisterMessageCallback( + "enableIPv6", + proxy_->CreateCallback(&IOThreadImpl::OnEnableIPv6)); + web_ui_->RegisterMessageCallback( + "startConnectionTests", + proxy_->CreateCallback(&IOThreadImpl::OnStartConnectionTests)); + web_ui_->RegisterMessageCallback( + "hstsQuery", + proxy_->CreateCallback(&IOThreadImpl::OnHSTSQuery)); + web_ui_->RegisterMessageCallback( + "hstsAdd", + proxy_->CreateCallback(&IOThreadImpl::OnHSTSAdd)); + web_ui_->RegisterMessageCallback( + "hstsDelete", + proxy_->CreateCallback(&IOThreadImpl::OnHSTSDelete)); + web_ui_->RegisterMessageCallback( + "getHttpCacheInfo", + proxy_->CreateCallback(&IOThreadImpl::OnGetHttpCacheInfo)); + web_ui_->RegisterMessageCallback( + "getSocketPoolInfo", + proxy_->CreateCallback(&IOThreadImpl::OnGetSocketPoolInfo)); + web_ui_->RegisterMessageCallback( + "getSpdySessionInfo", + proxy_->CreateCallback(&IOThreadImpl::OnGetSpdySessionInfo)); + web_ui_->RegisterMessageCallback( + "getSpdyStatus", + proxy_->CreateCallback(&IOThreadImpl::OnGetSpdyStatus)); + web_ui_->RegisterMessageCallback( + "getSpdyAlternateProtocolMappings", + proxy_->CreateCallback( + &IOThreadImpl::OnGetSpdyAlternateProtocolMappings)); +#ifdef OS_WIN + web_ui_->RegisterMessageCallback( + "getServiceProviders", + proxy_->CreateCallback(&IOThreadImpl::OnGetServiceProviders)); +#endif + + web_ui_->RegisterMessageCallback( + "setLogLevel", + proxy_->CreateCallback(&IOThreadImpl::OnSetLogLevel)); +} + +void NetInternalsMessageHandler::CallJavascriptFunction( + const std::wstring& function_name, + const Value* value) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (value) { + web_ui_->CallJavascriptFunction(function_name, *value); + } else { + web_ui_->CallJavascriptFunction(function_name); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NetInternalsMessageHandler::ReadLogFileTask +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsMessageHandler::ReadLogFileTask::ReadLogFileTask( + IOThreadImpl* proxy, const FilePath& path) + : proxy_(proxy), path_(path) { +} + +void NetInternalsMessageHandler::ReadLogFileTask::Run() { + std::string file_contents; + if (!file_util::ReadFileToString(path_, &file_contents)) + return; + proxy_->CallJavascriptFunction(L"g_browser.loadedLogFile", + new StringValue(file_contents)); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// NetInternalsMessageHandler::IOThreadImpl +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsMessageHandler::IOThreadImpl::IOThreadImpl( + const base::WeakPtr<NetInternalsMessageHandler>& handler, + IOThread* io_thread, + URLRequestContextGetter* context_getter) + : ThreadSafeObserver(net::NetLog::LOG_ALL_BUT_BYTES), + handler_(handler), + io_thread_(io_thread), + context_getter_(context_getter), + was_webui_deleted_(false), + is_observing_log_(false) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +NetInternalsMessageHandler::IOThreadImpl::~IOThreadImpl() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); +} + +WebUI::MessageCallback* +NetInternalsMessageHandler::IOThreadImpl::CreateCallback( + MessageHandler method) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return new CallbackHelper(this, method); +} + +void NetInternalsMessageHandler::IOThreadImpl::Detach() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Unregister with network stack to observe events. + if (is_observing_log_) + io_thread_->net_log()->RemoveObserver(this); + + // Cancel any in-progress connection tests. + connection_tester_.reset(); +} + +void NetInternalsMessageHandler::IOThreadImpl::SendPassiveLogEntries( + const ChromeNetLog::EntryList& passive_entries) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ListValue* dict_list = new ListValue(); + for (size_t i = 0; i < passive_entries.size(); ++i) { + const ChromeNetLog::Entry& e = passive_entries[i]; + dict_list->Append(net::NetLog::EntryToDictionaryValue(e.type, + e.time, + e.source, + e.phase, + e.params, + false)); + } + + CallJavascriptFunction(L"g_browser.receivedPassiveLogEntries", dict_list); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnWebUIDeleted() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + was_webui_deleted_ = true; +} + +void NetInternalsMessageHandler::IOThreadImpl::OnRendererReady( + const ListValue* list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!is_observing_log_) << "notifyReady called twice"; + + // Tell the javascript about the relationship between event type enums and + // their symbolic name. + { + std::vector<net::NetLog::EventType> event_types = + net::NetLog::GetAllEventTypes(); + + DictionaryValue* dict = new DictionaryValue(); + + for (size_t i = 0; i < event_types.size(); ++i) { + const char* name = net::NetLog::EventTypeToString(event_types[i]); + dict->SetInteger(name, static_cast<int>(event_types[i])); + } + + CallJavascriptFunction(L"g_browser.receivedLogEventTypeConstants", dict); + } + + // Tell the javascript about the version of the client and its + // command line arguments. + { + DictionaryValue* dict = new DictionaryValue(); + + chrome::VersionInfo version_info; + + if (!version_info.is_valid()) { + DLOG(ERROR) << "Unable to create chrome::VersionInfo"; + } else { + // We have everything we need to send the right values. + dict->SetString("version", version_info.Version()); + dict->SetString("cl", version_info.LastChange()); + dict->SetString("version_mod", + platform_util::GetVersionStringModifier()); + dict->SetString("official", + l10n_util::GetStringUTF16( + version_info.IsOfficialBuild() ? + IDS_ABOUT_VERSION_OFFICIAL + : IDS_ABOUT_VERSION_UNOFFICIAL)); + + dict->SetString("command_line", + CommandLine::ForCurrentProcess()->command_line_string()); + } + + CallJavascriptFunction(L"g_browser.receivedClientInfo", + dict); + } + + // Tell the javascript about the relationship between load flag enums and + // their symbolic name. + { + DictionaryValue* dict = new DictionaryValue(); + +#define LOAD_FLAG(label, value) \ + dict->SetInteger(# label, static_cast<int>(value)); +#include "net/base/load_flags_list.h" +#undef LOAD_FLAG + + CallJavascriptFunction(L"g_browser.receivedLoadFlagConstants", dict); + } + + // Tell the javascript about the relationship between net error codes and + // their symbolic name. + { + DictionaryValue* dict = new DictionaryValue(); + +#define NET_ERROR(label, value) \ + dict->SetInteger(# label, static_cast<int>(value)); +#include "net/base/net_error_list.h" +#undef NET_ERROR + + CallJavascriptFunction(L"g_browser.receivedNetErrorConstants", dict); + } + + // Tell the javascript about the relationship between event phase enums and + // their symbolic name. + { + DictionaryValue* dict = new DictionaryValue(); + + dict->SetInteger("PHASE_BEGIN", net::NetLog::PHASE_BEGIN); + dict->SetInteger("PHASE_END", net::NetLog::PHASE_END); + dict->SetInteger("PHASE_NONE", net::NetLog::PHASE_NONE); + + CallJavascriptFunction(L"g_browser.receivedLogEventPhaseConstants", dict); + } + + // Tell the javascript about the relationship between source type enums and + // their symbolic names. + { + DictionaryValue* dict = new DictionaryValue(); + +#define SOURCE_TYPE(label, value) dict->SetInteger(# label, value); +#include "net/base/net_log_source_type_list.h" +#undef SOURCE_TYPE + + CallJavascriptFunction(L"g_browser.receivedLogSourceTypeConstants", dict); + } + + // Tell the javascript about the relationship between LogLevel enums and their + // symbolic names. + { + DictionaryValue* dict = new DictionaryValue(); + + dict->SetInteger("LOG_ALL", net::NetLog::LOG_ALL); + dict->SetInteger("LOG_ALL_BUT_BYTES", net::NetLog::LOG_ALL_BUT_BYTES); + dict->SetInteger("LOG_BASIC", net::NetLog::LOG_BASIC); + + CallJavascriptFunction(L"g_browser.receivedLogLevelConstants", dict); + } + + // Tell the javascript about the relationship between address family enums and + // their symbolic names. + { + DictionaryValue* dict = new DictionaryValue(); + + dict->SetInteger("ADDRESS_FAMILY_UNSPECIFIED", + net::ADDRESS_FAMILY_UNSPECIFIED); + dict->SetInteger("ADDRESS_FAMILY_IPV4", + net::ADDRESS_FAMILY_IPV4); + dict->SetInteger("ADDRESS_FAMILY_IPV6", + net::ADDRESS_FAMILY_IPV6); + + CallJavascriptFunction(L"g_browser.receivedAddressFamilyConstants", dict); + } + + // Tell the javascript how the "time ticks" values we have given it relate to + // actual system times. (We used time ticks throughout since they are stable + // across system clock changes). + { + int64 cur_time_ms = (base::Time::Now() - base::Time()).InMilliseconds(); + + int64 cur_time_ticks_ms = + (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds(); + + // If we add this number to a time tick value, it gives the timestamp. + int64 tick_to_time_ms = cur_time_ms - cur_time_ticks_ms; + + // Chrome on all platforms stores times using the Windows epoch + // (Jan 1 1601), but the javascript wants a unix epoch. + // TODO(eroman): Getting the timestamp relative the to unix epoch should + // be part of the time library. + const int64 kUnixEpochMs = 11644473600000LL; + int64 tick_to_unix_time_ms = tick_to_time_ms - kUnixEpochMs; + + // Pass it as a string, since it may be too large to fit in an integer. + CallJavascriptFunction(L"g_browser.receivedTimeTickOffset", + Value::CreateStringValue( + base::Int64ToString(tick_to_unix_time_ms))); + } + + // Register with network stack to observe events. + is_observing_log_ = true; + ChromeNetLog::EntryList entries; + io_thread_->net_log()->AddObserverAndGetAllPassivelyCapturedEvents(this, + &entries); + SendPassiveLogEntries(entries); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetProxySettings( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + net::ProxyService* proxy_service = context->proxy_service(); + + DictionaryValue* dict = new DictionaryValue(); + if (proxy_service->fetched_config().is_valid()) + dict->Set("original", proxy_service->fetched_config().ToValue()); + if (proxy_service->config().is_valid()) + dict->Set("effective", proxy_service->config().ToValue()); + + CallJavascriptFunction(L"g_browser.receivedProxySettings", dict); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnReloadProxySettings( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + context->proxy_service()->ForceReloadProxyConfig(); + + // Cause the renderer to be notified of the new values. + OnGetProxySettings(NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetBadProxies( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + + const net::ProxyRetryInfoMap& bad_proxies_map = + context->proxy_service()->proxy_retry_info(); + + ListValue* dict_list = new ListValue(); + + for (net::ProxyRetryInfoMap::const_iterator it = bad_proxies_map.begin(); + it != bad_proxies_map.end(); ++it) { + const std::string& proxy_uri = it->first; + const net::ProxyRetryInfo& retry_info = it->second; + + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("proxy_uri", proxy_uri); + dict->SetString("bad_until", + net::NetLog::TickCountToString(retry_info.bad_until)); + + dict_list->Append(dict); + } + + CallJavascriptFunction(L"g_browser.receivedBadProxies", dict_list); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnClearBadProxies( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + context->proxy_service()->ClearBadProxiesCache(); + + // Cause the renderer to be notified of the new values. + OnGetBadProxies(NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetHostResolverInfo( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + net::HostResolverImpl* host_resolver_impl = + context->host_resolver()->GetAsHostResolverImpl(); + net::HostCache* cache = GetHostResolverCache(context); + + if (!host_resolver_impl || !cache) { + CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", NULL); + return; + } + + DictionaryValue* dict = new DictionaryValue(); + + dict->SetInteger( + "default_address_family", + static_cast<int>(host_resolver_impl->GetDefaultAddressFamily())); + + DictionaryValue* cache_info_dict = new DictionaryValue(); + + cache_info_dict->SetInteger( + "capacity", + static_cast<int>(cache->max_entries())); + cache_info_dict->SetInteger( + "ttl_success_ms", + static_cast<int>(cache->success_entry_ttl().InMilliseconds())); + cache_info_dict->SetInteger( + "ttl_failure_ms", + static_cast<int>(cache->failure_entry_ttl().InMilliseconds())); + + ListValue* entry_list = new ListValue(); + + for (net::HostCache::EntryMap::const_iterator it = + cache->entries().begin(); + it != cache->entries().end(); + ++it) { + const net::HostCache::Key& key = it->first; + const net::HostCache::Entry* entry = it->second.get(); + + DictionaryValue* entry_dict = new DictionaryValue(); + + entry_dict->SetString("hostname", key.hostname); + entry_dict->SetInteger("address_family", + static_cast<int>(key.address_family)); + entry_dict->SetString("expiration", + net::NetLog::TickCountToString(entry->expiration)); + + if (entry->error != net::OK) { + entry_dict->SetInteger("error", entry->error); + } else { + // Append all of the resolved addresses. + ListValue* address_list = new ListValue(); + const struct addrinfo* current_address = entry->addrlist.head(); + while (current_address) { + address_list->Append(Value::CreateStringValue( + net::NetAddressToStringWithPort(current_address))); + current_address = current_address->ai_next; + } + entry_dict->Set("addresses", address_list); + } + + entry_list->Append(entry_dict); + } + + cache_info_dict->Set("entries", entry_list); + dict->Set("cache", cache_info_dict); + + CallJavascriptFunction(L"g_browser.receivedHostResolverInfo", dict); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnClearHostResolverCache( + const ListValue* list) { + net::HostCache* cache = + GetHostResolverCache(context_getter_->GetURLRequestContext()); + + if (cache) + cache->clear(); + + // Cause the renderer to be notified of the new values. + OnGetHostResolverInfo(NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnEnableIPv6( + const ListValue* list) { + net::URLRequestContext* context = context_getter_->GetURLRequestContext(); + net::HostResolverImpl* host_resolver_impl = + context->host_resolver()->GetAsHostResolverImpl(); + + if (host_resolver_impl) { + host_resolver_impl->SetDefaultAddressFamily( + net::ADDRESS_FAMILY_UNSPECIFIED); + } + + // Cause the renderer to be notified of the new value. + OnGetHostResolverInfo(NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTests( + const ListValue* list) { + // |value| should be: [<URL to test>]. + string16 url_str; + CHECK(list->GetString(0, &url_str)); + + // Try to fix-up the user provided URL into something valid. + // For example, turn "www.google.com" into "http://www.google.com". + GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(url_str), std::string())); + + connection_tester_.reset(new ConnectionTester( + this, io_thread_->globals()->proxy_script_fetcher_context.get())); + connection_tester_->RunAllTests(url); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnHSTSQuery( + const ListValue* list) { + // |list| should be: [<domain to query>]. + std::string domain; + CHECK(list->GetString(0, &domain)); + DictionaryValue* result = new(DictionaryValue); + + if (!IsStringASCII(domain)) { + result->SetString("error", "non-ASCII domain name"); + } else { + net::TransportSecurityState* transport_security_state = + context_getter_->GetURLRequestContext()->transport_security_state(); + if (!transport_security_state) { + result->SetString("error", "no TransportSecurityState active"); + } else { + net::TransportSecurityState::DomainState state; + const bool found = transport_security_state->IsEnabledForHost( + &state, domain); + + result->SetBoolean("result", found); + if (found) { + result->SetInteger("mode", static_cast<int>(state.mode)); + result->SetBoolean("subdomains", state.include_subdomains); + result->SetBoolean("preloaded", state.preloaded); + result->SetString("domain", state.domain); + } + } + } + + CallJavascriptFunction(L"g_browser.receivedHSTSResult", result); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnHSTSAdd( + const ListValue* list) { + // |list| should be: [<domain to query>, <include subdomains>]. + std::string domain; + CHECK(list->GetString(0, &domain)); + if (!IsStringASCII(domain)) { + // Silently fail. The user will get a helpful error if they query for the + // name. + return; + } + bool include_subdomains; + CHECK(list->GetBoolean(1, &include_subdomains)); + + net::TransportSecurityState* transport_security_state = + context_getter_->GetURLRequestContext()->transport_security_state(); + if (!transport_security_state) + return; + + net::TransportSecurityState::DomainState state; + state.expiry = state.created + base::TimeDelta::FromDays(1000); + state.include_subdomains = include_subdomains; + + transport_security_state->EnableHost(domain, state); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnHSTSDelete( + const ListValue* list) { + // |list| should be: [<domain to query>]. + std::string domain; + CHECK(list->GetString(0, &domain)); + if (!IsStringASCII(domain)) { + // There cannot be a unicode entry in the HSTS set. + return; + } + net::TransportSecurityState* transport_security_state = + context_getter_->GetURLRequestContext()->transport_security_state(); + if (!transport_security_state) + return; + + transport_security_state->DeleteHost(domain); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetHttpCacheInfo( + const ListValue* list) { + DictionaryValue* info_dict = new DictionaryValue(); + DictionaryValue* stats_dict = new DictionaryValue(); + + disk_cache::Backend* disk_cache = GetDiskCacheBackend( + context_getter_->GetURLRequestContext()); + + if (disk_cache) { + // Extract the statistics key/value pairs from the backend. + std::vector<std::pair<std::string, std::string> > stats; + disk_cache->GetStats(&stats); + for (size_t i = 0; i < stats.size(); ++i) { + stats_dict->Set(stats[i].first, + Value::CreateStringValue(stats[i].second)); + } + } + + info_dict->Set("stats", stats_dict); + + CallJavascriptFunction(L"g_browser.receivedHttpCacheInfo", info_dict); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetSocketPoolInfo( + const ListValue* list) { + net::HttpNetworkSession* http_network_session = + GetHttpNetworkSession(context_getter_->GetURLRequestContext()); + + Value* socket_pool_info = NULL; + if (http_network_session) + socket_pool_info = http_network_session->SocketPoolInfoToValue(); + + CallJavascriptFunction(L"g_browser.receivedSocketPoolInfo", socket_pool_info); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdySessionInfo( + const ListValue* list) { + net::HttpNetworkSession* http_network_session = + GetHttpNetworkSession(context_getter_->GetURLRequestContext()); + + Value* spdy_info = NULL; + if (http_network_session) { + spdy_info = http_network_session->SpdySessionPoolInfoToValue(); + } + + CallJavascriptFunction(L"g_browser.receivedSpdySessionInfo", spdy_info); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyStatus( + const ListValue* list) { + DictionaryValue* status_dict = new DictionaryValue(); + + status_dict->Set("spdy_enabled", + Value::CreateBooleanValue( + net::HttpStreamFactory::spdy_enabled())); + status_dict->Set("use_alternate_protocols", + Value::CreateBooleanValue( + net::HttpStreamFactory::use_alternate_protocols())); + status_dict->Set("force_spdy_over_ssl", + Value::CreateBooleanValue( + net::HttpStreamFactory::force_spdy_over_ssl())); + status_dict->Set("force_spdy_always", + Value::CreateBooleanValue( + net::HttpStreamFactory::force_spdy_always())); + status_dict->Set("next_protos", + Value::CreateStringValue( + *net::HttpStreamFactory::next_protos())); + + CallJavascriptFunction(L"g_browser.receivedSpdyStatus", status_dict); +} + +void +NetInternalsMessageHandler::IOThreadImpl::OnGetSpdyAlternateProtocolMappings( + const ListValue* list) { + net::HttpNetworkSession* http_network_session = + GetHttpNetworkSession(context_getter_->GetURLRequestContext()); + + ListValue* dict_list = new ListValue(); + + if (http_network_session) { + const net::HttpAlternateProtocols& http_alternate_protocols = + http_network_session->alternate_protocols(); + const net::HttpAlternateProtocols::ProtocolMap& map = + http_alternate_protocols.protocol_map(); + + for (net::HttpAlternateProtocols::ProtocolMap::const_iterator it = + map.begin(); + it != map.end(); ++it) { + DictionaryValue* dict = new DictionaryValue(); + dict->SetString("host_port_pair", it->first.ToString()); + dict->SetString("alternate_protocol", it->second.ToString()); + dict_list->Append(dict); + } + } + + CallJavascriptFunction(L"g_browser.receivedSpdyAlternateProtocolMappings", + dict_list); +} + +#ifdef OS_WIN +void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders( + const ListValue* list) { + + DictionaryValue* service_providers = new DictionaryValue(); + + WinsockLayeredServiceProviderList layered_providers; + GetWinsockLayeredServiceProviders(&layered_providers); + ListValue* layered_provider_list = new ListValue(); + for (size_t i = 0; i < layered_providers.size(); ++i) { + DictionaryValue* service_dict = new DictionaryValue(); + service_dict->SetString("name", layered_providers[i].name); + service_dict->SetInteger("version", layered_providers[i].version); + service_dict->SetInteger("chain_length", layered_providers[i].chain_length); + service_dict->SetInteger("socket_type", layered_providers[i].socket_type); + service_dict->SetInteger("socket_protocol", + layered_providers[i].socket_protocol); + service_dict->SetString("path", layered_providers[i].path); + + layered_provider_list->Append(service_dict); + } + service_providers->Set("service_providers", layered_provider_list); + + WinsockNamespaceProviderList namespace_providers; + GetWinsockNamespaceProviders(&namespace_providers); + ListValue* namespace_list = new ListValue; + for (size_t i = 0; i < namespace_providers.size(); ++i) { + DictionaryValue* namespace_dict = new DictionaryValue(); + namespace_dict->SetString("name", namespace_providers[i].name); + namespace_dict->SetBoolean("active", namespace_providers[i].active); + namespace_dict->SetInteger("version", namespace_providers[i].version); + namespace_dict->SetInteger("type", namespace_providers[i].type); + + namespace_list->Append(namespace_dict); + } + service_providers->Set("namespace_providers", namespace_list); + + CallJavascriptFunction(L"g_browser.receivedServiceProviders", + service_providers); +} +#endif + +void NetInternalsMessageHandler::IOThreadImpl::OnSetLogLevel( + const ListValue* list) { + int log_level; + std::string log_level_string; + if (!list->GetString(0, &log_level_string) || + !base::StringToInt(log_level_string, &log_level)) { + NOTREACHED(); + return; + } + + DCHECK_GE(log_level, net::NetLog::LOG_ALL); + DCHECK_LE(log_level, net::NetLog::LOG_BASIC); + SetLogLevel(static_cast<net::NetLog::LogLevel>(log_level)); +} + +// Note that unlike other methods of IOThreadImpl, this function +// can be called from ANY THREAD. +void NetInternalsMessageHandler::IOThreadImpl::OnAddEntry( + net::NetLog::EventType type, + const base::TimeTicks& time, + const net::NetLog::Source& source, + net::NetLog::EventPhase phase, + net::NetLog::EventParameters* params) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod( + this, &IOThreadImpl::AddEntryToQueue, + net::NetLog::EntryToDictionaryValue(type, time, source, phase, + params, false))); +} + +void NetInternalsMessageHandler::IOThreadImpl::AddEntryToQueue(Value* entry) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!pending_entries_.get()) { + pending_entries_.reset(new ListValue()); + BrowserThread::PostDelayedTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(this, &IOThreadImpl::PostPendingEntries), + kNetLogEventDelayMilliseconds); + } + pending_entries_->Append(entry); +} + +void NetInternalsMessageHandler::IOThreadImpl::PostPendingEntries() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + CallJavascriptFunction( + L"g_browser.receivedLogEntries", + pending_entries_.release()); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestSuite() { + CallJavascriptFunction(L"g_browser.receivedStartConnectionTestSuite", NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::OnStartConnectionTestExperiment( + const ConnectionTester::Experiment& experiment) { + CallJavascriptFunction( + L"g_browser.receivedStartConnectionTestExperiment", + ExperimentToValue(experiment)); +} + +void +NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestExperiment( + const ConnectionTester::Experiment& experiment, + int result) { + DictionaryValue* dict = new DictionaryValue(); + + dict->Set("experiment", ExperimentToValue(experiment)); + dict->SetInteger("result", result); + + CallJavascriptFunction( + L"g_browser.receivedCompletedConnectionTestExperiment", + dict); +} + +void +NetInternalsMessageHandler::IOThreadImpl::OnCompletedConnectionTestSuite() { + CallJavascriptFunction( + L"g_browser.receivedCompletedConnectionTestSuite", + NULL); +} + +void NetInternalsMessageHandler::IOThreadImpl::DispatchToMessageHandler( + ListValue* arg, MessageHandler method) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + (this->*method)(arg); + delete arg; +} + +// Note that this can be called from ANY THREAD. +void NetInternalsMessageHandler::IOThreadImpl::CallJavascriptFunction( + const std::wstring& function_name, + Value* arg) { + if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { + if (handler_ && !was_webui_deleted_) { + // We check |handler_| in case it was deleted on the UI thread earlier + // while we were running on the IO thread. + handler_->CallJavascriptFunction(function_name, arg); + } + delete arg; + return; + } + + if (!BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod( + this, + &IOThreadImpl::CallJavascriptFunction, + function_name, arg))) { + // Failed posting the task, avoid leaking. + delete arg; + } +} + +} // namespace + + +//////////////////////////////////////////////////////////////////////////////// +// +// NetInternalsUI +// +//////////////////////////////////////////////////////////////////////////////// + +NetInternalsUI::NetInternalsUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new NetInternalsMessageHandler())->Attach(this)); + + NetInternalsHTMLSource* html_source = new NetInternalsHTMLSource(); + + // Set up the chrome://net-internals/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} diff --git a/chrome/browser/ui/webui/net_internals_ui.h b/chrome/browser/ui/webui/net_internals_ui.h new file mode 100644 index 0000000..023d76a --- /dev/null +++ b/chrome/browser/ui/webui/net_internals_ui.h @@ -0,0 +1,19 @@ +// 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 CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class NetInternalsUI : public WebUI { + public: + explicit NetInternalsUI(TabContents* contents); + + private: + DISALLOW_COPY_AND_ASSIGN(NetInternalsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_NET_INTERNALS_UI_H_ diff --git a/chrome/browser/ui/webui/new_tab_ui.cc b/chrome/browser/ui/webui/new_tab_ui.cc new file mode 100644 index 0000000..a2e28b7 --- /dev/null +++ b/chrome/browser/ui/webui/new_tab_ui.cc @@ -0,0 +1,604 @@ +// 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 "build/build_config.h" + +#include "chrome/browser/ui/webui/new_tab_ui.h" + +#include <set> + +#include "base/callback.h" +#include "base/command_line.h" +#include "base/i18n/rtl.h" +#include "base/metrics/histogram.h" +#include "base/singleton.h" +#include "base/string_number_conversions.h" +#include "base/threading/thread.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/sessions/session_types.h" +#include "chrome/browser/sessions/tab_restore_service.h" +#include "chrome/browser/sessions/tab_restore_service_observer.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/themes/browser_theme_provider.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/webui/app_launcher_handler.h" +#include "chrome/browser/webui/foreign_session_handler.h" +#include "chrome/browser/webui/most_visited_handler.h" +#include "chrome/browser/webui/new_tab_page_sync_handler.h" +#include "chrome/browser/webui/ntp_login_handler.h" +#include "chrome/browser/webui/ntp_resource_cache.h" +#include "chrome/browser/webui/shown_sections_handler.h" +#include "chrome/browser/webui/theme_source.h" +#include "chrome/browser/webui/tips_handler.h" +#include "chrome/browser/webui/value_helper.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +// The number of recent bookmarks we show. +const int kRecentBookmarks = 9; + +// The number of search URLs to show. +const int kSearchURLs = 3; + +// The amount of time there must be no painting for us to consider painting +// finished. Observed times are in the ~1200ms range on Windows. +const int kTimeoutMs = 2000; + +// Strings sent to the page via jstemplates used to set the direction of the +// HTML document based on locale. +const char kRTLHtmlTextDirection[] = "rtl"; +const char kDefaultHtmlTextDirection[] = "ltr"; + +/////////////////////////////////////////////////////////////////////////////// +// RecentlyClosedTabsHandler + +class RecentlyClosedTabsHandler : public WebUIMessageHandler, + public TabRestoreServiceObserver { + public: + RecentlyClosedTabsHandler() : tab_restore_service_(NULL) {} + virtual ~RecentlyClosedTabsHandler(); + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // 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 ListValue* args); + + // Callback for the "getRecentlyClosedTabs" message. + void HandleGetRecentlyClosedTabs(const ListValue* args); + + // Observer callback for TabRestoreServiceObserver. 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: + // TabRestoreService that we are observing. + TabRestoreService* tab_restore_service_; + + DISALLOW_COPY_AND_ASSIGN(RecentlyClosedTabsHandler); +}; + +void RecentlyClosedTabsHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("getRecentlyClosedTabs", + NewCallback(this, + &RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs)); + web_ui_->RegisterMessageCallback("reopenTab", + NewCallback(this, &RecentlyClosedTabsHandler::HandleReopenTab)); +} + +RecentlyClosedTabsHandler::~RecentlyClosedTabsHandler() { + if (tab_restore_service_) + tab_restore_service_->RemoveObserver(this); +} + +void RecentlyClosedTabsHandler::HandleReopenTab(const ListValue* args) { + Browser* browser = Browser::GetBrowserForController( + &web_ui_->tab_contents()->controller(), NULL); + if (!browser) + return; + + int session_to_restore; + if (ExtractIntegerValue(args, &session_to_restore)) + tab_restore_service_->RestoreEntryById(browser, session_to_restore, true); + // The current tab has been nuked at this point; don't touch any member + // variables. +} + +void RecentlyClosedTabsHandler::HandleGetRecentlyClosedTabs( + const ListValue* args) { + if (!tab_restore_service_) { + tab_restore_service_ = web_ui_->GetProfile()->GetTabRestoreService(); + + // GetTabRestoreService() can return NULL (i.e., when in Off the + // Record mode) + if (tab_restore_service_) { + // This does nothing if the tabs have already been loaded or they + // shouldn't be loaded. + tab_restore_service_->LoadTabsFromLastSession(); + + tab_restore_service_->AddObserver(this); + } + } + + if (tab_restore_service_) + TabRestoreServiceChanged(tab_restore_service_); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceChanged( + TabRestoreService* service) { + ListValue list_value; + NewTabUI::AddRecentlyClosedEntries(service->entries(), &list_value); + + web_ui_->CallJavascriptFunction(L"recentlyClosedTabs", list_value); +} + +void RecentlyClosedTabsHandler::TabRestoreServiceDestroyed( + TabRestoreService* service) { + tab_restore_service_ = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// MetricsHandler + +// Let the page contents record UMA actions. Only use when you can't do it from +// C++. For example, we currently use it to let the NTP log the postion of the +// Most Visited or Bookmark the user clicked on, as we don't get that +// information through RequestOpenURL. You will need to update the metrics +// dashboard with the action names you use, as our processor won't catch that +// information (treat it as RecordComputedMetrics) +class MetricsHandler : public WebUIMessageHandler { + public: + MetricsHandler() {} + virtual ~MetricsHandler() {} + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback which records a user action. + void HandleMetrics(const ListValue* args); + + // Callback for the "logEventTime" message. + void HandleLogEventTime(const ListValue* args); + + private: + + DISALLOW_COPY_AND_ASSIGN(MetricsHandler); +}; + +void MetricsHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("metrics", + NewCallback(this, &MetricsHandler::HandleMetrics)); + + web_ui_->RegisterMessageCallback("logEventTime", + NewCallback(this, &MetricsHandler::HandleLogEventTime)); +} + +void MetricsHandler::HandleMetrics(const ListValue* args) { + std::string string_action = WideToUTF8(ExtractStringValue(args)); + UserMetrics::RecordComputedAction(string_action, web_ui_->GetProfile()); +} + +void MetricsHandler::HandleLogEventTime(const ListValue* args) { + std::string event_name = WideToUTF8(ExtractStringValue(args)); + web_ui_->tab_contents()->LogNewTabTime(event_name); +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabPageSetHomePageHandler + +// Sets the new tab page as home page when user clicks on "make this my home +// page" link. +class NewTabPageSetHomePageHandler : public WebUIMessageHandler { + public: + NewTabPageSetHomePageHandler() {} + virtual ~NewTabPageSetHomePageHandler() {} + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for "setHomePage". + void HandleSetHomePage(const ListValue* args); + + private: + + DISALLOW_COPY_AND_ASSIGN(NewTabPageSetHomePageHandler); +}; + +void NewTabPageSetHomePageHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("setHomePage", NewCallback( + this, &NewTabPageSetHomePageHandler::HandleSetHomePage)); +} + +void NewTabPageSetHomePageHandler::HandleSetHomePage( + const ListValue* args) { + web_ui_->GetProfile()->GetPrefs()->SetBoolean(prefs::kHomePageIsNewTabPage, + true); + ListValue list_value; + list_value.Append(new StringValue( + l10n_util::GetStringUTF16(IDS_NEW_TAB_HOME_PAGE_SET_NOTIFICATION))); + list_value.Append(new StringValue( + l10n_util::GetStringUTF16(IDS_NEW_TAB_HOME_PAGE_HIDE_NOTIFICATION))); + web_ui_->CallJavascriptFunction(L"onHomePageSet", list_value); +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabPageClosePromoHandler + +// Turns off the promo line permanently when it has been explicitly closed by +// the user. +class NewTabPageClosePromoHandler : public WebUIMessageHandler { + public: + NewTabPageClosePromoHandler() {} + virtual ~NewTabPageClosePromoHandler() {} + + // WebUIMessageHandler implementation. + virtual void RegisterMessages(); + + // Callback for "closePromo". + void HandleClosePromo(const ListValue* args); + + private: + + DISALLOW_COPY_AND_ASSIGN(NewTabPageClosePromoHandler); +}; + +void NewTabPageClosePromoHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("closePromo", NewCallback( + this, &NewTabPageClosePromoHandler::HandleClosePromo)); +} + +void NewTabPageClosePromoHandler::HandleClosePromo( + const ListValue* args) { + web_ui_->GetProfile()->GetPrefs()->SetBoolean(prefs::kNTPPromoClosed, true); + NotificationService* service = NotificationService::current(); + service->Notify(NotificationType::PROMO_RESOURCE_STATE_CHANGED, + Source<NewTabPageClosePromoHandler>(this), + NotificationService::NoDetails()); +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// NewTabUI + +NewTabUI::NewTabUI(TabContents* contents) + : WebUI(contents) { + // Override some options on the Web UI. + hide_favicon_ = true; + force_bookmark_bar_visible_ = true; + focus_location_bar_by_default_ = true; + should_hide_url_ = true; + overridden_title_ = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE); + + // We count all link clicks as AUTO_BOOKMARK, so that site can be ranked more + // highly. Note this means we're including clicks on not only most visited + // thumbnails, but also clicks on recently bookmarked. + link_transition_type_ = PageTransition::AUTO_BOOKMARK; + + if (NewTabUI::FirstRunDisabled()) + NewTabHTMLSource::set_first_run(false); + + static bool first_view = true; + if (first_view) { + first_view = false; + } + + if (!GetProfile()->IsOffTheRecord()) { + PrefService* pref_service = GetProfile()->GetPrefs(); + AddMessageHandler((new NTPLoginHandler())->Attach(this)); + AddMessageHandler((new ShownSectionsHandler(pref_service))->Attach(this)); + AddMessageHandler((new browser_sync::ForeignSessionHandler())-> + Attach(this)); + AddMessageHandler((new MostVisitedHandler())->Attach(this)); + AddMessageHandler((new RecentlyClosedTabsHandler())->Attach(this)); + AddMessageHandler((new MetricsHandler())->Attach(this)); + if (GetProfile()->IsSyncAccessible()) + AddMessageHandler((new NewTabPageSyncHandler())->Attach(this)); + ExtensionService* service = GetProfile()->GetExtensionService(); + // We might not have an ExtensionService (on ChromeOS when not logged in + // for example). + if (service) + AddMessageHandler((new AppLauncherHandler(service))->Attach(this)); + + AddMessageHandler((new NewTabPageSetHomePageHandler())->Attach(this)); + AddMessageHandler((new NewTabPageClosePromoHandler())->Attach(this)); + } + + // Initializing the CSS and HTML can require some CPU, so do it after + // we've hooked up the most visited handler. This allows the DB query + // for the new tab thumbs to happen earlier. + InitializeCSSCaches(); + NewTabHTMLSource* html_source = + new NewTabHTMLSource(GetProfile()->GetOriginalProfile()); + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); + + // Listen for theme installation. + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, + NotificationService::AllSources()); + // Listen for bookmark bar visibility changes. + registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, + NotificationService::AllSources()); +} + +NewTabUI::~NewTabUI() { +} + +// The timer callback. If enough time has elapsed since the last paint +// message, we say we're done painting; otherwise, we keep waiting. +void NewTabUI::PaintTimeout() { + // The amount of time there must be no painting for us to consider painting + // finished. Observed times are in the ~1200ms range on Windows. + base::TimeTicks now = base::TimeTicks::Now(); + if ((now - last_paint_) >= base::TimeDelta::FromMilliseconds(kTimeoutMs)) { + // Painting has quieted down. Log this as the full time to run. + base::TimeDelta load_time = last_paint_ - start_; + int load_time_ms = static_cast<int>(load_time.InMilliseconds()); + NotificationService::current()->Notify( + NotificationType::INITIAL_NEW_TAB_UI_LOAD, + NotificationService::AllSources(), + Details<int>(&load_time_ms)); + UMA_HISTOGRAM_TIMES("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. + timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this, + &NewTabUI::PaintTimeout); + } +} + +void NewTabUI::StartTimingPaint(RenderViewHost* render_view_host) { + start_ = base::TimeTicks::Now(); + last_paint_ = start_; + registrar_.Add(this, NotificationType::RENDER_WIDGET_HOST_DID_PAINT, + Source<RenderWidgetHost>(render_view_host)); + timer_.Start(base::TimeDelta::FromMilliseconds(kTimeoutMs), this, + &NewTabUI::PaintTimeout); + +} +void NewTabUI::RenderViewCreated(RenderViewHost* render_view_host) { + StartTimingPaint(render_view_host); +} + +void NewTabUI::RenderViewReused(RenderViewHost* render_view_host) { + StartTimingPaint(render_view_host); +} + +void NewTabUI::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::BROWSER_THEME_CHANGED: { + InitializeCSSCaches(); + ListValue args; + args.Append(Value::CreateStringValue( + GetProfile()->GetThemeProvider()->HasCustomImage( + IDR_THEME_NTP_ATTRIBUTION) ? + "true" : "false")); + CallJavascriptFunction(L"themeChanged", args); + break; + } + case NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED: { + if (GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar)) + CallJavascriptFunction(L"bookmarkBarAttached"); + else + CallJavascriptFunction(L"bookmarkBarDetached"); + break; + } + case NotificationType::RENDER_WIDGET_HOST_DID_PAINT: { + last_paint_ = base::TimeTicks::Now(); + break; + } + default: + CHECK(false) << "Unexpected notification: " << type.value; + } +} + +void NewTabUI::InitializeCSSCaches() { + Profile* profile = GetProfile(); + ThemeSource* theme = new ThemeSource(profile); + profile->GetChromeURLDataManager()->AddDataSource(theme); +} + +// static +void NewTabUI::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterIntegerPref(prefs::kNTPPrefVersion, 0); + + MostVisitedHandler::RegisterUserPrefs(prefs); + ShownSectionsHandler::RegisterUserPrefs(prefs); + if (NewTabUI::WebResourcesEnabled()) + TipsHandler::RegisterUserPrefs(prefs); + + UpdateUserPrefsVersion(prefs); +} + +// static +bool NewTabUI::UpdateUserPrefsVersion(PrefService* prefs) { + const int old_pref_version = prefs->GetInteger(prefs::kNTPPrefVersion); + if (old_pref_version != current_pref_version()) { + MigrateUserPrefs(prefs, old_pref_version, current_pref_version()); + prefs->SetInteger(prefs::kNTPPrefVersion, current_pref_version()); + return true; + } + return false; +} + +// static +void NewTabUI::MigrateUserPrefs(PrefService* prefs, int old_pref_version, + int new_pref_version) { + ShownSectionsHandler::MigrateUserPrefs(prefs, old_pref_version, + current_pref_version()); +} + +// static +bool NewTabUI::WebResourcesEnabled() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + return !command_line->HasSwitch(switches::kDisableWebResources); +} + +// static +bool NewTabUI::FirstRunDisabled() { + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(switches::kDisableNewTabFirstRun); +} + +// static +void NewTabUI::SetURLTitleAndDirection(DictionaryValue* dictionary, + const string16& title, + const GURL& gurl) { + dictionary->SetString("url", gurl.spec()); + + bool using_url_as_the_title = false; + string16 title_to_set(title); + if (title_to_set.empty()) { + using_url_as_the_title = true; + title_to_set = UTF8ToUTF16(gurl.spec()); + } + + // We set the "dir" attribute of the title, so that in RTL locales, a LTR + // title is rendered left-to-right and truncated from the right. For example, + // the title of http://msdn.microsoft.com/en-us/default.aspx is "MSDN: + // Microsoft developer network". In RTL locales, in the [New Tab] page, if + // the "dir" of this title is not specified, it takes Chrome UI's + // directionality. So the title will be truncated as "soft developer + // network". Setting the "dir" attribute as "ltr" renders the truncated title + // as "MSDN: Microsoft D...". As another example, the title of + // http://yahoo.com is "Yahoo!". In RTL locales, in the [New Tab] page, the + // title will be rendered as "!Yahoo" if its "dir" attribute is not set to + // "ltr". + // + // 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. Simply setting the title's "dir" attribute works + // fine for rendering and truncating the title. However, it does not work for + // entire title within a tooltip when the mouse is over the title link.. For + // example, without LRE-PDF pair, the title "Yahoo!" will be rendered as + // "!Yahoo" within the tooltip when the mouse is over the title link. + std::string direction = kDefaultHtmlTextDirection; + if (base::i18n::IsRTL()) { + if (using_url_as_the_title) { + base::i18n::WrapStringWithLTRFormatting(&title_to_set); + } else { + if (base::i18n::StringContainsStrongRTLChars(title)) { + base::i18n::WrapStringWithRTLFormatting(&title_to_set); + direction = kRTLHtmlTextDirection; + } else { + base::i18n::WrapStringWithLTRFormatting(&title_to_set); + } + } + } + dictionary->SetString("title", title_to_set); + dictionary->SetString("direction", direction); +} + +namespace { + +bool IsTabUnique(const DictionaryValue* tab, + std::set<std::string>* unique_items) { + DCHECK(unique_items); + std::string title; + std::string url; + if (tab->GetString("title", &title) && + tab->GetString("url", &url)) { + // TODO(viettrungluu): this isn't obviously reliable, since different + // combinations of titles/urls may conceivably yield the same string. + std::string unique_key = title + url; + if (unique_items->find(unique_key) != unique_items->end()) + return false; + else + unique_items->insert(unique_key); + } + return true; +} + +} // namespace + +// static +void NewTabUI::AddRecentlyClosedEntries( + const TabRestoreService::Entries& entries, ListValue* entry_list_value) { + const int max_count = 10; + int added_count = 0; + std::set<std::string> unique_items; + // We filter the list of recently closed to only show 'interesting' entries, + // where an interesting entry is either a closed window or a closed tab + // whose selected navigation is not the new tab ui. + for (TabRestoreService::Entries::const_iterator it = entries.begin(); + it != entries.end() && added_count < max_count; ++it) { + TabRestoreService::Entry* entry = *it; + scoped_ptr<DictionaryValue> entry_dict(new DictionaryValue()); + if ((entry->type == TabRestoreService::TAB && + ValueHelper::TabToValue( + *static_cast<TabRestoreService::Tab*>(entry), + entry_dict.get()) && + IsTabUnique(entry_dict.get(), &unique_items)) || + (entry->type == TabRestoreService::WINDOW && + ValueHelper::WindowToValue( + *static_cast<TabRestoreService::Window*>(entry), + entry_dict.get()))) { + entry_dict->SetInteger("sessionId", entry->id); + entry_list_value->Append(entry_dict.release()); + added_count++; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// NewTabHTMLSource + +bool NewTabUI::NewTabHTMLSource::first_run_ = true; + +NewTabUI::NewTabHTMLSource::NewTabHTMLSource(Profile* profile) + : DataSource(chrome::kChromeUINewTabHost, MessageLoop::current()), + profile_(profile) { +} + +void NewTabUI::NewTabHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (AppLauncherHandler::HandlePing(profile_, path)) { + return; + } else if (!path.empty() && path[0] != '#') { + // 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; + } + + scoped_refptr<RefCountedBytes> html_bytes( + profile_->GetNTPResourceCache()->GetNewTabHTML(is_off_the_record)); + + SendResponse(request_id, html_bytes); +} + +std::string NewTabUI::NewTabHTMLSource::GetMimeType(const std::string&) const { + return "text/html"; +} + +bool NewTabUI::NewTabHTMLSource::ShouldReplaceExistingSource() const { + return false; +} diff --git a/chrome/browser/ui/webui/new_tab_ui.h b/chrome/browser/ui/webui/new_tab_ui.h new file mode 100644 index 0000000..005482c --- /dev/null +++ b/chrome/browser/ui/webui/new_tab_ui.h @@ -0,0 +1,123 @@ +// 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 CHROME_BROWSER_UI_WEBUI_NEW_TAB_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_NEW_TAB_UI_H_ +#pragma once + +#include <string> + +#include "base/gtest_prod_util.h" +#include "base/timer.h" +#include "chrome/browser/sessions/tab_restore_service.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "content/browser/webui/web_ui.h" + +class GURL; +class MessageLoop; +class PrefService; +class Profile; + +// The TabContents used for the New Tab page. +class NewTabUI : public WebUI, + public NotificationObserver { + public: + explicit NewTabUI(TabContents* manager); + ~NewTabUI(); + + // Override WebUI methods so we can hook up the paint timer to the render + // view host. + virtual void RenderViewCreated(RenderViewHost* render_view_host); + virtual void RenderViewReused(RenderViewHost* render_view_host); + + static void RegisterUserPrefs(PrefService* prefs); + static void MigrateUserPrefs(PrefService* prefs, int old_pref_version, + int new_pref_version); + + // Whether we should disable the web resources backend service + static bool WebResourcesEnabled(); + + // Whether we should disable the first run notification based on the command + // line switch. + static bool FirstRunDisabled(); + + // Adds "url", "title", and "direction" keys on incoming dictionary, setting + // title as the url as a fallback on empty title. + static void SetURLTitleAndDirection(DictionaryValue* dictionary, + const string16& title, + const GURL& gurl); + + // Converts a list of TabRestoreService entries to the JSON format required + // by the NTP and adds them to the given list value. + static void AddRecentlyClosedEntries( + const TabRestoreService::Entries& entries, + ListValue* entry_list_value); + + // The current preference version. + static int current_pref_version() { return current_pref_version_; } + + class NewTabHTMLSource : public ChromeURLDataManager::DataSource { + public: + explicit NewTabHTMLSource(Profile* profile); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + + virtual std::string GetMimeType(const std::string&) const; + + virtual bool ShouldReplaceExistingSource() const; + + // Setters and getters for first_run. + static void set_first_run(bool first_run) { first_run_ = first_run; } + static bool first_run() { return first_run_; } + + private: + virtual ~NewTabHTMLSource() {} + + // Whether this is the first run. + static bool first_run_; + + // Pointer back to the original profile. + Profile* profile_; + + DISALLOW_COPY_AND_ASSIGN(NewTabHTMLSource); + }; + + private: + FRIEND_TEST_ALL_PREFIXES(NewTabUITest, UpdateUserPrefsVersion); + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Reset the CSS caches. + void InitializeCSSCaches(); + + void StartTimingPaint(RenderViewHost* render_view_host); + void PaintTimeout(); + + // Updates the user prefs version and calls |MigrateUserPrefs| if needed. + // Returns true if the version was updated. + static bool UpdateUserPrefsVersion(PrefService* prefs); + + NotificationRegistrar registrar_; + + // The time when we started benchmarking. + base::TimeTicks start_; + // The last time we got a paint notification. + base::TimeTicks last_paint_; + // Scoping so we can be sure our timeouts don't outlive us. + base::OneShotTimer<NewTabUI> timer_; + // The preference version. This used for migrating prefs of the NTP. + static const int current_pref_version_ = 3; + + DISALLOW_COPY_AND_ASSIGN(NewTabUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_NEW_TAB_UI_H_ diff --git a/chrome/browser/ui/webui/new_tab_ui_uitest.cc b/chrome/browser/ui/webui/new_tab_ui_uitest.cc new file mode 100644 index 0000000..3164fb6 --- /dev/null +++ b/chrome/browser/ui/webui/new_tab_ui_uitest.cc @@ -0,0 +1,183 @@ +// 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/test/ui/ui_test.h" + +#include "base/test/test_timeouts.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/prefs/pref_value_store.h" +#include "chrome/browser/sync/signin_manager.h" +#include "chrome/browser/ui/webui/new_tab_ui.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/json_pref_store.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/automation/window_proxy.h" +#include "chrome/test/testing_pref_service.h" + +class NewTabUITest : public UITest { + public: + NewTabUITest() { + dom_automation_enabled_ = true; + // Set home page to the empty string so that we can set the home page using + // preferences. + set_homepage(""); + + // Setup the DEFAULT_THEME profile (has fake history entries). + set_template_user_data(UITest::ComputeTypicalUserDataSource( + ProxyLauncher::DEFAULT_THEME)); + } +}; + +TEST_F(NewTabUITest, NTPHasThumbnails) { + // Switch to the "new tab" tab, which should be any new tab after the + // first (the first is about:blank). + scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + // Bring up a new tab page. + ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB)); + + scoped_refptr<TabProxy> tab = window->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + // TopSites should return at least 3 non-filler pages. + // 8 - 3 = max 5 filler pages. + ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"", + L"window.domAutomationController.send(" + L"document.getElementsByClassName('filler').length <= 5)", + TestTimeouts::action_max_timeout_ms())); +} + +// Sometimes hangs: http://crbug.com/70157 +TEST_F(NewTabUITest, DISABLED_NTPHasLoginName) { + scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + ASSERT_TRUE(window->SetStringPreference(prefs::kGoogleServicesUsername, + "user@gmail.com")); + // Bring up a new tab page. + ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB)); + + scoped_refptr<TabProxy> tab = window->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + std::wstring displayed_username; + // The login span should be eventually populated and have the + // correct value. + ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"", + L"window.domAutomationController.send(" + L"document.getElementById('login-username').innerText.length > 0)", + TestTimeouts::action_max_timeout_ms())); + + ASSERT_TRUE(tab->ExecuteAndExtractString( + L"", + L"window.domAutomationController.send(" + L"document.getElementById('login-username').innerText)", + &displayed_username)); + + EXPECT_EQ(L"user@gmail.com", displayed_username); +} + +// Loads about:hang into two NTP tabs, ensuring we don't crash. +// See http://crbug.com/59859. +TEST_F(NewTabUITest, AboutHangInNTP) { + scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + // Bring up a new tab page. + ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB)); + scoped_refptr<TabProxy> tab = window->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + // Navigate to about:hang to stall the process. + ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutHangURL))); + + // Visit about:hang again in another NTP. Don't bother waiting for the + // NTP to load, because it's hung. + ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB)); + scoped_refptr<TabProxy> tab2 = window->GetActiveTab(); + ASSERT_TRUE(tab2.get()); + ASSERT_TRUE(tab2->NavigateToURLAsync(GURL(chrome::kAboutHangURL))); +} + +// Allows testing NTP in process-per-tab mode. +class NewTabUIProcessPerTabTest : public NewTabUITest { + public: + NewTabUIProcessPerTabTest() : NewTabUITest() {} + + protected: + virtual void SetUp() { + launch_arguments_.AppendSwitch(switches::kProcessPerTab); + UITest::SetUp(); + } +}; + +// Navigates away from NTP before it commits, in process-per-tab mode. +// Ensures that we don't load the normal page in the NTP process (and thus +// crash), as in http://crbug.com/69224. +TEST_F(NewTabUIProcessPerTabTest, NavBeforeNTPCommits) { + scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + // Bring up a new tab page. + ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB)); + scoped_refptr<TabProxy> tab = window->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + // Navigate to about:hang to stall the process. + ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutHangURL))); + + // Visit a normal URL in another NTP that hasn't committed. + ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB)); + scoped_refptr<TabProxy> tab2 = window->GetActiveTab(); + ASSERT_TRUE(tab2.get()); + ASSERT_TRUE(tab2->NavigateToURL(GURL("data:text/html,hello world"))); +} + +// Fails about ~5% of the time on all platforms. http://crbug.com/45001 +TEST_F(NewTabUITest, FLAKY_ChromeInternalLoadsNTP) { + scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + // Go to the "new tab page" using its old url, rather than chrome://newtab. + scoped_refptr<TabProxy> tab = window->GetTab(0); + ASSERT_TRUE(tab.get()); + ASSERT_TRUE(tab->NavigateToURLAsync(GURL("chrome-internal:"))); + int load_time; + ASSERT_TRUE(automation()->WaitForInitialNewTabUILoad(&load_time)); + + // Ensure there are some thumbnails loaded in the page. + int thumbnails_count = -1; + ASSERT_TRUE(tab->ExecuteAndExtractInt(L"", + L"window.domAutomationController.send(" + L"document.getElementsByClassName('thumbnail-container').length)", + &thumbnails_count)); + EXPECT_GT(thumbnails_count, 0); +} + +TEST_F(NewTabUITest, UpdateUserPrefsVersion) { + // PrefService with JSON user-pref file only, no enforced or advised prefs. + scoped_ptr<PrefService> prefs(new TestingPrefService); + + // Does the migration + NewTabUI::RegisterUserPrefs(prefs.get()); + + ASSERT_EQ(NewTabUI::current_pref_version(), + prefs->GetInteger(prefs::kNTPPrefVersion)); + + // Reset the version + prefs->ClearPref(prefs::kNTPPrefVersion); + ASSERT_EQ(0, prefs->GetInteger(prefs::kNTPPrefVersion)); + + bool migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get()); + ASSERT_TRUE(migrated); + ASSERT_EQ(NewTabUI::current_pref_version(), + prefs->GetInteger(prefs::kNTPPrefVersion)); + + migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get()); + ASSERT_FALSE(migrated); +} diff --git a/chrome/browser/ui/webui/plugins_ui.cc b/chrome/browser/ui/webui/plugins_ui.cc new file mode 100644 index 0000000..fed678c --- /dev/null +++ b/chrome/browser/ui/webui/plugins_ui.cc @@ -0,0 +1,374 @@ +// 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/ui/webui/plugins_ui.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/plugin_updater.h" +#include "chrome/browser/prefs/pref_member.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pepper_plugin_registry.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "webkit/plugins/npapi/plugin_list.h" + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// +// PluginsHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +class PluginsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + PluginsUIHTMLSource() + : DataSource(chrome::kChromeUIPluginsHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~PluginsUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(PluginsUIHTMLSource); +}; + +void PluginsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + // Strings used in the JsTemplate file. + DictionaryValue localized_strings; + localized_strings.SetString("pluginsTitle", + l10n_util::GetStringUTF16(IDS_PLUGINS_TITLE)); + localized_strings.SetString("pluginsDetailsModeLink", + l10n_util::GetStringUTF16(IDS_PLUGINS_DETAILS_MODE_LINK)); + localized_strings.SetString("pluginsNoneInstalled", + l10n_util::GetStringUTF16(IDS_PLUGINS_NONE_INSTALLED)); + localized_strings.SetString("pluginDisabled", + l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_PLUGIN)); + localized_strings.SetString("pluginDisabledByPolicy", + l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN)); + localized_strings.SetString("pluginCannotBeEnabledDueToPolicy", + l10n_util::GetStringUTF16(IDS_PLUGINS_CANNOT_ENABLE_DUE_TO_POLICY)); + localized_strings.SetString("pluginDownload", + l10n_util::GetStringUTF16(IDS_PLUGINS_DOWNLOAD)); + localized_strings.SetString("pluginName", + l10n_util::GetStringUTF16(IDS_PLUGINS_NAME)); + localized_strings.SetString("pluginVersion", + l10n_util::GetStringUTF16(IDS_PLUGINS_VERSION)); + localized_strings.SetString("pluginDescription", + l10n_util::GetStringUTF16(IDS_PLUGINS_DESCRIPTION)); + localized_strings.SetString("pluginPath", + l10n_util::GetStringUTF16(IDS_PLUGINS_PATH)); + localized_strings.SetString("pluginMimeTypes", + l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES)); + localized_strings.SetString("pluginMimeTypesMimeType", + l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_MIME_TYPE)); + localized_strings.SetString("pluginMimeTypesDescription", + l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_DESCRIPTION)); + localized_strings.SetString("pluginMimeTypesFileExtensions", + l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS)); + localized_strings.SetString("disable", + l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLE)); + localized_strings.SetString("enable", + l10n_util::GetStringUTF16(IDS_PLUGINS_ENABLE)); + + ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece plugins_html( + ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_PLUGINS_HTML)); + std::string full_html(plugins_html.data(), plugins_html.size()); + jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html); + jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html); + jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html); + jstemplate_builder::AppendJsTemplateSourceHtml(&full_html); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// PluginsDOMHandler +// +//////////////////////////////////////////////////////////////////////////////// + +// The handler for Javascript messages for the chrome://plugins/ page. +// TODO(viettrungluu): Make plugin list updates notify, and then observe +// changes; maybe replumb plugin list through plugin service? +// <http://crbug.com/39101> +class PluginsDOMHandler : public WebUIMessageHandler, + public NotificationObserver { + public: + explicit PluginsDOMHandler(); + virtual ~PluginsDOMHandler() {} + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + // Callback for the "requestPluginsData" message. + void HandleRequestPluginsData(const ListValue* args); + + // Callback for the "enablePlugin" message. + void HandleEnablePluginMessage(const ListValue* args); + + // Callback for the "showTermsOfService" message. This really just opens a new + // window with about:terms. Flash can't link directly to about:terms due to + // the security model. + void HandleShowTermsOfServiceMessage(const ListValue* args); + + // Callback for the "saveShowDetailsToPrefs" message. + void HandleSaveShowDetailsToPrefs(const ListValue* args); + + // Calback for the "getShowDetails" message. + void HandleGetShowDetails(const ListValue* args); + + // NotificationObserver method overrides + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + // This extra wrapper is used to ensure we don't leak the ListValue* pointer + // if the PluginsDOMHandler object goes away before the task on the UI thread + // to give it the plugin list runs. + struct ListWrapper { + ListValue* list; + }; + // Loads the plugins on the FILE thread. + static void LoadPluginsOnFileThread(ListWrapper* wrapper, Task* task); + + // Used in conjunction with ListWrapper to avoid any memory leaks. + static void EnsureListDeleted(ListWrapper* wrapper); + + // Call this to start getting the plugins on the UI thread. + void LoadPlugins(); + + // Called on the UI thread when the plugin information is ready. + void PluginsLoaded(ListWrapper* wrapper); + + NotificationRegistrar registrar_; + + ScopedRunnableMethodFactory<PluginsDOMHandler> get_plugins_factory_; + + // This pref guards the value whether about:plugins is in the details mode or + // not. + BooleanPrefMember show_details_; + + DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler); +}; + +PluginsDOMHandler::PluginsDOMHandler() + : ALLOW_THIS_IN_INITIALIZER_LIST(get_plugins_factory_(this)) { + registrar_.Add(this, + NotificationType::PLUGIN_ENABLE_STATUS_CHANGED, + NotificationService::AllSources()); +} + +WebUIMessageHandler* PluginsDOMHandler::Attach(WebUI* web_ui) { + PrefService* prefs = web_ui->GetProfile()->GetPrefs(); + + show_details_.Init(prefs::kPluginsShowDetails, prefs, this); + + return WebUIMessageHandler::Attach(web_ui); +} + +void PluginsDOMHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("requestPluginsData", + NewCallback(this, &PluginsDOMHandler::HandleRequestPluginsData)); + web_ui_->RegisterMessageCallback("enablePlugin", + NewCallback(this, &PluginsDOMHandler::HandleEnablePluginMessage)); + web_ui_->RegisterMessageCallback("showTermsOfService", + NewCallback(this, &PluginsDOMHandler::HandleShowTermsOfServiceMessage)); + web_ui_->RegisterMessageCallback("saveShowDetailsToPrefs", + NewCallback(this, &PluginsDOMHandler::HandleSaveShowDetailsToPrefs)); + web_ui_->RegisterMessageCallback("getShowDetails", + NewCallback(this, &PluginsDOMHandler::HandleGetShowDetails)); +} + +void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) { + LoadPlugins(); +} + +void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) { + // Be robust in accepting badness since plug-ins display HTML (hence + // JavaScript). + if (args->GetSize() != 3) + return; + + std::string enable_str; + std::string is_group_str; + if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) + return; + bool enable = enable_str == "true"; + + PluginUpdater* plugin_updater = PluginUpdater::GetInstance(); + if (is_group_str == "true") { + string16 group_name; + if (!args->GetString(0, &group_name)) + return; + + plugin_updater->EnablePluginGroup(enable, group_name); + if (enable) { + // See http://crbug.com/50105 for background. + string16 adobereader = ASCIIToUTF16( + webkit::npapi::PluginGroup::kAdobeReaderGroupName); + string16 internalpdf = ASCIIToUTF16(PepperPluginRegistry::kPDFPluginName); + if (group_name == adobereader) { + plugin_updater->EnablePluginGroup(false, internalpdf); + } else if (group_name == internalpdf) { + plugin_updater->EnablePluginGroup(false, adobereader); + } + } + } else { + FilePath::StringType file_path; + if (!args->GetString(0, &file_path)) + return; + + plugin_updater->EnablePlugin(enable, file_path); + } + + // TODO(viettrungluu): We might also want to ensure that the plugins + // list is always written to prefs even when the user hasn't disabled a + // plugin. <http://crbug.com/39101> + plugin_updater->UpdatePreferences(web_ui_->GetProfile(), 0); +} + +void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const ListValue* args) { + // Show it in a new browser window.... + Browser* browser = Browser::Create(web_ui_->GetProfile()); + browser->OpenURL(GURL(chrome::kAboutTermsURL), + GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + browser->window()->Show(); +} + +void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) { + std::string details_mode; + if (!args->GetString(0, &details_mode)) { + NOTREACHED(); + return; + } + show_details_.SetValue(details_mode == "true"); +} + +void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) { + FundamentalValue show_details(show_details_.GetValue()); + web_ui_->CallJavascriptFunction(L"loadShowDetailsFromPrefs", show_details); +} + +void PluginsDOMHandler::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK_EQ(NotificationType::PLUGIN_ENABLE_STATUS_CHANGED, type.value); + LoadPlugins(); +} + +void PluginsDOMHandler::LoadPluginsOnFileThread(ListWrapper* wrapper, + Task* task) { + wrapper->list = PluginUpdater::GetInstance()->GetPluginGroupsData(); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + NewRunnableFunction(&PluginsDOMHandler::EnsureListDeleted, wrapper)); +} + +void PluginsDOMHandler::EnsureListDeleted(ListWrapper* wrapper) { + delete wrapper->list; + delete wrapper; +} + +void PluginsDOMHandler::LoadPlugins() { + if (!get_plugins_factory_.empty()) + return; + + ListWrapper* wrapper = new ListWrapper; + wrapper->list = NULL; + Task* task = get_plugins_factory_.NewRunnableMethod( + &PluginsDOMHandler::PluginsLoaded, wrapper); + + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + NewRunnableFunction( + &PluginsDOMHandler::LoadPluginsOnFileThread, wrapper, task)); +} + +void PluginsDOMHandler::PluginsLoaded(ListWrapper* wrapper) { + DictionaryValue results; + results.Set("plugins", wrapper->list); + wrapper->list = NULL; // So it doesn't get deleted. + web_ui_->CallJavascriptFunction(L"returnPluginsData", results); +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// PluginsUI +// +/////////////////////////////////////////////////////////////////////////////// + +PluginsUI::PluginsUI(TabContents* contents) : WebUI(contents) { + AddMessageHandler((new PluginsDOMHandler())->Attach(this)); + + PluginsUIHTMLSource* html_source = new PluginsUIHTMLSource(); + + // Set up the chrome://plugins/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + + +// static +RefCountedMemory* PluginsUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + LoadDataResourceBytes(IDR_PLUGIN); +} + +// static +void PluginsUI::RegisterUserPrefs(PrefService* prefs) { + FilePath internal_dir; + PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir); + prefs->RegisterFilePathPref(prefs::kPluginsLastInternalDirectory, + internal_dir); + + prefs->RegisterListPref(prefs::kPluginsPluginsBlacklist); + prefs->RegisterListPref(prefs::kPluginsPluginsList); + prefs->RegisterBooleanPref(prefs::kPluginsEnabledInternalPDF, false); + prefs->RegisterBooleanPref(prefs::kPluginsShowDetails, false); + prefs->RegisterBooleanPref(prefs::kPluginsShowSetReaderDefaultInfobar, true); +} diff --git a/chrome/browser/ui/webui/plugins_ui.h b/chrome/browser/ui/webui/plugins_ui.h new file mode 100644 index 0000000..752e639 --- /dev/null +++ b/chrome/browser/ui/webui/plugins_ui.h @@ -0,0 +1,25 @@ +// 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 CHROME_BROWSER_UI_WEBUI_PLUGINS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_PLUGINS_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class PrefService; +class RefCountedMemory; + +class PluginsUI : public WebUI { + public: + explicit PluginsUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + static void RegisterUserPrefs(PrefService* prefs); + + private: + DISALLOW_COPY_AND_ASSIGN(PluginsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_PLUGINS_UI_H_ diff --git a/chrome/browser/ui/webui/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview_ui.cc new file mode 100644 index 0000000..8cb2212 --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui.cc @@ -0,0 +1,36 @@ +// 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/ui/webui/print_preview_ui.h" + +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/webui/print_preview_ui_html_source.h" +#include "chrome/browser/webui/print_preview_handler.h" + +PrintPreviewUI::PrintPreviewUI(TabContents* contents) + : WebUI(contents), + html_source_(new PrintPreviewUIHTMLSource()) { + // PrintPreviewUI owns |handler|. + PrintPreviewHandler* handler = new PrintPreviewHandler(); + AddMessageHandler(handler->Attach(this)); + + // Set up the chrome://print/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source_); +} + +PrintPreviewUI::~PrintPreviewUI() { +} + +PrintPreviewUIHTMLSource* PrintPreviewUI::html_source() { + return html_source_.get(); +} + +void PrintPreviewUI::PreviewDataIsAvailable(int expected_pages_count) { + StringValue dummy_url("chrome://print/print.pdf"); + FundamentalValue pages_count(expected_pages_count); + CallJavascriptFunction(L"createPDFPlugin", dummy_url, pages_count); +} diff --git a/chrome/browser/ui/webui/print_preview_ui.h b/chrome/browser/ui/webui/print_preview_ui.h new file mode 100644 index 0000000..b4a62cf --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui.h @@ -0,0 +1,31 @@ +// 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 CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_H_ +#pragma once + +#include "base/ref_counted.h" +#include "content/browser/webui/web_ui.h" + +class PrintPreviewUIHTMLSource; + +class PrintPreviewUI : public WebUI { + public: + explicit PrintPreviewUI(TabContents* contents); + virtual ~PrintPreviewUI(); + + PrintPreviewUIHTMLSource* html_source(); + + // Notify the Web UI renderer that preview data is available. + // |expected_pages_count| specifies the total number of pages. + void PreviewDataIsAvailable(int expected_pages_count); + + private: + scoped_refptr<PrintPreviewUIHTMLSource> html_source_; + + DISALLOW_COPY_AND_ASSIGN(PrintPreviewUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_H_ diff --git a/chrome/browser/ui/webui/print_preview_ui_html_source.cc b/chrome/browser/ui/webui/print_preview_ui_html_source.cc new file mode 100644 index 0000000..9e37c64 --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui_html_source.cc @@ -0,0 +1,123 @@ +// 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/ui/webui/print_preview_ui_html_source.h" + +#include <algorithm> +#include <vector> + +#include "base/message_loop.h" +#include "base/shared_memory.h" +#include "base/string_piece.h" +#include "base/values.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +void SetLocalizedStrings(DictionaryValue* localized_strings) { + localized_strings->SetString(std::string("title"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_TITLE)); + localized_strings->SetString(std::string("loading"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_LOADING)); + localized_strings->SetString(std::string("noPlugin"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_NO_PLUGIN)); + localized_strings->SetString(std::string("noPrinter"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_NO_PRINTER)); + + localized_strings->SetString(std::string("printButton"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_PRINT_BUTTON)); + localized_strings->SetString(std::string("cancelButton"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_CANCEL_BUTTON)); + + localized_strings->SetString(std::string("optionAllPages"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_ALL_PAGES)); + localized_strings->SetString(std::string("optionBw"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_BW)); + localized_strings->SetString(std::string("optionCollate"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_COLLATE)); + localized_strings->SetString(std::string("optionColor"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_COLOR)); + localized_strings->SetString(std::string("optionLandscape"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_LANDSCAPE)); + localized_strings->SetString(std::string("optionPortrait"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_PORTRAIT)); + localized_strings->SetString(std::string("optionTwoSided"), + l10n_util::GetStringUTF8(IDS_PRINT_PREVIEW_OPTION_TWO_SIDED)); +} + +} // namespace + +PrintPreviewUIHTMLSource::PrintPreviewUIHTMLSource() + : DataSource(chrome::kChromeUIPrintHost, MessageLoop::current()), + data_(std::make_pair(static_cast<base::SharedMemory*>(NULL), 0U)) { +} + +PrintPreviewUIHTMLSource::~PrintPreviewUIHTMLSource() { + delete data_.first; +} + +void PrintPreviewUIHTMLSource::GetPrintPreviewData(PrintPreviewData* data) { + *data = data_; +} + +void PrintPreviewUIHTMLSource::SetPrintPreviewData( + const PrintPreviewData& data) { + delete data_.first; + data_ = data; +} + +void PrintPreviewUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + if (path.empty()) { + // Print Preview Index page. + DictionaryValue localized_strings; + SetLocalizedStrings(&localized_strings); + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece print_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_PRINT_PREVIEW_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + print_html, &localized_strings); + + 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); + return; + } else if (path == "print.pdf" && data_.first) { + // Print Preview data. + char* preview_data = reinterpret_cast<char*>(data_.first->memory()); + uint32 preview_data_size = data_.second; + + scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); + html_bytes->data.resize(preview_data_size); + std::vector<unsigned char>::iterator it = html_bytes->data.begin(); + for (uint32 i = 0; i < preview_data_size; ++i, ++it) + *it = *(preview_data + i); + SendResponse(request_id, html_bytes); + return; + } else { + // Invalid request. + scoped_refptr<RefCountedBytes> empty_bytes(new RefCountedBytes); + SendResponse(request_id, empty_bytes); + return; + } +} + +std::string PrintPreviewUIHTMLSource::GetMimeType( + const std::string& path) const { + // Print Preview Index page. + if (path.empty()) + return "text/html"; + // Print Preview data. + return "application/pdf"; +} diff --git a/chrome/browser/ui/webui/print_preview_ui_html_source.h b/chrome/browser/ui/webui/print_preview_ui_html_source.h new file mode 100644 index 0000000..2f9f8b8 --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui_html_source.h @@ -0,0 +1,51 @@ +// 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 CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_HTML_SOURCE_H_ +#define CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_HTML_SOURCE_H_ +#pragma once + +#include <string> +#include <utility> + +#include "chrome/browser/webui/chrome_url_data_manager.h" + +namespace base { +class SharedMemory; +} + +class PrintPreviewUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + // A SharedMemory that contains the data for print preview, + // and the size of the print preview data in bytes. + typedef std::pair<base::SharedMemory*, uint32> PrintPreviewData; + + PrintPreviewUIHTMLSource(); + virtual ~PrintPreviewUIHTMLSource(); + + // Gets the print preview |data|. The data is valid as long as the + // PrintPreviewHandler is valid and SetPrintPreviewData() does not get called. + void GetPrintPreviewData(PrintPreviewData* data); + + // Sets the print preview |data|. PrintPreviewHandler owns the data and is + // responsible for freeing it when either: + // a) there is new data. + // b) when PrintPreviewHandler is destroyed. + void SetPrintPreviewData(const PrintPreviewData& data); + + // ChromeURLDataManager::DataSource implementation. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const; + + private: + // Current print preview data, the contents of which are owned by + // PrintPreviewHandler. + PrintPreviewData data_; + + DISALLOW_COPY_AND_ASSIGN(PrintPreviewUIHTMLSource); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_UI_HTML_SOURCE_H_ diff --git a/chrome/browser/ui/webui/print_preview_ui_html_source_unittest.cc b/chrome/browser/ui/webui/print_preview_ui_html_source_unittest.cc new file mode 100644 index 0000000..eb0d123 --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui_html_source_unittest.cc @@ -0,0 +1,60 @@ +// 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 "base/command_line.h" +#include "base/shared_memory.h" +#include "chrome/browser/printing/print_preview_tab_controller.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/webui/print_preview_ui.h" +#include "chrome/browser/ui/webui/print_preview_ui_html_source.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/browser_with_test_window_test.h" +#include "chrome/test/testing_profile.h" + +typedef BrowserWithTestWindowTest PrintPreviewUIHTMLSourceTest; + +// Create/Get a preview tab for initiator tab. +TEST_F(PrintPreviewUIHTMLSourceTest, PrintPreviewData) { + // TODO(thestig) Remove when print preview is enabled by default. + CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnablePrintPreview); + ASSERT_TRUE(browser()); + BrowserList::SetLastActive(browser()); + ASSERT_TRUE(BrowserList::GetLastActive()); + + browser()->NewTab(); + TabContents* initiator_tab = browser()->GetSelectedTabContents(); + ASSERT_TRUE(initiator_tab); + + scoped_refptr<printing::PrintPreviewTabController> + controller(new printing::PrintPreviewTabController()); + ASSERT_TRUE(controller); + + TabContents* preview_tab = controller->GetOrCreatePreviewTab( + initiator_tab, initiator_tab->controller().window_id().id()); + + EXPECT_NE(initiator_tab, preview_tab); + EXPECT_EQ(2, browser()->tab_count()); + + PrintPreviewUI* preview_ui = + reinterpret_cast<PrintPreviewUI*>(preview_tab->web_ui()); + ASSERT_TRUE(preview_ui != NULL); + PrintPreviewUIHTMLSource* html_source = preview_ui->html_source(); + + PrintPreviewUIHTMLSource::PrintPreviewData data; + html_source->GetPrintPreviewData(&data); + EXPECT_EQ(NULL, data.first); + EXPECT_EQ(0U, data.second); + + PrintPreviewUIHTMLSource::PrintPreviewData dummy_data = + std::make_pair(new base::SharedMemory(), 1234); + + html_source->SetPrintPreviewData(dummy_data); + html_source->GetPrintPreviewData(&data); + EXPECT_EQ(dummy_data, data); + + // This should not cause any memory leaks. + dummy_data.first = new base::SharedMemory(); + html_source->SetPrintPreviewData(dummy_data); +} diff --git a/chrome/browser/ui/webui/print_preview_ui_uitest.cc b/chrome/browser/ui/webui/print_preview_ui_uitest.cc new file mode 100644 index 0000000..d53cf33 --- /dev/null +++ b/chrome/browser/ui/webui/print_preview_ui_uitest.cc @@ -0,0 +1,75 @@ +// 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 "base/string16.h" +#include "base/utf_string_conversions.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/ui/ui_test.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +class PrintPreviewUITest : public UITest { + public: + PrintPreviewUITest() { + dom_automation_enabled_ = true; + // TODO(thestig): Remove when print preview is enabled by default. + launch_arguments_.AppendSwitch(switches::kEnablePrintPreview); + } + + void AssertIsPrintPage(TabProxy* tab) { + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + string16 expected_title = + l10n_util::GetStringUTF16(IDS_PRINT_PREVIEW_TITLE); + ASSERT_EQ(expected_title, WideToUTF16Hack(title)); + } +}; + +// TODO(thestig) Remove this test in the future if loading +// chrome::kChromeUIPrintURL directly does not make sense. +TEST_F(PrintPreviewUITest, LoadPrintPreviewByURL) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + ASSERT_TRUE(tab.get()); + + // Go to the print preview tab via URL. + NavigateToURL(GURL(chrome::kChromeUIPrintURL)); + AssertIsPrintPage(tab); +} + +TEST_F(PrintPreviewUITest, PrintCommandDisabled) { + scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + + // Go to the about:blank page. + NavigateToURL(GURL(chrome::kAboutBlankURL)); + + // Make sure there is 1 tab and print is enabled. Create print preview tab. + int tab_count; + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); + bool enabled; + ASSERT_TRUE(browser->IsMenuCommandEnabled(IDC_PRINT, &enabled)); + ASSERT_TRUE(enabled); + ASSERT_TRUE(browser->RunCommand(IDC_PRINT)); + + // Make sure there are 2 tabs and print is disabled. + ASSERT_TRUE(browser->GetTabCount(&tab_count)); + ASSERT_EQ(2, tab_count); + scoped_refptr<TabProxy> tab = browser->GetActiveTab(); + ASSERT_TRUE(tab.get()); + AssertIsPrintPage(tab); + ASSERT_TRUE(browser->IsMenuCommandEnabled(IDC_PRINT, &enabled)); + ASSERT_FALSE(enabled); +} + +} // namespace diff --git a/chrome/browser/ui/webui/remoting_ui.cc b/chrome/browser/ui/webui/remoting_ui.cc new file mode 100644 index 0000000..654894a --- /dev/null +++ b/chrome/browser/ui/webui/remoting_ui.cc @@ -0,0 +1,83 @@ +// 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/ui/webui/remoting_ui.h" + +#include "base/singleton.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/common/url_constants.h" +#include "grit/theme_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace { + +/////////////////////////////////////////////////////////////////////////////// +// +// RemotingHTMLSource +// +/////////////////////////////////////////////////////////////////////////////// + +class RemotingUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + RemotingUIHTMLSource() + : DataSource(chrome::kChromeUIRemotingHost, MessageLoop::current()) {} + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "pepper-application/x-chromoting"; + } + + private: + ~RemotingUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(RemotingUIHTMLSource); +}; + +void RemotingUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + // Dummy data. Not used, but we need to send something back in the response. + std::string full_html = "remoting"; + + 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); +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// +// RemotingUI +// +/////////////////////////////////////////////////////////////////////////////// + +RemotingUI::RemotingUI(TabContents* contents) : WebUI(contents) { + RemotingUIHTMLSource* html_source = new RemotingUIHTMLSource(); + + // Set up the chrome://remoting source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} + + +// static +RefCountedMemory* RemotingUI::GetFaviconResourceBytes() { + return ResourceBundle::GetSharedInstance(). + // TODO(garykac): Have custom remoting icon created. + LoadDataResourceBytes(IDR_PLUGIN); +} + +// static +void RemotingUI::RegisterUserPrefs(PrefService* prefs) { + // TODO(garykac): Add remoting prefs (if needed). +} diff --git a/chrome/browser/ui/webui/remoting_ui.h b/chrome/browser/ui/webui/remoting_ui.h new file mode 100644 index 0000000..d5c43d9 --- /dev/null +++ b/chrome/browser/ui/webui/remoting_ui.h @@ -0,0 +1,25 @@ +// 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 CHROME_BROWSER_UI_WEBUI_REMOTING_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_REMOTING_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class PrefService; +class RefCountedMemory; + +class RemotingUI : public WebUI { + public: + explicit RemotingUI(TabContents* contents); + + static RefCountedMemory* GetFaviconResourceBytes(); + static void RegisterUserPrefs(PrefService* prefs); + + private: + DISALLOW_COPY_AND_ASSIGN(RemotingUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_REMOTING_UI_H_ diff --git a/chrome/browser/ui/webui/slideshow_ui.cc b/chrome/browser/ui/webui/slideshow_ui.cc new file mode 100644 index 0000000..b8080ea --- /dev/null +++ b/chrome/browser/ui/webui/slideshow_ui.cc @@ -0,0 +1,284 @@ +// 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/ui/webui/slideshow_ui.h" + +#include "base/callback.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "base/weak_ptr.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/history/history_types.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/webui/favicon_source.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "net/base/directory_lister.h" +#include "net/base/escape.h" +#include "ui/base/resource/resource_bundle.h" + +static const char kPropertyPath[] = "path"; +static const char kPropertyTitle[] = "title"; +static const char kPropertyOffset[] = "currentOffset"; +static const char kPropertyDirectory[] = "isDirectory"; + +class SlideshowUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + SlideshowUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string&) const { + return "text/html"; + } + + private: + ~SlideshowUIHTMLSource() {} + + DISALLOW_COPY_AND_ASSIGN(SlideshowUIHTMLSource); +}; + +// The handler for Javascript messages related to the "slideshow" view. +class SlideshowHandler : public net::DirectoryLister::DirectoryListerDelegate, + public WebUIMessageHandler, + public base::SupportsWeakPtr<SlideshowHandler> { + public: + SlideshowHandler(); + virtual ~SlideshowHandler(); + + // Init work after Attach. + void Init(); + + // DirectoryLister::DirectoryListerDelegate methods: + virtual void OnListFile( + const net::DirectoryLister::DirectoryListerData& data); + virtual void OnListDone(int error); + + // WebUIMessageHandler implementation. + virtual WebUIMessageHandler* Attach(WebUI* web_ui); + virtual void RegisterMessages(); + + void GetChildrenForPath(const FilePath& path, bool is_refresh); + + // Callback for the "getChildren" message. + void HandleGetChildren(const ListValue* args); + + void HandleRefreshDirectory(const ListValue* args); + + private: + bool PathIsImageFile(const char* filename); + + scoped_ptr<ListValue> filelist_value_; + FilePath currentpath_; + FilePath originalpath_; + Profile* profile_; + int counter_; + int currentOffset_; + scoped_refptr<net::DirectoryLister> lister_; + bool is_refresh_; + + DISALLOW_COPY_AND_ASSIGN(SlideshowHandler); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// SlideshowHTMLSource +// +//////////////////////////////////////////////////////////////////////////////// + +SlideshowUIHTMLSource::SlideshowUIHTMLSource() + : DataSource(chrome::kChromeUISlideshowHost, MessageLoop::current()) { +} + +void SlideshowUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + DictionaryValue localized_strings; + // TODO(dhg): Add stirings to localized strings, also add more strings + // that are currently hardcoded. + SetFontAndTextDirection(&localized_strings); + + static const base::StringPiece slideshow_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_SLIDESHOW_HTML)); + const std::string full_html = jstemplate_builder::GetI18nTemplateHtml( + slideshow_html, &localized_strings); + + 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); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// SlideshowHandler +// +//////////////////////////////////////////////////////////////////////////////// +SlideshowHandler::SlideshowHandler() + : profile_(NULL), + is_refresh_(false) { + lister_ = NULL; +} + +SlideshowHandler::~SlideshowHandler() { + if (lister_.get()) { + lister_->Cancel(); + lister_->set_delegate(NULL); + } +} + +WebUIMessageHandler* SlideshowHandler::Attach(WebUI* web_ui) { + profile_ = web_ui->GetProfile(); + // Create our favicon data source. + profile_->GetChromeURLDataManager()->AddDataSource( + new FavIconSource(profile_)); + return WebUIMessageHandler::Attach(web_ui); +} + +void SlideshowHandler::Init() { +} + +void SlideshowHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("getChildren", + NewCallback(this, &SlideshowHandler::HandleGetChildren)); + web_ui_->RegisterMessageCallback("refreshDirectory", + NewCallback(this, &SlideshowHandler::HandleRefreshDirectory)); +} + +void SlideshowHandler::HandleRefreshDirectory(const ListValue* args) { +#if defined(OS_CHROMEOS) + std::string path = WideToUTF8(ExtractStringValue(args)); + GetChildrenForPath(FilePath(path), true); +#endif +} + +void SlideshowHandler::GetChildrenForPath(const FilePath& path, + bool is_refresh) { + filelist_value_.reset(new ListValue()); + currentpath_ = path; + + if (lister_.get()) { + lister_->Cancel(); + lister_->set_delegate(NULL); + lister_ = NULL; + } + + is_refresh_ = is_refresh; + if (file_util::EnsureEndsWithSeparator(¤tpath_) && + currentpath_.IsAbsolute()) { + lister_ = new net::DirectoryLister(currentpath_, this); + } else { + originalpath_ = currentpath_; + currentpath_ = currentpath_.DirName(); + lister_ = new net::DirectoryLister(currentpath_, this); + } + counter_ = 0; + currentOffset_ = -1; + lister_->Start(); +} + +void SlideshowHandler::HandleGetChildren(const ListValue* args) { +#if defined(OS_CHROMEOS) + filelist_value_.reset(new ListValue()); + std::string path = WideToUTF8(ExtractStringValue(args)); + GetChildrenForPath(FilePath(path), false); +#endif +} + +bool SlideshowHandler::PathIsImageFile(const char* filename) { +#if defined(OS_CHROMEOS) + FilePath file = FilePath(filename); + std::string ext = file.Extension(); + ext = StringToLowerASCII(ext); + if (ext == ".jpg" || + ext == ".jpeg" || + ext == ".png" || + ext == ".gif") { + return true; + } else { + return false; + } +#else + return false; +#endif +} + +void SlideshowHandler::OnListFile( + const net::DirectoryLister::DirectoryListerData& data) { +#if defined(OS_CHROMEOS) + if (data.info.filename[0] == '.') { + return; + } + if (!PathIsImageFile(data.info.filename.c_str())) { + return; + } + + DictionaryValue* file_value = new DictionaryValue(); + + file_value->SetString(kPropertyTitle, data.info.filename); + file_value->SetString(kPropertyPath, + currentpath_.Append(data.info.filename).value()); + file_value->SetBoolean(kPropertyDirectory, S_ISDIR(data.info.stat.st_mode)); + filelist_value_->Append(file_value); + std::string val; + file_value->GetString(kPropertyTitle, &val); + if (val == originalpath_.BaseName().value()) { + currentOffset_ = counter_; + } + counter_++; +#endif +} + +void SlideshowHandler::OnListDone(int error) { + DictionaryValue info_value; + counter_ = 0; + if (!(file_util::EnsureEndsWithSeparator(&originalpath_) && + originalpath_.IsAbsolute()) && + currentOffset_ != -1) { + info_value.SetInteger(kPropertyOffset, currentOffset_); + } + if (is_refresh_) { + info_value.SetString("functionCall", "refresh"); + } else { + info_value.SetString("functionCall", "getChildren"); + } + info_value.SetString(kPropertyPath, currentpath_.value()); + web_ui_->CallJavascriptFunction(L"browseFileResult", + info_value, *(filelist_value_.get())); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// SlideshowUI +// +//////////////////////////////////////////////////////////////////////////////// + +SlideshowUI::SlideshowUI(TabContents* contents) : WebUI(contents) { + SlideshowHandler* handler = new SlideshowHandler(); + AddMessageHandler((handler)->Attach(this)); + handler->Init(); + SlideshowUIHTMLSource* html_source = new SlideshowUIHTMLSource(); + + // Set up the chrome://slideshow/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} diff --git a/chrome/browser/ui/webui/slideshow_ui.h b/chrome/browser/ui/webui/slideshow_ui.h new file mode 100644 index 0000000..fd7413a --- /dev/null +++ b/chrome/browser/ui/webui/slideshow_ui.h @@ -0,0 +1,19 @@ +// 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 CHROME_BROWSER_UI_WEBUI_SLIDESHOW_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_SLIDESHOW_UI_H_ +#pragma once + +#include "content/browser/webui/web_ui.h" + +class SlideshowUI : public WebUI { + public: + explicit SlideshowUI(TabContents* contents); + + private: + DISALLOW_COPY_AND_ASSIGN(SlideshowUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_SLIDESHOW_UI_H_ diff --git a/chrome/browser/ui/webui/sync_internals_ui.cc b/chrome/browser/ui/webui/sync_internals_ui.cc new file mode 100644 index 0000000..5edfbcc --- /dev/null +++ b/chrome/browser/ui/webui/sync_internals_ui.cc @@ -0,0 +1,83 @@ +// 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/ui/webui/sync_internals_ui.h" + +#include <string> + +#include "base/logging.h" +#include "base/ref_counted.h" +#include "base/task.h" +#include "base/tracked_objects.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sync/js_arg_list.h" +#include "chrome/browser/sync/js_frontend.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/sync_ui_util.h" +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "chrome/browser/webui/sync_internals_html_source.h" +#include "chrome/common/render_messages_params.h" + +SyncInternalsUI::SyncInternalsUI(TabContents* contents) + : WebUI(contents) { + browser_sync::JsFrontend* backend = GetJsFrontend(); + if (backend) { + backend->AddHandler(this); + } + // If this PostTask() call fails, it's most likely because this is + // being run from a unit test. The created objects will be cleaned + // up, anyway. + contents->profile()->GetChromeURLDataManager()->AddDataSource( + new SyncInternalsHTMLSource()); +} + +SyncInternalsUI::~SyncInternalsUI() { + browser_sync::JsFrontend* backend = GetJsFrontend(); + if (backend) { + backend->RemoveHandler(this); + } +} + +void SyncInternalsUI::ProcessWebUIMessage( + const ViewHostMsg_DomMessage_Params& params) { + const std::string& name = params.name; + browser_sync::JsArgList args(params.arguments); + VLOG(1) << "Received message: " << name << " with args " + << args.ToString(); + // We handle this case directly because it needs to work even if + // the sync service doesn't exist. + if (name == "getAboutInfo") { + ListValue args; + DictionaryValue* about_info = new DictionaryValue(); + args.Append(about_info); + ProfileSyncService* service = GetProfile()->GetProfileSyncService(); + sync_ui_util::ConstructAboutInformation(service, about_info); + HandleJsEvent("onGetAboutInfoFinished", + browser_sync::JsArgList(args)); + } else { + browser_sync::JsFrontend* backend = GetJsFrontend(); + if (backend) { + backend->ProcessMessage(name, args, this); + } else { + LOG(WARNING) << "No sync service; dropping message " << name + << " with args " << args.ToString(); + } + } +} + +void SyncInternalsUI::HandleJsEvent(const std::string& name, + const browser_sync::JsArgList& args) { + VLOG(1) << "Handling event: " << name << " with args " << args.ToString(); + std::vector<const Value*> arg_list(args.Get().begin(), args.Get().end()); + CallJavascriptFunction(UTF8ToWide(name), arg_list); +} + +browser_sync::JsFrontend* SyncInternalsUI::GetJsFrontend() { + // If this returns NULL that means that sync is disabled for + // whatever reason. + ProfileSyncService* sync_service = GetProfile()->GetProfileSyncService(); + return sync_service ? sync_service->GetJsFrontend() : NULL; +} diff --git a/chrome/browser/ui/webui/sync_internals_ui.h b/chrome/browser/ui/webui/sync_internals_ui.h new file mode 100644 index 0000000..0afdca4 --- /dev/null +++ b/chrome/browser/ui/webui/sync_internals_ui.h @@ -0,0 +1,53 @@ +// 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 CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "chrome/browser/sync/js_event_handler.h" +#include "content/browser/webui/web_ui.h" + +namespace browser_sync { +class JsFrontend; +} // namespace browser_sync + +// The implementation for the chrome://sync-internals page. +class SyncInternalsUI : public WebUI, public browser_sync::JsEventHandler { + public: + explicit SyncInternalsUI(TabContents* contents); + virtual ~SyncInternalsUI(); + + // WebUI implementation. + // + // The following messages are processed: + // + // getAboutInfo(): + // Immediately fires a onGetAboutInfoFinished() event with a + // dictionary of sync-related stats and info. + // + // All other messages are routed to the sync service if it exists, + // and dropped otherwise. + // + // TODO(akalin): Add a simple isSyncEnabled() message and make + // getAboutInfo() be handled by the sync service. + virtual void ProcessWebUIMessage( + const ViewHostMsg_DomMessage_Params& params); + + // browser_sync::JsEventHandler implementation. + virtual void HandleJsEvent(const std::string& name, + const browser_sync::JsArgList& args); + + private: + // Returns the sync service's JsFrontend object, or NULL if the sync + // service does not exist. + browser_sync::JsFrontend* GetJsFrontend(); + + DISALLOW_COPY_AND_ASSIGN(SyncInternalsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_UI_H_ diff --git a/chrome/browser/ui/webui/sync_internals_ui_unittest.cc b/chrome/browser/ui/webui/sync_internals_ui_unittest.cc new file mode 100644 index 0000000..379a704 --- /dev/null +++ b/chrome/browser/ui/webui/sync_internals_ui_unittest.cc @@ -0,0 +1,226 @@ +// 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/ui/webui/sync_internals_ui.h" + +#include <cstddef> +#include <string> + +#include "base/message_loop.h" +#include "base/values.h" +#include "chrome/browser/sync/js_arg_list.h" +#include "chrome/browser/sync/js_test_util.h" +#include "chrome/browser/sync/profile_sync_service_mock.h" +#include "chrome/browser/tab_contents/test_tab_contents.h" +#include "chrome/common/render_messages_params.h" +#include "chrome/test/profile_mock.h" +#include "content/browser/browser_thread.h" +#include "content/browser/renderer_host/test_render_view_host.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +using browser_sync::HasArgsAsList; +using browser_sync::JsArgList; +using testing::NiceMock; +using testing::Return; +using testing::StrictMock; + +// Subclass of SyncInternalsUI to mock out ExecuteJavascript. +class TestSyncInternalsUI : public SyncInternalsUI { + public: + explicit TestSyncInternalsUI(TabContents* contents) + : SyncInternalsUI(contents) {} + virtual ~TestSyncInternalsUI() {} + + MOCK_METHOD1(ExecuteJavascript, void(const std::wstring&)); +}; + +class SyncInternalsUITest : public RenderViewHostTestHarness { + protected: + // We allocate memory for |sync_internals_ui_| but we don't + // construct it. This is because we want to set mock expectations + // with its address before we construct it, and its constructor + // calls into our mocks. + SyncInternalsUITest() + // The message loop is provided by RenderViewHostTestHarness. + : ui_thread_(BrowserThread::UI, MessageLoopForUI::current()), + test_sync_internals_ui_buf_(NULL), + test_sync_internals_ui_constructor_called_(false) {} + + virtual void SetUp() { + test_sync_internals_ui_buf_ = operator new(sizeof(TestSyncInternalsUI)); + test_sync_internals_ui_constructor_called_ = false; + profile_.reset(new NiceMock<ProfileMock>()); + RenderViewHostTestHarness::SetUp(); + } + + virtual void TearDown() { + if (test_sync_internals_ui_constructor_called_) { + GetTestSyncInternalsUI()->~TestSyncInternalsUI(); + } + operator delete(test_sync_internals_ui_buf_); + RenderViewHostTestHarness::TearDown(); + } + + NiceMock<ProfileMock>* GetProfileMock() { + return static_cast<NiceMock<ProfileMock>*>(profile()); + } + + // Set up boilerplate expectations for calls done during + // SyncInternalUI's construction/destruction. + void ExpectSetupTeardownCalls() { + EXPECT_CALL(*GetProfileMock(), GetProfileSyncService()) + .WillRepeatedly(Return(&profile_sync_service_mock_)); + + EXPECT_CALL(profile_sync_service_mock_, GetJsFrontend()) + .WillRepeatedly(Return(&mock_js_backend_)); + + // Called by sync_ui_util::ConstructAboutInformation(). + EXPECT_CALL(profile_sync_service_mock_, HasSyncSetupCompleted()) + .WillRepeatedly(Return(false)); + + // Called by SyncInternalsUI's constructor. + EXPECT_CALL(mock_js_backend_, + AddHandler(GetTestSyncInternalsUIAddress())); + + // Called by SyncInternalUI's destructor. + EXPECT_CALL(mock_js_backend_, + RemoveHandler(GetTestSyncInternalsUIAddress())); + } + + // Like ExpectSetupTeardownCalls() but with a NULL + // ProfileSyncService. + void ExpectSetupTeardownCallsNullService() { + EXPECT_CALL(*GetProfileMock(), GetProfileSyncService()) + .WillRepeatedly(Return(static_cast<ProfileSyncService*>(NULL))); + } + + void ConstructTestSyncInternalsUI() { + if (test_sync_internals_ui_constructor_called_) { + ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called " + << "at most once per test"; + return; + } + new(test_sync_internals_ui_buf_) TestSyncInternalsUI(contents()); + test_sync_internals_ui_constructor_called_ = true; + } + + TestSyncInternalsUI* GetTestSyncInternalsUI() { + if (!test_sync_internals_ui_constructor_called_) { + ADD_FAILURE() << "ConstructTestSyncInternalsUI() should be called " + << "before GetTestSyncInternalsUI()"; + return NULL; + } + return GetTestSyncInternalsUIAddress(); + } + + // Used for passing into EXPECT_CALL(). + TestSyncInternalsUI* GetTestSyncInternalsUIAddress() { + EXPECT_TRUE(test_sync_internals_ui_buf_); + return static_cast<TestSyncInternalsUI*>(test_sync_internals_ui_buf_); + } + + StrictMock<ProfileSyncServiceMock> profile_sync_service_mock_; + StrictMock<browser_sync::MockJsFrontend> mock_js_backend_; + + private: + // Needed by |contents()|. + BrowserThread ui_thread_; + void* test_sync_internals_ui_buf_; + bool test_sync_internals_ui_constructor_called_; +}; + +TEST_F(SyncInternalsUITest, HandleJsEvent) { + ExpectSetupTeardownCalls(); + + ConstructTestSyncInternalsUI(); + + EXPECT_CALL(*GetTestSyncInternalsUI(), + ExecuteJavascript(std::wstring(L"testMessage(5,true);"))); + + ListValue args; + args.Append(Value::CreateIntegerValue(5)); + args.Append(Value::CreateBooleanValue(true)); + GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args)); +} + +TEST_F(SyncInternalsUITest, HandleJsEventNullService) { + ExpectSetupTeardownCallsNullService(); + + ConstructTestSyncInternalsUI(); + + EXPECT_CALL(*GetTestSyncInternalsUI(), + ExecuteJavascript(std::wstring(L"testMessage(5,true);"))); + + ListValue args; + args.Append(Value::CreateIntegerValue(5)); + args.Append(Value::CreateBooleanValue(true)); + GetTestSyncInternalsUI()->HandleJsEvent("testMessage", JsArgList(args)); +} + +TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasic) { + ExpectSetupTeardownCalls(); + + ViewHostMsg_DomMessage_Params params; + params.name = "testName"; + params.arguments.Append(Value::CreateIntegerValue(10)); + + EXPECT_CALL(mock_js_backend_, + ProcessMessage(params.name, HasArgsAsList(params.arguments), + GetTestSyncInternalsUIAddress())); + + ConstructTestSyncInternalsUI(); + + GetTestSyncInternalsUI()->ProcessWebUIMessage(params); +} + +TEST_F(SyncInternalsUITest, ProcessWebUIMessageBasicNullService) { + ExpectSetupTeardownCallsNullService(); + + ConstructTestSyncInternalsUI(); + + ViewHostMsg_DomMessage_Params params; + params.name = "testName"; + params.arguments.Append(Value::CreateIntegerValue(5)); + + // Should drop the message. + GetTestSyncInternalsUI()->ProcessWebUIMessage(params); +} + +namespace { +const wchar_t kAboutInfoCall[] = + L"onGetAboutInfoFinished({\"summary\":\"SYNC DISABLED\"});"; +} // namespace + +TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfo) { + ExpectSetupTeardownCalls(); + + ViewHostMsg_DomMessage_Params params; + params.name = "getAboutInfo"; + + ConstructTestSyncInternalsUI(); + + EXPECT_CALL(*GetTestSyncInternalsUI(), + ExecuteJavascript(std::wstring(kAboutInfoCall))); + + GetTestSyncInternalsUI()->ProcessWebUIMessage(params); +} + +TEST_F(SyncInternalsUITest, ProcessWebUIMessageGetAboutInfoNullService) { + ExpectSetupTeardownCallsNullService(); + + ViewHostMsg_DomMessage_Params params; + params.name = "getAboutInfo"; + + ConstructTestSyncInternalsUI(); + + EXPECT_CALL(*GetTestSyncInternalsUI(), + ExecuteJavascript(std::wstring(kAboutInfoCall))); + + GetTestSyncInternalsUI()->ProcessWebUIMessage(params); +} + +} // namespace diff --git a/chrome/browser/ui/webui/textfields_ui.cc b/chrome/browser/ui/webui/textfields_ui.cc new file mode 100644 index 0000000..73217e0 --- /dev/null +++ b/chrome/browser/ui/webui/textfields_ui.cc @@ -0,0 +1,74 @@ +// 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/ui/webui/textfields_ui.h" + +#include <algorithm> +#include <string> + +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/values.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/url_constants.h" +#include "grit/browser_resources.h" +#include "ui/base/resource/resource_bundle.h" + +/** + * TextfieldsUIHTMLSource implementation. + */ +TextfieldsUIHTMLSource::TextfieldsUIHTMLSource() + : DataSource(chrome::kChromeUITextfieldsHost, MessageLoop::current()) { +} + +void TextfieldsUIHTMLSource::StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id) { + const std::string full_html = ResourceBundle::GetSharedInstance() + .GetRawDataResource(IDR_TEXTFIELDS_HTML).as_string(); + + 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); +} + +std::string TextfieldsUIHTMLSource::GetMimeType( + const std::string& /* path */) const { + return "text/html"; +} + +TextfieldsUIHTMLSource::~TextfieldsUIHTMLSource() {} + +/** + * TextfieldsDOMHandler implementation. + */ +TextfieldsDOMHandler::TextfieldsDOMHandler() : WebUIMessageHandler() {} + +void TextfieldsDOMHandler::RegisterMessages() { + web_ui_->RegisterMessageCallback("textfieldValue", + NewCallback(this, &TextfieldsDOMHandler::HandleTextfieldValue)); +} + +void TextfieldsDOMHandler::HandleTextfieldValue(const ListValue* args) { + static_cast<TextfieldsUI*>(web_ui_)->set_text(ExtractStringValue(args)); +} + +/** + * TextfieldsUI implementation. + */ +TextfieldsUI::TextfieldsUI(TabContents* contents) : WebUI(contents) { + TextfieldsDOMHandler* handler = new TextfieldsDOMHandler(); + AddMessageHandler(handler); + handler->Attach(this); + + TextfieldsUIHTMLSource* html_source = new TextfieldsUIHTMLSource(); + + // Set up the chrome://textfields/ source. + contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); +} diff --git a/chrome/browser/ui/webui/textfields_ui.h b/chrome/browser/ui/webui/textfields_ui.h new file mode 100644 index 0000000..1b62982 --- /dev/null +++ b/chrome/browser/ui/webui/textfields_ui.h @@ -0,0 +1,71 @@ +// 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 CHROME_BROWSER_UI_WEBUI_TEXTFIELDS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_TEXTFIELDS_UI_H_ +#pragma once + +#include <string> + +#include "chrome/browser/webui/chrome_url_data_manager.h" +#include "content/browser/webui/web_ui.h" + +class RefCountedMemory; + +/** + * ChromeURLDataManager::DataSource implementation that asynchronously answers + * requests for chrome://textfields URL. On receiving a request, this object + * reads the html from the local resource textfields.html and sends back the + * response. + */ +class TextfieldsUIHTMLSource : public ChromeURLDataManager::DataSource { + public: + TextfieldsUIHTMLSource(); + + // Called when the network layer has requested a resource underneath + // the path we registered. + virtual void StartDataRequest(const std::string& path, + bool is_off_the_record, + int request_id); + virtual std::string GetMimeType(const std::string& path) const; + + private: + virtual ~TextfieldsUIHTMLSource(); + + DISALLOW_COPY_AND_ASSIGN(TextfieldsUIHTMLSource); +}; + +/** + * Handler for JavaScript calls from the DOM. + */ +class TextfieldsDOMHandler : public WebUIMessageHandler { + public: + TextfieldsDOMHandler(); + + // Handles the "textfieldValue" call from the JavaScript. This call + // synchonizes the value inside the JavaScript textfield with the copy in the + // DOM object. + virtual void HandleTextfieldValue(const ListValue* args); + + protected: + virtual void RegisterMessages(); + + private: + DISALLOW_COPY_AND_ASSIGN(TextfieldsDOMHandler); +}; + +class TextfieldsUI : public WebUI { + public: + explicit TextfieldsUI(TabContents* contents); + + const std::wstring& text() const { return text_; } + void set_text(const std::wstring& text) { text_ = text; } + + private: + std::wstring text_; + + DISALLOW_COPY_AND_ASSIGN(TextfieldsUI); +}; + +#endif // CHROME_BROWSER_UI_WEBUI_TEXTFIELDS_UI_H_ |