// Copyright (c) 2012 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/bind.h" #include "base/cancelable_callback.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" #include "base/prefs/pref_service.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_timeouts.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/devtools/browser_list_tabcontents_provider.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h" #include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_switches.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/child_process_data.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_client_host.h" #include "content/public/browser/devtools_http_handler.h" #include "content/public/browser/devtools_manager.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/worker_service.h" #include "content/public/browser/worker_service_observer.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "extensions/common/switches.h" #include "net/socket/tcp_listen_socket.h" #include "net/test/spawned_test_server/spawned_test_server.h" using content::BrowserThread; using content::DevToolsManager; using content::DevToolsAgentHost; using content::NavigationController; using content::RenderViewHost; using content::WebContents; using content::WorkerService; using content::WorkerServiceObserver; namespace { const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.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"; const char kNavigateBackTestPage[] = "files/devtools/navigate_back.html"; const char kChunkedTestPage[] = "chunked"; const char kSlowTestPage[] = "chunked?waitBeforeHeaders=100&waitBetweenChunks=100&chunksNumber=2"; const char kSharedWorkerTestPage[] = "files/workers/workers_ui_shared_worker.html"; const char kReloadSharedWorkerTestPage[] = "files/workers/debug_shared_worker_initialization.html"; void RunTestFunction(DevToolsWindow* window, const char* test_name) { 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( content::ExecuteScriptAndExtractString( window->GetRenderViewHost(), "window.domAutomationController.send(" " '' + (window.uiTests && (typeof uiTests.runTest)));", &result)); if (result == "function") { ASSERT_TRUE( content::ExecuteScriptAndExtractString( window->GetRenderViewHost(), base::StringPrintf("uiTests.runTest('%s')", test_name), &result)); EXPECT_EQ("[OK]", result); } else { FAIL() << "DevTools front-end is broken."; } } class DevToolsSanityTest : public InProcessBrowserTest { public: DevToolsSanityTest() : window_(NULL), inspected_rvh_(NULL) {} protected: void RunTest(const std::string& test_name, const std::string& test_page) { OpenDevToolsWindow(test_page); RunTestFunction(window_, test_name.c_str()); CloseDevToolsWindow(); } void LoadTestPage(const std::string& test_page) { content::WindowedNotificationObserver load_observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); GURL url = test_server()->GetURL(test_page); ui_test_utils::NavigateToURL(browser(), url); load_observer.Wait(); } void OpenDevToolsWindow(const std::string& test_page) { ASSERT_TRUE(test_server()->Start()); LoadTestPage(test_page); content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); inspected_rvh_ = GetInspectedTab()->GetRenderViewHost(); window_ = DevToolsWindow::OpenDevToolsWindow(inspected_rvh_); observer.Wait(); } WebContents* GetInspectedTab() { return browser()->tab_strip_model()->GetWebContentsAt(0); } void ToggleDevToolsWindow() { content::WindowedNotificationObserver close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>(window_->web_contents())); DevToolsWindow::ToggleDevToolsWindow(inspected_rvh_, false, DevToolsToggleAction::Toggle()); close_observer.Wait(); } void CloseDevToolsWindow() { DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); content::WindowedNotificationObserver close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>(window_->web_contents())); devtools_manager->CloseAllClientHosts(); close_observer.Wait(); } DevToolsWindow* window_; RenderViewHost* inspected_rvh_; }; // Used to block until a dev tools window gets beforeunload event. class DevToolsWindowBeforeUnloadObserver : public content::WebContentsObserver { public: explicit DevToolsWindowBeforeUnloadObserver(DevToolsWindow*); void Wait(); private: // Invoked when the beforeunload handler fires. virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE; bool m_fired; scoped_refptr<content::MessageLoopRunner> message_loop_runner_; DISALLOW_COPY_AND_ASSIGN(DevToolsWindowBeforeUnloadObserver); }; DevToolsWindowBeforeUnloadObserver::DevToolsWindowBeforeUnloadObserver( DevToolsWindow* devtools_window) : WebContentsObserver(devtools_window->web_contents()), m_fired(false) { } void DevToolsWindowBeforeUnloadObserver::Wait() { if (m_fired) return; message_loop_runner_ = new content::MessageLoopRunner; message_loop_runner_->Run(); } void DevToolsWindowBeforeUnloadObserver::BeforeUnloadFired( const base::TimeTicks& proceed_time) { m_fired = true; if (message_loop_runner_.get()) message_loop_runner_->Quit(); } class DevToolsBeforeUnloadTest: public DevToolsSanityTest { public: virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { command_line->AppendSwitch( switches::kDisableHangMonitor); } void CloseInspectedTab() { browser()->tab_strip_model()->CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); } void CloseDockedDevTools() { DevToolsWindow::ToggleDevToolsWindow(inspected_rvh_, false, DevToolsToggleAction::Toggle()); } void CloseUndockedDevTools() { chrome::CloseWindow(window_->browser()); } void CloseInspectedBrowser() { chrome::CloseWindow(browser()); } protected: void InjectBeforeUnloadListener(content::WebContents* web_contents) { ASSERT_TRUE(content::ExecuteScript(web_contents->GetRenderViewHost(), "window.addEventListener('beforeunload'," "function(event) { event.returnValue = 'Foo'; });")); } void RunBeforeUnloadSanityTest(DevToolsDockSide dock_side, base::Callback<void(void)> close_method, bool wait_for_browser_close = true) { OpenDevToolsWindow(kDebuggerTestPage); window_->SetDockSideForTest(dock_side); content::WindowedNotificationObserver devtools_close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>(window_->web_contents())); InjectBeforeUnloadListener(window_->web_contents()); { DevToolsWindowBeforeUnloadObserver before_unload_observer(window_); close_method.Run(); CancelModalDialog(); before_unload_observer.Wait(); } { content::WindowedNotificationObserver close_observer( chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser())); close_method.Run(); AcceptModalDialog(); if (wait_for_browser_close) close_observer.Wait(); } devtools_close_observer.Wait(); } DevToolsWindow* OpenDevToolWindowOnWebContents( content::WebContents* contents) { content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); DevToolsWindow* window = DevToolsWindow::OpenDevToolsWindow( contents->GetRenderViewHost()); observer.Wait(); return window; } void OpenDevToolsPopupWindow(DevToolsWindow* devtools_window) { content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); ASSERT_TRUE(content::ExecuteScript( devtools_window->web_contents()->GetRenderViewHost(), "window.open(\"\", \"\", \"location=0\");")); observer.Wait(); } void CloseDevToolsPopupWindow(DevToolsWindow* devtools_window) { Browser* popup_browser = NULL; for (chrome::BrowserIterator it; !it.done(); it.Next()) { if (it->is_devtools()) { content::WebContents* contents = it->tab_strip_model()->GetWebContentsAt(0); if (devtools_window->web_contents() != contents) { popup_browser = *it; break; } } } ASSERT_FALSE(popup_browser == NULL); content::WindowedNotificationObserver close_observer( chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(popup_browser)); chrome::CloseWindow(popup_browser); close_observer.Wait(); } void AcceptModalDialog() { NativeAppModalDialog* native_dialog = GetDialog(); native_dialog->AcceptAppModalDialog(); } void CancelModalDialog() { NativeAppModalDialog* native_dialog = GetDialog(); native_dialog->CancelAppModalDialog(); } NativeAppModalDialog* GetDialog() { AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog(); EXPECT_TRUE(dialog->IsJavaScriptModalDialog()); JavaScriptAppModalDialog* js_dialog = static_cast<JavaScriptAppModalDialog*>(dialog); NativeAppModalDialog* native_dialog = js_dialog->native_dialog(); EXPECT_TRUE(native_dialog); return native_dialog; } }; class DevToolsUnresponsiveBeforeUnloadTest: public DevToolsBeforeUnloadTest { public: virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {} }; void TimeoutCallback(const std::string& timeout_message) { FAIL() << timeout_message; base::MessageLoop::current()->Quit(); } // Base class for DevTools tests that test devtools functionality for // extensions and content scripts. class DevToolsExtensionTest : public DevToolsSanityTest, public content::NotificationObserver { public: DevToolsExtensionTest() : 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 extension from test\data\devtools\extensions\<extension_name> void LoadExtension(const char* extension_name) { base::FilePath path = test_extensions_dir_.AppendASCII(extension_name); ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension."; } private: bool LoadExtensionFromPath(const base::FilePath& path) { ExtensionService* service = extensions::ExtensionSystem::Get( browser()->profile())->extension_service(); size_t num_before = service->extensions()->size(); { content::NotificationRegistrar registrar; registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, content::NotificationService::AllSources()); base::CancelableClosure timeout( base::Bind(&TimeoutCallback, "Extension load timed out.")); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, timeout.callback(), TestTimeouts::action_timeout()); extensions::UnpackedInstaller::Create(service)->Load(path); content::RunMessageLoop(); timeout.Cancel(); } size_t num_after = service->extensions()->size(); if (num_after != (num_before + 1)) return false; return WaitForExtensionViewsToLoad(); } bool WaitForExtensionViewsToLoad() { // Wait for all the extension render views that exist to finish loading. // NOTE: This assumes that the extension views list is not changing while // this method is running. content::NotificationRegistrar registrar; registrar.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING, content::NotificationService::AllSources()); base::CancelableClosure timeout( base::Bind(&TimeoutCallback, "Extension host load timed out.")); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, timeout.callback(), TestTimeouts::action_timeout()); extensions::ProcessManager* manager = extensions::ExtensionSystem::Get(browser()->profile())-> process_manager(); extensions::ProcessManager::ViewSet all_views = manager->GetAllViews(); for (extensions::ProcessManager::ViewSet::const_iterator iter = all_views.begin(); iter != all_views.end();) { if (!(*iter)->IsLoading()) ++iter; else content::RunMessageLoop(); } timeout.Cancel(); return true; } virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE { switch (type) { case chrome::NOTIFICATION_EXTENSION_LOADED: case chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING: base::MessageLoopForUI::current()->Quit(); break; default: NOTREACHED(); break; } } base::FilePath test_extensions_dir_; }; class DevToolsExperimentalExtensionTest : public DevToolsExtensionTest { public: virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { command_line->AppendSwitch( extensions::switches::kEnableExperimentalExtensionApis); } }; class WorkerDevToolsSanityTest : public InProcessBrowserTest { public: WorkerDevToolsSanityTest() : window_(NULL) {} protected: class WorkerData : public base::RefCountedThreadSafe<WorkerData> { public: WorkerData() : worker_process_id(0), worker_route_id(0) {} int worker_process_id; int worker_route_id; private: friend class base::RefCountedThreadSafe<WorkerData>; ~WorkerData() {} }; class WorkerCreationObserver : public WorkerServiceObserver { public: explicit WorkerCreationObserver(WorkerData* worker_data) : worker_data_(worker_data) { } private: virtual ~WorkerCreationObserver() {} virtual void WorkerCreated ( const GURL& url, const base::string16& name, int process_id, int route_id) OVERRIDE { worker_data_->worker_process_id = process_id; worker_data_->worker_route_id = route_id; WorkerService::GetInstance()->RemoveObserver(this); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); delete this; } scoped_refptr<WorkerData> worker_data_; }; class WorkerTerminationObserver : public WorkerServiceObserver { public: explicit WorkerTerminationObserver(WorkerData* worker_data) : worker_data_(worker_data) { } private: virtual ~WorkerTerminationObserver() {} virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE { ASSERT_EQ(worker_data_->worker_process_id, process_id); ASSERT_EQ(worker_data_->worker_route_id, route_id); WorkerService::GetInstance()->RemoveObserver(this); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); delete this; } scoped_refptr<WorkerData> worker_data_; }; void RunTest(const char* test_name, const char* test_page) { ASSERT_TRUE(test_server()->Start()); GURL url = test_server()->GetURL(test_page); ui_test_utils::NavigateToURL(browser(), url); scoped_refptr<WorkerData> worker_data = WaitForFirstSharedWorker(); OpenDevToolsWindowForSharedWorker(worker_data.get()); RunTestFunction(window_, test_name); CloseDevToolsWindow(); } static void TerminateWorkerOnIOThread(scoped_refptr<WorkerData> worker_data) { if (WorkerService::GetInstance()->TerminateWorker( worker_data->worker_process_id, worker_data->worker_route_id)) { WorkerService::GetInstance()->AddObserver( new WorkerTerminationObserver(worker_data.get())); return; } FAIL() << "Failed to terminate worker.\n"; } static void TerminateWorker(scoped_refptr<WorkerData> worker_data) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&TerminateWorkerOnIOThread, worker_data)); content::RunMessageLoop(); } static void WaitForFirstSharedWorkerOnIOThread( scoped_refptr<WorkerData> worker_data) { std::vector<WorkerService::WorkerInfo> worker_info = WorkerService::GetInstance()->GetWorkers(); if (!worker_info.empty()) { worker_data->worker_process_id = worker_info[0].process_id; worker_data->worker_route_id = worker_info[0].route_id; BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure()); return; } WorkerService::GetInstance()->AddObserver( new WorkerCreationObserver(worker_data.get())); } static scoped_refptr<WorkerData> WaitForFirstSharedWorker() { scoped_refptr<WorkerData> worker_data(new WorkerData()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&WaitForFirstSharedWorkerOnIOThread, worker_data)); content::RunMessageLoop(); return worker_data; } void OpenDevToolsWindowForSharedWorker(WorkerData* worker_data) { Profile* profile = browser()->profile(); window_ = DevToolsWindow::CreateDevToolsWindowForWorker(profile); window_->Show(DevToolsToggleAction::Show()); scoped_refptr<DevToolsAgentHost> agent_host( DevToolsAgentHost::GetForWorker( worker_data->worker_process_id, worker_data->worker_route_id)); DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( agent_host.get(), window_->GetDevToolsClientHostForTest()); RenderViewHost* client_rvh = window_->GetRenderViewHost(); WebContents* client_contents = WebContents::FromRenderViewHost(client_rvh); if (client_contents->IsLoading()) { content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::Source<NavigationController>( &client_contents->GetController())); observer.Wait(); } } void CloseDevToolsWindow() { Browser* browser = window_->browser(); content::WindowedNotificationObserver close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>(window_->web_contents())); browser->tab_strip_model()->CloseAllTabs(); close_observer.Wait(); } DevToolsWindow* window_; }; // Tests that BeforeUnload event gets called on docked devtools if // we try to close them. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_BOTTOM, base::Bind( &DevToolsBeforeUnloadTest::CloseDockedDevTools, this), false); } // Tests that BeforeUnload event gets called on docked devtools if // we try to close the inspected page. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsInspectedTabClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_BOTTOM, base::Bind( &DevToolsBeforeUnloadTest::CloseInspectedTab, this)); } // Tests that BeforeUnload event gets called on docked devtools if // we try to close the inspected browser. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsInspectedBrowserClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_BOTTOM, base::Bind( &DevToolsBeforeUnloadTest::CloseInspectedBrowser, this)); } // Tests that BeforeUnload event gets called on undocked devtools if // we try to close them. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_UNDOCKED, base::Bind( &DevToolsBeforeUnloadTest::CloseUndockedDevTools, this), false); } // Tests that BeforeUnload event gets called on undocked devtools if // we try to close the inspected page. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsInspectedTabClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_UNDOCKED, base::Bind( &DevToolsBeforeUnloadTest::CloseInspectedTab, this)); } // Tests that BeforeUnload event gets called on undocked devtools if // we try to close the inspected browser. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsInspectedBrowserClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_UNDOCKED, base::Bind( &DevToolsBeforeUnloadTest::CloseInspectedBrowser, this)); } // Tests that BeforeUnload event gets called on undocked devtools if // we try to exit application. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsApplicationClose) { RunBeforeUnloadSanityTest(DEVTOOLS_DOCK_SIDE_UNDOCKED, base::Bind( &chrome::CloseAllBrowsers)); } // Tests that inspected tab gets closed if devtools renderer // becomes unresponsive during beforeunload event interception. // @see http://crbug.com/322380 IN_PROC_BROWSER_TEST_F(DevToolsUnresponsiveBeforeUnloadTest, TestUndockedDevToolsUnresponsive) { ASSERT_TRUE(test_server()->Start()); LoadTestPage(kDebuggerTestPage); DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents( GetInspectedTab()); devtools_window->SetDockSideForTest(DEVTOOLS_DOCK_SIDE_UNDOCKED); content::WindowedNotificationObserver devtools_close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>( devtools_window->web_contents())); ASSERT_TRUE(content::ExecuteScript( devtools_window->web_contents()->GetRenderViewHost(), "window.addEventListener('beforeunload'," "function(event) { while (true); });")); CloseInspectedTab(); devtools_close_observer.Wait(); } // Tests that closing worker inspector window does not cause browser crash // @see http://crbug.com/323031 IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestWorkerWindowClosing) { ASSERT_TRUE(test_server()->Start()); LoadTestPage(kDebuggerTestPage); DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents( GetInspectedTab()); devtools_window->SetDockSideForTest(DEVTOOLS_DOCK_SIDE_UNDOCKED); content::WindowedNotificationObserver devtools_close_observer( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>( devtools_window->web_contents())); OpenDevToolsPopupWindow(devtools_window); CloseDevToolsPopupWindow(devtools_window); } // Tests that BeforeUnload event gets called on devtools that are opened // on another devtools. IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDevToolsOnDevTools) { ASSERT_TRUE(test_server()->Start()); LoadTestPage(kDebuggerTestPage); std::vector<DevToolsWindow*> windows; std::vector<content::WindowedNotificationObserver*> close_observers; content::WebContents* inspected_web_contents = GetInspectedTab(); for (int i = 0; i < 3; ++i) { DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents( inspected_web_contents); windows.push_back(devtools_window); content::WindowedNotificationObserver* close_observer = new content::WindowedNotificationObserver( content::NOTIFICATION_WEB_CONTENTS_DESTROYED, content::Source<content::WebContents>( devtools_window->web_contents())); close_observers.push_back(close_observer); inspected_web_contents = devtools_window->web_contents(); } InjectBeforeUnloadListener(windows[0]->web_contents()); InjectBeforeUnloadListener(windows[2]->web_contents()); // Try to close second devtools. { content::WindowedNotificationObserver cancel_browser( chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, content::NotificationService::AllSources()); chrome::CloseWindow(windows[1]->browser()); CancelModalDialog(); cancel_browser.Wait(); } // Try to close browser window. { content::WindowedNotificationObserver cancel_browser( chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED, content::NotificationService::AllSources()); chrome::CloseWindow(browser()); AcceptModalDialog(); CancelModalDialog(); cancel_browser.Wait(); } // Try to exit application. { content::WindowedNotificationObserver close_observer( chrome::NOTIFICATION_BROWSER_CLOSED, content::Source<Browser>(browser())); chrome::CloseAllBrowsers(); AcceptModalDialog(); AcceptModalDialog(); close_observer.Wait(); } for (size_t i = 0; i < close_observers.size(); ++i) { close_observers[i]->Wait(); delete close_observers[i]; } } // Tests scripts panel showing. // TODO(pfeldman): figure out flake. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_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. content::BrowserContext* browser_context = GetInspectedTab()->GetBrowserContext(); Profile::FromBrowserContext(browser_context)->GetPrefs()-> ClearPref(prefs::kWebKitInspectorSettings); RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh", kDebuggerTestPage); } // Tests that chrome.devtools extension is correctly exposed. IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestDevToolsExtensionAPI) { LoadExtension("devtools_extension"); RunTest("waitForTestResultsInConsole", std::string()); } // Disabled on Windows due to flakiness. http://crbug.com/183649 #if defined(OS_WIN) #define MAYBE_TestDevToolsExtensionMessaging DISABLED_TestDevToolsExtensionMessaging #else #define MAYBE_TestDevToolsExtensionMessaging TestDevToolsExtensionMessaging #endif // Tests that chrome.devtools extension can communicate with background page // using extension messaging. IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, MAYBE_TestDevToolsExtensionMessaging) { LoadExtension("devtools_messaging"); RunTest("waitForTestResultsInConsole", std::string()); } // Tests that chrome.experimental.devtools extension is correctly exposed // when the extension has experimental permission. IN_PROC_BROWSER_TEST_F(DevToolsExperimentalExtensionTest, TestDevToolsExperimentalExtensionAPI) { LoadExtension("devtools_experimental"); RunTest("waitForTestResultsInConsole", std::string()); } // Tests that a content script is in the scripts list. // History of flakiness: http://crbug.com/114104, http://crbug.com/315288. IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, DISABLED_TestContentScriptIsPresent) { LoadExtension("simple_content_script"); RunTest("testContentScriptIsPresent", kPageWithContentScript); } // Fails quite consistently on Win XP: crbug.com/317725. #if defined(OS_WIN) #define MAYBE_TestNoScriptDuplicatesOnPanelSwitch \ DISABLED_TestNoScriptDuplicatesOnPanelSwitch #else #define MAYBE_TestNoScriptDuplicatesOnPanelSwitch \ TestNoScriptDuplicatesOnPanelSwitch #endif // Tests that scripts are not duplicated after Scripts Panel switch. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestNoScriptDuplicatesOnPanelSwitch) { RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage); } #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) #define MAYBE_TestPauseWhenLoadingDevTools DISABLED_TestPauseWhenLoadingDevTools #else #define MAYBE_TestPauseWhenLoadingDevTools TestPauseWhenLoadingDevTools #endif // Tests that debugger works correctly if pause event occurs when DevTools // frontend is being loaded. // Disabled because of flakiness on multiple platforms: crbug.com/329036 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestPauseWhenLoadingDevTools) { RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools); } // Tests that pressing 'Pause' will pause script execution if the script // is already running. #if defined(OS_WIN) // Timing out on windows tryservers: http://crbug.com/219515 #define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY) // Timing out on linux ARM bot: https://crbug/238453 #define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning #else #define MAYBE_TestPauseWhenScriptIsRunning TestPauseWhenScriptIsRunning #endif IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestPauseWhenScriptIsRunning) { RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning); } // Tests network timing. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkTiming) { RunTest("testNetworkTiming", kSlowTestPage); } // Tests network size. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSize) { RunTest("testNetworkSize", kChunkedTestPage); } // Tests raw headers text. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSyncSize) { RunTest("testNetworkSyncSize", kChunkedTestPage); } // Tests raw headers text. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkRawHeadersText) { RunTest("testNetworkRawHeadersText", kChunkedTestPage); } // Tests that console messages are not duplicated on navigation back. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestConsoleOnNavigateBack) { RunTest("testConsoleOnNavigateBack", kNavigateBackTestPage); } // Tests that external navigation from inspector page is always handled by // DevToolsWindow and results in inspected page navigation. IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDevToolsExternalNavigation) { OpenDevToolsWindow(kDebuggerTestPage); GURL url = test_server()->GetURL(kNavigateBackTestPage); content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::NotificationService::AllSources()); ASSERT_TRUE(content::ExecuteScript( window_->web_contents(), std::string("window.location = \"") + url.spec() + "\"")); observer.Wait(); ASSERT_TRUE(window_->web_contents()->GetURL(). SchemeIs(chrome::kChromeDevToolsScheme)); ASSERT_EQ(GetInspectedTab()->GetURL(), url); CloseDevToolsWindow(); } #if defined(OS_WIN) // Flakily times out: http://crbug.com/163411 #define MAYBE_TestReattachAfterCrash DISABLED_TestReattachAfterCrash #else #define MAYBE_TestReattachAfterCrash TestReattachAfterCrash #endif // Tests that inspector will reattach to inspected page when it is reloaded // after a crash. See http://crbug.com/101952 IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestReattachAfterCrash) { OpenDevToolsWindow(kDebuggerTestPage); content::CrashTab(GetInspectedTab()); content::WindowedNotificationObserver observer( content::NOTIFICATION_LOAD_STOP, content::Source<NavigationController>( &browser()->tab_strip_model()->GetActiveWebContents()-> GetController())); chrome::Reload(browser(), CURRENT_TAB); observer.Wait(); RunTestFunction(window_, "testReattachAfterCrash"); CloseDevToolsWindow(); } IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPageWithNoJavaScript) { OpenDevToolsWindow("about:blank"); std::string result; ASSERT_TRUE( content::ExecuteScriptAndExtractString( window_->GetRenderViewHost(), "window.domAutomationController.send(" " '' + (window.uiTests && (typeof uiTests.runTest)));", &result)); ASSERT_EQ("function", result) << "DevTools front-end is broken."; CloseDevToolsWindow(); } #if defined(OS_MACOSX) #define MAYBE_InspectSharedWorker DISABLED_InspectSharedWorker #else #define MAYBE_InspectSharedWorker InspectSharedWorker #endif // Flakily fails with 25s timeout: http://crbug.com/89845 IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest, MAYBE_InspectSharedWorker) { #if defined(OS_WIN) && defined(USE_ASH) // Disable this test in Metro+Ash for now (http://crbug.com/262796). if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) return; #endif RunTest("testSharedWorker", kSharedWorkerTestPage); } // http://crbug.com/100538 #if defined(OS_MACOSX) || defined(OS_WIN) #define MAYBE_PauseInSharedWorkerInitialization DISABLED_PauseInSharedWorkerInitialization #else #define MAYBE_PauseInSharedWorkerInitialization PauseInSharedWorkerInitialization #endif // http://crbug.com/106114 is masking // MAYBE_PauseInSharedWorkerInitialization into // DISABLED_PauseInSharedWorkerInitialization IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest, MAYBE_PauseInSharedWorkerInitialization) { ASSERT_TRUE(test_server()->Start()); GURL url = test_server()->GetURL(kReloadSharedWorkerTestPage); ui_test_utils::NavigateToURL(browser(), url); scoped_refptr<WorkerData> worker_data = WaitForFirstSharedWorker(); OpenDevToolsWindowForSharedWorker(worker_data.get()); TerminateWorker(worker_data); // Reload page to restart the worker. ui_test_utils::NavigateToURL(browser(), url); // Wait until worker script is paused on the debugger statement. RunTestFunction(window_, "testPauseInSharedWorkerInitialization"); CloseDevToolsWindow(); } class DevToolsAgentHostTest : public InProcessBrowserTest {}; // Tests DevToolsAgentHost retention by its target. IN_PROC_BROWSER_TEST_F(DevToolsAgentHostTest, TestAgentHostReleased) { ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); RenderViewHost* rvh = browser()->tab_strip_model()->GetWebContentsAt(0)-> GetRenderViewHost(); DevToolsAgentHost* agent_raw = DevToolsAgentHost::GetOrCreateFor(rvh).get(); const std::string agent_id = agent_raw->GetId(); ASSERT_EQ(agent_raw, DevToolsAgentHost::GetForId(agent_id)) << "DevToolsAgentHost cannot be found by id"; browser()->tab_strip_model()-> CloseWebContentsAt(0, TabStripModel::CLOSE_NONE); ASSERT_FALSE(DevToolsAgentHost::GetForId(agent_id).get()) << "DevToolsAgentHost is not released when the tab is closed"; } class RemoteDebuggingTest: public ExtensionApiTest { virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { ExtensionApiTest::SetUpCommandLine(command_line); command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, "9222"); // Override the extension root path. PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); test_data_dir_ = test_data_dir_.AppendASCII("devtools"); } }; IN_PROC_BROWSER_TEST_F(RemoteDebuggingTest, RemoteDebugger) { #if defined(OS_WIN) && defined(USE_ASH) // Disable this test in Metro+Ash for now (http://crbug.com/262796). if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) return; #endif ASSERT_TRUE(RunExtensionTest("target_list")) << message_; } } // namespace