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
25 namespace {
26
27 // Used to block until a dev tools client window's browser is closed.
28 class BrowserClosedObserver : public NotificationObserver {
29 public:
BrowserClosedObserver(Browser * browser)30 explicit BrowserClosedObserver(Browser* browser) {
31 registrar_.Add(this, NotificationType::BROWSER_CLOSED,
32 Source<Browser>(browser));
33 ui_test_utils::RunMessageLoop();
34 }
35
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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.
49 const int kActionDelayMs = 500;
50
51 const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html";
52 const char kHeapProfilerPage[] = "files/devtools/heap_profiler.html";
53 const char kPauseWhenLoadingDevTools[] =
54 "files/devtools/pause_when_loading_devtools.html";
55 const char kPauseWhenScriptIsRunning[] =
56 "files/devtools/pause_when_script_is_running.html";
57 const char kPageWithContentScript[] =
58 "files/devtools/page_with_content_script.html";
59
60
61 class DevToolsSanityTest : public InProcessBrowserTest {
62 public:
DevToolsSanityTest()63 DevToolsSanityTest() {
64 set_show_window(true);
65 EnableDOMAutomation();
66 }
67
68 protected:
RunTest(const std::string & test_name,const std::string & test_page)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
OpenDevToolsWindow(const std::string & test_page)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
GetInspectedTab()116 TabContents* GetInspectedTab() {
117 return browser()->GetTabContentsAt(0);
118 }
119
CloseDevToolsWindow()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
139 class CancelableQuitTask : public Task {
140 public:
CancelableQuitTask(const std::string & timeout_message)141 explicit CancelableQuitTask(const std::string& timeout_message)
142 : timeout_message_(timeout_message),
143 cancelled_(false) {
144 }
145
cancel()146 void cancel() {
147 cancelled_ = true;
148 }
149
Run()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.
166 class DevToolsExtensionDebugTest : public DevToolsSanityTest,
167 public NotificationObserver {
168 public:
DevToolsExtensionDebugTest()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>
LoadExtension(const char * 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:
LoadExtensionFromPath(const FilePath & path)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
WaitForExtensionHostsToLoad()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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,FAILS_TestHeapProfiler)250 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, FAILS_TestHeapProfiler) {
251 RunTest("testHeapProfiler", kHeapProfilerPage);
252 }
253
254 // Tests scripts panel showing.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,TestShowScriptsTab)255 IN_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
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,TestScriptsTabIsPopulatedOnInspectedPageRefresh)262 IN_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.
IN_PROC_BROWSER_TEST_F(DevToolsExtensionDebugTest,TestContentScriptIsPresent)273 IN_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.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,TestNoScriptDuplicatesOnPanelSwitch)280 IN_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.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,TestPauseWhenLoadingDevTools)287 IN_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.
IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,TestPauseWhenScriptIsRunning)293 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPauseWhenScriptIsRunning) {
294 RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
295 }
296
297 } // namespace
298