1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/command_line.h" 6#include "base/path_service.h" 7#include "base/string_util.h" 8#include "base/utf_string_conversions.h" 9#include "chrome/browser/debugger/devtools_client_host.h" 10#include "chrome/browser/debugger/devtools_manager.h" 11#include "chrome/browser/debugger/devtools_window.h" 12#include "chrome/browser/extensions/extension_host.h" 13#include "chrome/browser/extensions/extension_service.h" 14#include "chrome/browser/profiles/profile.h" 15#include "chrome/browser/ui/browser.h" 16#include "chrome/common/chrome_paths.h" 17#include "chrome/test/in_process_browser_test.h" 18#include "chrome/test/ui_test_utils.h" 19#include "content/browser/renderer_host/render_view_host.h" 20#include "content/browser/tab_contents/tab_contents.h" 21#include "content/common/notification_registrar.h" 22#include "content/common/notification_service.h" 23#include "net/test/test_server.h" 24 25namespace { 26 27// Used to block until a dev tools client window's browser is closed. 28class BrowserClosedObserver : public NotificationObserver { 29 public: 30 explicit BrowserClosedObserver(Browser* browser) { 31 registrar_.Add(this, NotificationType::BROWSER_CLOSED, 32 Source<Browser>(browser)); 33 ui_test_utils::RunMessageLoop(); 34 } 35 36 virtual void Observe(NotificationType type, 37 const NotificationSource& source, 38 const NotificationDetails& details) { 39 MessageLoopForUI::current()->Quit(); 40 } 41 42 private: 43 NotificationRegistrar registrar_; 44 DISALLOW_COPY_AND_ASSIGN(BrowserClosedObserver); 45}; 46 47// The delay waited in some cases where we don't have a notifications for an 48// action we take. 49const int kActionDelayMs = 500; 50 51const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html"; 52const char kHeapProfilerPage[] = "files/devtools/heap_profiler.html"; 53const char kPauseWhenLoadingDevTools[] = 54 "files/devtools/pause_when_loading_devtools.html"; 55const char kPauseWhenScriptIsRunning[] = 56 "files/devtools/pause_when_script_is_running.html"; 57const char kPageWithContentScript[] = 58 "files/devtools/page_with_content_script.html"; 59 60 61class DevToolsSanityTest : public InProcessBrowserTest { 62 public: 63 DevToolsSanityTest() { 64 set_show_window(true); 65 EnableDOMAutomation(); 66 } 67 68 protected: 69 void RunTest(const std::string& test_name, const std::string& test_page) { 70 OpenDevToolsWindow(test_page); 71 std::string result; 72 73 // At first check that JavaScript part of the front-end is loaded by 74 // checking that global variable uiTests exists(it's created after all js 75 // files have been loaded) and has runTest method. 76 ASSERT_TRUE( 77 ui_test_utils::ExecuteJavaScriptAndExtractString( 78 client_contents_->render_view_host(), 79 L"", 80 L"window.domAutomationController.send(" 81 L"'' + (window.uiTests && (typeof uiTests.runTest)));", 82 &result)); 83 84 if (result == "function") { 85 ASSERT_TRUE( 86 ui_test_utils::ExecuteJavaScriptAndExtractString( 87 client_contents_->render_view_host(), 88 L"", 89 UTF8ToWide(StringPrintf("uiTests.runTest('%s')", 90 test_name.c_str())), 91 &result)); 92 EXPECT_EQ("[OK]", result); 93 } else { 94 FAIL() << "DevTools front-end is broken."; 95 } 96 CloseDevToolsWindow(); 97 } 98 99 void OpenDevToolsWindow(const std::string& test_page) { 100 ASSERT_TRUE(test_server()->Start()); 101 GURL url = test_server()->GetURL(test_page); 102 ui_test_utils::NavigateToURL(browser(), url); 103 104 inspected_rvh_ = GetInspectedTab()->render_view_host(); 105 DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); 106 devtools_manager->OpenDevToolsWindow(inspected_rvh_); 107 108 DevToolsClientHost* client_host = 109 devtools_manager->GetDevToolsClientHostFor(inspected_rvh_); 110 window_ = client_host->AsDevToolsWindow(); 111 RenderViewHost* client_rvh = window_->GetRenderViewHost(); 112 client_contents_ = client_rvh->delegate()->GetAsTabContents(); 113 ui_test_utils::WaitForNavigation(&client_contents_->controller()); 114 } 115 116 TabContents* GetInspectedTab() { 117 return browser()->GetTabContentsAt(0); 118 } 119 120 void CloseDevToolsWindow() { 121 DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); 122 // UnregisterDevToolsClientHostFor may destroy window_ so store the browser 123 // first. 124 Browser* browser = window_->browser(); 125 devtools_manager->UnregisterDevToolsClientHostFor(inspected_rvh_); 126 127 // Wait only when DevToolsWindow has a browser. For docked DevTools, this 128 // is NULL and we skip the wait. 129 if (browser) 130 BrowserClosedObserver close_observer(browser); 131 } 132 133 TabContents* client_contents_; 134 DevToolsWindow* window_; 135 RenderViewHost* inspected_rvh_; 136}; 137 138 139class CancelableQuitTask : public Task { 140 public: 141 explicit CancelableQuitTask(const std::string& timeout_message) 142 : timeout_message_(timeout_message), 143 cancelled_(false) { 144 } 145 146 void cancel() { 147 cancelled_ = true; 148 } 149 150 virtual void Run() { 151 if (cancelled_) { 152 return; 153 } 154 FAIL() << timeout_message_; 155 MessageLoop::current()->Quit(); 156 } 157 158 private: 159 std::string timeout_message_; 160 bool cancelled_; 161}; 162 163 164// Base class for DevTools tests that test devtools functionality for 165// extensions and content scripts. 166class DevToolsExtensionDebugTest : public DevToolsSanityTest, 167 public NotificationObserver { 168 public: 169 DevToolsExtensionDebugTest() : DevToolsSanityTest() { 170 PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_); 171 test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools"); 172 test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions"); 173 } 174 175 protected: 176 // Load an extention from test\data\devtools\extensions\<extension_name> 177 void LoadExtension(const char* extension_name) { 178 FilePath path = test_extensions_dir_.AppendASCII(extension_name); 179 ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension."; 180 } 181 182 private: 183 bool LoadExtensionFromPath(const FilePath& path) { 184 ExtensionService* service = browser()->profile()->GetExtensionService(); 185 size_t num_before = service->extensions()->size(); 186 { 187 NotificationRegistrar registrar; 188 registrar.Add(this, NotificationType::EXTENSION_LOADED, 189 NotificationService::AllSources()); 190 CancelableQuitTask* delayed_quit = 191 new CancelableQuitTask("Extension load timed out."); 192 MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit, 193 4*1000); 194 service->LoadExtension(path); 195 ui_test_utils::RunMessageLoop(); 196 delayed_quit->cancel(); 197 } 198 size_t num_after = service->extensions()->size(); 199 if (num_after != (num_before + 1)) 200 return false; 201 202 return WaitForExtensionHostsToLoad(); 203 } 204 205 bool WaitForExtensionHostsToLoad() { 206 // Wait for all the extension hosts that exist to finish loading. 207 // NOTE: This assumes that the extension host list is not changing while 208 // this method is running. 209 210 NotificationRegistrar registrar; 211 registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING, 212 NotificationService::AllSources()); 213 CancelableQuitTask* delayed_quit = 214 new CancelableQuitTask("Extension host load timed out."); 215 MessageLoop::current()->PostDelayedTask(FROM_HERE, delayed_quit, 216 4*1000); 217 218 ExtensionProcessManager* manager = 219 browser()->profile()->GetExtensionProcessManager(); 220 for (ExtensionProcessManager::const_iterator iter = manager->begin(); 221 iter != manager->end();) { 222 if ((*iter)->did_stop_loading()) 223 ++iter; 224 else 225 ui_test_utils::RunMessageLoop(); 226 } 227 228 delayed_quit->cancel(); 229 return true; 230 } 231 232 void Observe(NotificationType type, 233 const NotificationSource& source, 234 const NotificationDetails& details) { 235 switch (type.value) { 236 case NotificationType::EXTENSION_LOADED: 237 case NotificationType::EXTENSION_HOST_DID_STOP_LOADING: 238 MessageLoopForUI::current()->Quit(); 239 break; 240 default: 241 NOTREACHED(); 242 break; 243 } 244 } 245 246 FilePath test_extensions_dir_; 247}; 248 249// Tests heap profiler. 250IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, FAILS_TestHeapProfiler) { 251 RunTest("testHeapProfiler", kHeapProfilerPage); 252} 253 254// Tests scripts panel showing. 255IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) { 256 RunTest("testShowScriptsTab", kDebuggerTestPage); 257} 258 259// Tests that scripts tab is populated with inspected scripts even if it 260// hadn't been shown by the moment inspected paged refreshed. 261// @see http://crbug.com/26312 262IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, 263 TestScriptsTabIsPopulatedOnInspectedPageRefresh) { 264 // Clear inspector settings to ensure that Elements will be 265 // current panel when DevTools window is open. 266 GetInspectedTab()->render_view_host()->delegate()->ClearInspectorSettings(); 267 RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh", 268 kDebuggerTestPage); 269} 270 271// Tests that a content script is in the scripts list. 272// This test is disabled, see bug 28961. 273IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest, 274 TestContentScriptIsPresent) { 275 LoadExtension("simple_content_script"); 276 RunTest("testContentScriptIsPresent", kPageWithContentScript); 277} 278 279// Tests that scripts are not duplicated after Scripts Panel switch. 280IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, 281 TestNoScriptDuplicatesOnPanelSwitch) { 282 RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage); 283} 284 285// Tests that debugger works correctly if pause event occurs when DevTools 286// frontend is being loaded. 287IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenLoadingDevTools) { 288 RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools); 289} 290 291// Tests that pressing 'Pause' will pause script execution if the script 292// is already running. 293IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) { 294 RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning); 295} 296 297} // namespace 298