// 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/path_service.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/debugger/devtools_client_host.h" #include "chrome/browser/debugger/devtools_manager.h" #include "chrome/browser/debugger/devtools_window.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/common/notification_registrar.h" #include "content/common/notification_service.h" #include "net/test/test_server.h" namespace { // Used to block until a dev tools client window's browser is closed. class BrowserClosedObserver : public NotificationObserver { public: explicit BrowserClosedObserver(Browser* browser) { registrar_.Add(this, NotificationType::BROWSER_CLOSED, Source<Browser>(browser)); ui_test_utils::RunMessageLoop(); } virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { MessageLoopForUI::current()->Quit(); } private: NotificationRegistrar registrar_; DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver); }; // The delay waited in some cases where we don't have a notifications for an // action we take. const int kActionDelayMs = 500; const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html"; const char kHeapProfilerPage[] = "files/devtools/heap_profiler.html"; const char kPauseWhenLoadingDevTools[] = "files/devtools/pause_when_loading_devtools.html"; const char kPauseWhenScriptIsRunning[] = "files/devtools/pause_when_script_is_running.html"; const char kPageWithContentScript[] = "files/devtools/page_with_content_script.html"; class DevToolsSanityTest : public InProcessBrowserTest { public: DevToolsSanityTest() { set_show_window(true); EnableDOMAutomation(); } protected: void RunTest(const std::string& test_name, const std::string& test_page) { OpenDevToolsWindow(test_page); std::string result; // At first check that JavaScript part of the front-end is loaded by // checking that global variable uiTests exists(it's created after all js // files have been loaded) and has runTest method. ASSERT_TRUE( ui_test_utils::ExecuteJavaScriptAndExtractString( client_contents_->render_view_host(), L"", L"window.domAutomationController.send(" L"'' + (window.uiTests && (typeof uiTests.runTest)));", &result)); if (result == "function") { ASSERT_TRUE( ui_test_utils::ExecuteJavaScriptAndExtractString( client_contents_->render_view_host(), L"", UTF8ToWide(StringPrintf("uiTests.runTest('%s')", test_name.c_str())), &result)); EXPECT_EQ("[OK]", result); } else { FAIL() << "DevTools front-end is broken."; } CloseDevToolsWindow(); } void OpenDevToolsWindow(const std::string& test_page) { ASSERT_TRUE(test_server()->Start()); GURL url = test_server()->GetURL(test_page); ui_test_utils::NavigateToURL(browser(), url); inspected_rvh_ = GetInspectedTab()->render_view_host(); DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); devtools_manager->OpenDevToolsWindow(inspected_rvh_); DevToolsClientHost* client_host = devtools_manager->GetDevToolsClientHostFor(inspected_rvh_); window_ = client_host->AsDevToolsWindow(); RenderViewHost* client_rvh = window_->GetRenderViewHost(); client_contents_ = client_rvh->delegate()->GetAsTabContents(); ui_test_utils::WaitForNavigation(&client_contents_->controller()); } TabContents* GetInspectedTab() { return browser()->GetTabContentsAt(0); } void CloseDevToolsWindow() { DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); // UnregisterDevToolsClientHostFor may destroy window_ so store the browser // first. Browser* browser = window_->browser(); devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_); // Wait only when DevToolsWindow has a browser. For docked DevTools, this // is NULL and we skip the wait. if (browser) BrowserClosedObserver close_observer(browser); } TabContents* client_contents_; DevToolsWindow* window_; RenderViewHost* inspected_rvh_; }; class CancelableQuitTask : public Task { public: explicit CancelableQuitTask(const std::string& timeout_message) : timeout_message_(timeout_message), cancelled_(false) { } void cancel() { cancelled_ = true; } virtual void Run() { if (cancelled_) { return; } FAIL() << timeout_message_; MessageLoop::current()->Quit(); } private: std::string timeout_message_; bool cancelled_; }; // Base class for DevTools tests that test devtools functionality for // extensions and content scripts. class DevToolsExtensionDebugTest : public DevToolsSanityTest, public NotificationObserver { public: DevToolsExtensionDebugTest() : DevToolsSanityTest() { PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_); test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools"); test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions"); } protected: // Load an extention from test\data\devtools\extensions\<extension_name> void LoadExtension(const char* extension_name) { FilePath path = test_extensions_dir_.AppendASCII(extension_name); ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension."; } private: bool LoadExtensionFromPath(const FilePath& path) { ExtensionService* service = browser()->profile()->GetExtensionService(); size_t num_before = service->extensions()->size(); { NotificationRegistrar registrar; registrar.Add(this, NotificationType::EXTENSION_LOADED, NotificationService::AllSources()); CancelableQuitTask* delayed_quit = new CancelableQuitTask("Extension load timed out."); MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit, 4*1000); service->LoadExtension(path); ui_test_utils::RunMessageLoop(); delayed_quit->cancel(); } size_t num_after = service->extensions()->size(); if (num_after != (num_before + 1)) return false; return WaitForExtensionHostsToLoad(); } bool WaitForExtensionHostsToLoad() { // Wait for all the extension hosts that exist to finish loading. // NOTE: This assumes that the extension host list is not changing while // this method is running. NotificationRegistrar registrar; registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, NotificationService::AllSources()); CancelableQuitTask* delayed_quit = new CancelableQuitTask("Extension host load timed out."); MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit, 4*1000); ExtensionProcessManager* manager = browser()->profile()->GetExtensionProcessManager(); for (ExtensionProcessManager::const_iterator iter = manager->begin(); iter != manager->end();) { if ((*iter)->did_stop_loading()) ++iter; else ui_test_utils::RunMessageLoop(); } delayed_quit->cancel(); return true; } void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSION_LOADED: case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: MessageLoopForUI::current()->Quit(); break; default: NOTREACHED(); break; } } FilePath test_extensions_dir_; }; // Tests heap profiler. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, FAILS_TestHeapProfiler) { RunTest("testHeapProfiler", kHeapProfilerPage); } // Tests scripts panel showing. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) { RunTest("testShowScriptsTab", kDebuggerTestPage); } // Tests that scripts tab is populated with inspected scripts even if it // hadn't been shown by the moment inspected paged refreshed. // @see http://crbug.com/26312 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestScriptsTabIsPopulatedOnInspectedPageRefresh) { // Clear inspector settings to ensure that Elements will be // current panel when DevTools window is open. GetInspectedTab()->render_view_host()->delegate()->ClearInspectorSettings(); RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh", kDebuggerTestPage); } // Tests that a content script is in the scripts list. // This test is disabled, see bug 28961. IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest, TestContentScriptIsPresent) { LoadExtension("simple_content_script"); RunTest("testContentScriptIsPresent", kPageWithContentScript); } // Tests that scripts are not duplicated after Scripts Panel switch. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNoScriptDuplicatesOnPanelSwitch) { RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage); } // Tests that debugger works correctly if pause event occurs when DevTools // frontend is being loaded. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenLoadingDevTools) { RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools); } // Tests that pressing 'Pause' will pause script execution if the script // is already running. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) { RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning); } } // namespace