1// Copyright (c) 2012 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/bind.h"
6#include "base/cancelable_callback.h"
7#include "base/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/memory/ref_counted.h"
10#include "base/path_service.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "base/test/test_timeouts.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/devtools/browser_list_tabcontents_provider.h"
17#include "chrome/browser/devtools/devtools_window_testing.h"
18#include "chrome/browser/extensions/extension_apitest.h"
19#include "chrome/browser/extensions/extension_browsertest.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/extensions/unpacked_installer.h"
22#include "chrome/browser/lifetime/application_lifetime.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/ui/app_modal_dialogs/javascript_app_modal_dialog.h"
25#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
26#include "chrome/browser/ui/browser.h"
27#include "chrome/browser/ui/browser_commands.h"
28#include "chrome/browser/ui/browser_iterator.h"
29#include "chrome/browser/ui/tabs/tab_strip_model.h"
30#include "chrome/common/chrome_paths.h"
31#include "chrome/common/chrome_switches.h"
32#include "chrome/common/pref_names.h"
33#include "chrome/common/url_constants.h"
34#include "chrome/test/base/in_process_browser_test.h"
35#include "chrome/test/base/test_switches.h"
36#include "chrome/test/base/ui_test_utils.h"
37#include "content/public/browser/child_process_data.h"
38#include "content/public/browser/content_browser_client.h"
39#include "content/public/browser/devtools_agent_host.h"
40#include "content/public/browser/devtools_http_handler.h"
41#include "content/public/browser/notification_registrar.h"
42#include "content/public/browser/notification_service.h"
43#include "content/public/browser/render_view_host.h"
44#include "content/public/browser/web_contents.h"
45#include "content/public/browser/worker_service.h"
46#include "content/public/browser/worker_service_observer.h"
47#include "content/public/common/content_switches.h"
48#include "content/public/test/browser_test_utils.h"
49#include "extensions/browser/extension_system.h"
50#include "extensions/browser/notification_types.h"
51#include "extensions/common/switches.h"
52#include "net/socket/tcp_listen_socket.h"
53#include "net/test/spawned_test_server/spawned_test_server.h"
54
55using content::BrowserThread;
56using content::DevToolsAgentHost;
57using content::NavigationController;
58using content::RenderViewHost;
59using content::WebContents;
60using content::WorkerService;
61using content::WorkerServiceObserver;
62
63namespace {
64
65const char kDebuggerTestPage[] = "files/devtools/debugger_test_page.html";
66const char kPauseWhenLoadingDevTools[] =
67    "files/devtools/pause_when_loading_devtools.html";
68const char kPauseWhenScriptIsRunning[] =
69    "files/devtools/pause_when_script_is_running.html";
70const char kPageWithContentScript[] =
71    "files/devtools/page_with_content_script.html";
72const char kNavigateBackTestPage[] =
73    "files/devtools/navigate_back.html";
74const char kChunkedTestPage[] = "chunked";
75const char kSlowTestPage[] =
76    "chunked?waitBeforeHeaders=100&waitBetweenChunks=100&chunksNumber=2";
77const char kSharedWorkerTestPage[] =
78    "files/workers/workers_ui_shared_worker.html";
79const char kReloadSharedWorkerTestPage[] =
80    "files/workers/debug_shared_worker_initialization.html";
81
82void RunTestFunction(DevToolsWindow* window, const char* test_name) {
83  std::string result;
84
85  RenderViewHost* rvh = DevToolsWindowTesting::Get(window)->
86      main_web_contents()->GetRenderViewHost();
87  // At first check that JavaScript part of the front-end is loaded by
88  // checking that global variable uiTests exists(it's created after all js
89  // files have been loaded) and has runTest method.
90  ASSERT_TRUE(
91      content::ExecuteScriptAndExtractString(
92          rvh,
93          "window.domAutomationController.send("
94          "    '' + (window.uiTests && (typeof uiTests.runTest)));",
95          &result));
96
97  ASSERT_EQ("function", result) << "DevTools front-end is broken.";
98  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
99      rvh,
100      base::StringPrintf("uiTests.runTest('%s')", test_name),
101      &result));
102  EXPECT_EQ("[OK]", result);
103}
104
105}  // namespace
106
107class DevToolsSanityTest : public InProcessBrowserTest {
108 public:
109  DevToolsSanityTest() : window_(NULL) {}
110
111 protected:
112  void RunTest(const std::string& test_name, const std::string& test_page) {
113    OpenDevToolsWindow(test_page, false);
114    RunTestFunction(window_, test_name.c_str());
115    CloseDevToolsWindow();
116  }
117
118  void LoadTestPage(const std::string& test_page) {
119    GURL url = test_server()->GetURL(test_page);
120    ui_test_utils::NavigateToURL(browser(), url);
121  }
122
123  void OpenDevToolsWindow(const std::string& test_page, bool is_docked) {
124    ASSERT_TRUE(test_server()->Start());
125    LoadTestPage(test_page);
126
127    window_ = DevToolsWindowTesting::OpenDevToolsWindowSync(GetInspectedTab(),
128                                                            is_docked);
129  }
130
131  WebContents* GetInspectedTab() {
132    return browser()->tab_strip_model()->GetWebContentsAt(0);
133  }
134
135  void CloseDevToolsWindow() {
136    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
137  }
138
139  WebContents* main_web_contents() {
140    return DevToolsWindowTesting::Get(window_)->main_web_contents();
141  }
142
143  WebContents* toolbox_web_contents() {
144    return DevToolsWindowTesting::Get(window_)->toolbox_web_contents();
145  }
146
147  DevToolsWindow* window_;
148};
149
150// Used to block until a dev tools window gets beforeunload event.
151class DevToolsWindowBeforeUnloadObserver
152    : public content::WebContentsObserver {
153 public:
154  explicit DevToolsWindowBeforeUnloadObserver(DevToolsWindow*);
155  void Wait();
156 private:
157  // Invoked when the beforeunload handler fires.
158  virtual void BeforeUnloadFired(const base::TimeTicks& proceed_time) OVERRIDE;
159
160  bool m_fired;
161  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
162  DISALLOW_COPY_AND_ASSIGN(DevToolsWindowBeforeUnloadObserver);
163};
164
165DevToolsWindowBeforeUnloadObserver::DevToolsWindowBeforeUnloadObserver(
166    DevToolsWindow* devtools_window)
167    : WebContentsObserver(
168          DevToolsWindowTesting::Get(devtools_window)->main_web_contents()),
169      m_fired(false) {
170}
171
172void DevToolsWindowBeforeUnloadObserver::Wait() {
173  if (m_fired)
174    return;
175  message_loop_runner_ = new content::MessageLoopRunner;
176  message_loop_runner_->Run();
177}
178
179void DevToolsWindowBeforeUnloadObserver::BeforeUnloadFired(
180    const base::TimeTicks& proceed_time) {
181  m_fired = true;
182  if (message_loop_runner_.get())
183    message_loop_runner_->Quit();
184}
185
186class DevToolsBeforeUnloadTest: public DevToolsSanityTest {
187 public:
188  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
189    command_line->AppendSwitch(
190        switches::kDisableHangMonitor);
191  }
192
193  void CloseInspectedTab() {
194    browser()->tab_strip_model()->CloseWebContentsAt(0,
195        TabStripModel::CLOSE_NONE);
196  }
197
198  void CloseDevToolsWindowAsync() {
199    DevToolsWindowTesting::CloseDevToolsWindow(window_);
200  }
201
202  void CloseInspectedBrowser() {
203    chrome::CloseWindow(browser());
204  }
205
206 protected:
207  void InjectBeforeUnloadListener(content::WebContents* web_contents) {
208    ASSERT_TRUE(content::ExecuteScript(web_contents->GetRenderViewHost(),
209        "window.addEventListener('beforeunload',"
210        "function(event) { event.returnValue = 'Foo'; });"));
211  }
212
213  void RunBeforeUnloadSanityTest(bool is_docked,
214                                 base::Callback<void(void)> close_method,
215                                 bool wait_for_browser_close = true) {
216    OpenDevToolsWindow(kDebuggerTestPage, is_docked);
217    scoped_refptr<content::MessageLoopRunner> runner =
218        new content::MessageLoopRunner;
219    DevToolsWindowTesting::Get(window_)->
220        SetCloseCallback(runner->QuitClosure());
221    InjectBeforeUnloadListener(main_web_contents());
222    {
223      DevToolsWindowBeforeUnloadObserver before_unload_observer(window_);
224      close_method.Run();
225      CancelModalDialog();
226      before_unload_observer.Wait();
227    }
228    {
229      content::WindowedNotificationObserver close_observer(
230          chrome::NOTIFICATION_BROWSER_CLOSED,
231          content::Source<Browser>(browser()));
232      close_method.Run();
233      AcceptModalDialog();
234      if (wait_for_browser_close)
235        close_observer.Wait();
236    }
237    runner->Run();
238  }
239
240  DevToolsWindow* OpenDevToolWindowOnWebContents(
241      content::WebContents* contents, bool is_docked) {
242    DevToolsWindow* window =
243        DevToolsWindowTesting::OpenDevToolsWindowSync(contents, is_docked);
244    return window;
245  }
246
247  void OpenDevToolsPopupWindow(DevToolsWindow* devtools_window) {
248    content::WindowedNotificationObserver observer(
249        content::NOTIFICATION_LOAD_STOP,
250        content::NotificationService::AllSources());
251    ASSERT_TRUE(content::ExecuteScript(
252        DevToolsWindowTesting::Get(devtools_window)->
253            main_web_contents()->GetRenderViewHost(),
254        "window.open(\"\", \"\", \"location=0\");"));
255    observer.Wait();
256  }
257
258  void CloseDevToolsPopupWindow(DevToolsWindow* devtools_window) {
259    DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
260  }
261
262  void AcceptModalDialog() {
263    NativeAppModalDialog* native_dialog = GetDialog();
264    native_dialog->AcceptAppModalDialog();
265  }
266
267  void CancelModalDialog() {
268    NativeAppModalDialog* native_dialog = GetDialog();
269    native_dialog->CancelAppModalDialog();
270  }
271
272  NativeAppModalDialog* GetDialog() {
273    AppModalDialog* dialog = ui_test_utils::WaitForAppModalDialog();
274    EXPECT_TRUE(dialog->IsJavaScriptModalDialog());
275    JavaScriptAppModalDialog* js_dialog =
276        static_cast<JavaScriptAppModalDialog*>(dialog);
277    NativeAppModalDialog* native_dialog = js_dialog->native_dialog();
278    EXPECT_TRUE(native_dialog);
279    return native_dialog;
280  }
281};
282
283class DevToolsUnresponsiveBeforeUnloadTest: public DevToolsBeforeUnloadTest {
284 public:
285  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {}
286};
287
288void TimeoutCallback(const std::string& timeout_message) {
289  ADD_FAILURE() << timeout_message;
290  base::MessageLoop::current()->Quit();
291}
292
293// Base class for DevTools tests that test devtools functionality for
294// extensions and content scripts.
295class DevToolsExtensionTest : public DevToolsSanityTest,
296                              public content::NotificationObserver {
297 public:
298  DevToolsExtensionTest() : DevToolsSanityTest() {
299    PathService::Get(chrome::DIR_TEST_DATA, &test_extensions_dir_);
300    test_extensions_dir_ = test_extensions_dir_.AppendASCII("devtools");
301    test_extensions_dir_ = test_extensions_dir_.AppendASCII("extensions");
302  }
303
304 protected:
305  // Load an extension from test\data\devtools\extensions\<extension_name>
306  void LoadExtension(const char* extension_name) {
307    base::FilePath path = test_extensions_dir_.AppendASCII(extension_name);
308    ASSERT_TRUE(LoadExtensionFromPath(path)) << "Failed to load extension.";
309  }
310
311 private:
312  bool LoadExtensionFromPath(const base::FilePath& path) {
313    ExtensionService* service = extensions::ExtensionSystem::Get(
314        browser()->profile())->extension_service();
315    size_t num_before = service->extensions()->size();
316    {
317      content::NotificationRegistrar registrar;
318      registrar.Add(this,
319                    extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
320                    content::NotificationService::AllSources());
321      base::CancelableClosure timeout(
322          base::Bind(&TimeoutCallback, "Extension load timed out."));
323      base::MessageLoop::current()->PostDelayedTask(
324          FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
325      extensions::UnpackedInstaller::Create(service)->Load(path);
326      content::RunMessageLoop();
327      timeout.Cancel();
328    }
329    size_t num_after = service->extensions()->size();
330    if (num_after != (num_before + 1))
331      return false;
332
333    return WaitForExtensionViewsToLoad();
334  }
335
336  bool WaitForExtensionViewsToLoad() {
337    // Wait for all the extension render views that exist to finish loading.
338    // NOTE: This assumes that the extension views list is not changing while
339    // this method is running.
340
341    content::NotificationRegistrar registrar;
342    registrar.Add(this,
343                  extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
344                  content::NotificationService::AllSources());
345    base::CancelableClosure timeout(
346        base::Bind(&TimeoutCallback, "Extension host load timed out."));
347    base::MessageLoop::current()->PostDelayedTask(
348        FROM_HERE, timeout.callback(), TestTimeouts::action_timeout());
349
350    extensions::ProcessManager* manager =
351        extensions::ExtensionSystem::Get(browser()->profile())->
352            process_manager();
353    extensions::ProcessManager::ViewSet all_views = manager->GetAllViews();
354    for (extensions::ProcessManager::ViewSet::const_iterator iter =
355             all_views.begin();
356         iter != all_views.end();) {
357      if (!(*iter)->IsLoading())
358        ++iter;
359      else
360        content::RunMessageLoop();
361    }
362
363    timeout.Cancel();
364    return true;
365  }
366
367  virtual void Observe(int type,
368                       const content::NotificationSource& source,
369                       const content::NotificationDetails& details) OVERRIDE {
370    switch (type) {
371      case extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED:
372      case extensions::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING:
373        base::MessageLoopForUI::current()->Quit();
374        break;
375      default:
376        NOTREACHED();
377        break;
378    }
379  }
380
381  base::FilePath test_extensions_dir_;
382};
383
384class DevToolsExperimentalExtensionTest : public DevToolsExtensionTest {
385 public:
386  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
387    command_line->AppendSwitch(
388        extensions::switches::kEnableExperimentalExtensionApis);
389  }
390};
391
392class WorkerDevToolsSanityTest : public InProcessBrowserTest {
393 public:
394  WorkerDevToolsSanityTest() : window_(NULL) {}
395
396 protected:
397  class WorkerData : public base::RefCountedThreadSafe<WorkerData> {
398   public:
399    WorkerData() : worker_process_id(0), worker_route_id(0) {}
400    int worker_process_id;
401    int worker_route_id;
402
403   private:
404    friend class base::RefCountedThreadSafe<WorkerData>;
405    ~WorkerData() {}
406  };
407
408  class WorkerCreationObserver : public WorkerServiceObserver {
409   public:
410    explicit WorkerCreationObserver(WorkerData* worker_data)
411        : worker_data_(worker_data) {
412    }
413
414   private:
415    virtual ~WorkerCreationObserver() {}
416
417    virtual void WorkerCreated (
418        const GURL& url,
419        const base::string16& name,
420        int process_id,
421        int route_id) OVERRIDE {
422      worker_data_->worker_process_id = process_id;
423      worker_data_->worker_route_id = route_id;
424      WorkerService::GetInstance()->RemoveObserver(this);
425      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
426          base::MessageLoop::QuitClosure());
427      delete this;
428    }
429    scoped_refptr<WorkerData> worker_data_;
430  };
431
432  class WorkerTerminationObserver : public WorkerServiceObserver {
433   public:
434    explicit WorkerTerminationObserver(WorkerData* worker_data)
435        : worker_data_(worker_data) {
436    }
437
438   private:
439    virtual ~WorkerTerminationObserver() {}
440
441    virtual void WorkerDestroyed(int process_id, int route_id) OVERRIDE {
442      ASSERT_EQ(worker_data_->worker_process_id, process_id);
443      ASSERT_EQ(worker_data_->worker_route_id, route_id);
444      WorkerService::GetInstance()->RemoveObserver(this);
445      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
446          base::MessageLoop::QuitClosure());
447      delete this;
448    }
449    scoped_refptr<WorkerData> worker_data_;
450  };
451
452  void RunTest(const char* test_name, const char* test_page) {
453    ASSERT_TRUE(test_server()->Start());
454    GURL url = test_server()->GetURL(test_page);
455    ui_test_utils::NavigateToURL(browser(), url);
456
457    scoped_refptr<WorkerData> worker_data = WaitForFirstSharedWorker();
458    OpenDevToolsWindowForSharedWorker(worker_data.get());
459    RunTestFunction(window_, test_name);
460    CloseDevToolsWindow();
461  }
462
463  static void TerminateWorkerOnIOThread(scoped_refptr<WorkerData> worker_data) {
464    if (!WorkerService::GetInstance()->TerminateWorker(
465        worker_data->worker_process_id, worker_data->worker_route_id))
466      FAIL() << "Failed to terminate worker.\n";
467    WorkerService::GetInstance()->AddObserver(
468        new WorkerTerminationObserver(worker_data.get()));
469  }
470
471  static void TerminateWorker(scoped_refptr<WorkerData> worker_data) {
472    BrowserThread::PostTask(
473        BrowserThread::IO, FROM_HERE,
474        base::Bind(&TerminateWorkerOnIOThread, worker_data));
475    content::RunMessageLoop();
476  }
477
478  static void WaitForFirstSharedWorkerOnIOThread(
479      scoped_refptr<WorkerData> worker_data) {
480    std::vector<WorkerService::WorkerInfo> worker_info =
481        WorkerService::GetInstance()->GetWorkers();
482    if (!worker_info.empty()) {
483      worker_data->worker_process_id = worker_info[0].process_id;
484      worker_data->worker_route_id = worker_info[0].route_id;
485      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
486          base::MessageLoop::QuitClosure());
487      return;
488    }
489
490    WorkerService::GetInstance()->AddObserver(
491        new WorkerCreationObserver(worker_data.get()));
492  }
493
494  static scoped_refptr<WorkerData> WaitForFirstSharedWorker() {
495    scoped_refptr<WorkerData> worker_data(new WorkerData());
496    BrowserThread::PostTask(
497        BrowserThread::IO, FROM_HERE,
498        base::Bind(&WaitForFirstSharedWorkerOnIOThread, worker_data));
499    content::RunMessageLoop();
500    return worker_data;
501  }
502
503  void OpenDevToolsWindowForSharedWorker(WorkerData* worker_data) {
504    Profile* profile = browser()->profile();
505    scoped_refptr<DevToolsAgentHost> agent_host(
506        DevToolsAgentHost::GetForWorker(
507            worker_data->worker_process_id,
508            worker_data->worker_route_id));
509    window_ = DevToolsWindowTesting::OpenDevToolsWindowForWorkerSync(
510        profile, agent_host.get());
511  }
512
513  void CloseDevToolsWindow() {
514    DevToolsWindowTesting::CloseDevToolsWindowSync(window_);
515  }
516
517  DevToolsWindow* window_;
518};
519
520// Tests that BeforeUnload event gets called on docked devtools if
521// we try to close them.
522IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestDockedDevToolsClose) {
523  RunBeforeUnloadSanityTest(true, base::Bind(
524      &DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync, this), false);
525}
526
527// Tests that BeforeUnload event gets called on docked devtools if
528// we try to close the inspected page.
529IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
530                       TestDockedDevToolsInspectedTabClose) {
531  RunBeforeUnloadSanityTest(true, base::Bind(
532      &DevToolsBeforeUnloadTest::CloseInspectedTab, this));
533}
534
535// Tests that BeforeUnload event gets called on docked devtools if
536// we try to close the inspected browser.
537IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
538                       TestDockedDevToolsInspectedBrowserClose) {
539  RunBeforeUnloadSanityTest(true, base::Bind(
540      &DevToolsBeforeUnloadTest::CloseInspectedBrowser, this));
541}
542
543// Tests that BeforeUnload event gets called on undocked devtools if
544// we try to close them.
545IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, TestUndockedDevToolsClose) {
546  RunBeforeUnloadSanityTest(false, base::Bind(
547      &DevToolsBeforeUnloadTest::CloseDevToolsWindowAsync, this), false);
548}
549
550// Tests that BeforeUnload event gets called on undocked devtools if
551// we try to close the inspected page.
552IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
553                       TestUndockedDevToolsInspectedTabClose) {
554  RunBeforeUnloadSanityTest(false, base::Bind(
555      &DevToolsBeforeUnloadTest::CloseInspectedTab, this));
556}
557
558// Tests that BeforeUnload event gets called on undocked devtools if
559// we try to close the inspected browser.
560IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
561                       TestUndockedDevToolsInspectedBrowserClose) {
562  RunBeforeUnloadSanityTest(false, base::Bind(
563      &DevToolsBeforeUnloadTest::CloseInspectedBrowser, this));
564}
565
566// Tests that BeforeUnload event gets called on undocked devtools if
567// we try to exit application.
568IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
569                       TestUndockedDevToolsApplicationClose) {
570  RunBeforeUnloadSanityTest(false, base::Bind(
571      &chrome::CloseAllBrowsers));
572}
573
574// Times out on Win and Linux
575// @see http://crbug.com/410327
576#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
577#define MAYBE_TestUndockedDevToolsUnresponsive DISABLED_TestUndockedDevToolsUnresponsive
578#else
579#define MAYBE_TestUndockedDevToolsUnresponsive TestUndockedDevToolsUnresponsive
580#endif
581
582// Tests that inspected tab gets closed if devtools renderer
583// becomes unresponsive during beforeunload event interception.
584// @see http://crbug.com/322380
585IN_PROC_BROWSER_TEST_F(DevToolsUnresponsiveBeforeUnloadTest,
586                       MAYBE_TestUndockedDevToolsUnresponsive) {
587  ASSERT_TRUE(test_server()->Start());
588  LoadTestPage(kDebuggerTestPage);
589  DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
590      GetInspectedTab(), false);
591
592  scoped_refptr<content::MessageLoopRunner> runner =
593      new content::MessageLoopRunner;
594  DevToolsWindowTesting::Get(devtools_window)->SetCloseCallback(
595      runner->QuitClosure());
596
597  ASSERT_TRUE(content::ExecuteScript(
598      DevToolsWindowTesting::Get(devtools_window)->main_web_contents()->
599          GetRenderViewHost(),
600      "window.addEventListener('beforeunload',"
601      "function(event) { while (true); });"));
602  CloseInspectedTab();
603  runner->Run();
604}
605
606// Tests that closing worker inspector window does not cause browser crash
607// @see http://crbug.com/323031
608IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
609                       TestWorkerWindowClosing) {
610  ASSERT_TRUE(test_server()->Start());
611  LoadTestPage(kDebuggerTestPage);
612  DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
613      GetInspectedTab(), false);
614
615  OpenDevToolsPopupWindow(devtools_window);
616  CloseDevToolsPopupWindow(devtools_window);
617}
618
619// Tests that BeforeUnload event gets called on devtools that are opened
620// on another devtools.
621IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest,
622                       TestDevToolsOnDevTools) {
623  ASSERT_TRUE(test_server()->Start());
624  LoadTestPage(kDebuggerTestPage);
625
626  std::vector<DevToolsWindow*> windows;
627  std::vector<content::WindowedNotificationObserver*> close_observers;
628  content::WebContents* inspected_web_contents = GetInspectedTab();
629  for (int i = 0; i < 3; ++i) {
630    DevToolsWindow* devtools_window = OpenDevToolWindowOnWebContents(
631      inspected_web_contents, i == 0);
632    windows.push_back(devtools_window);
633    content::WindowedNotificationObserver* close_observer =
634        new content::WindowedNotificationObserver(
635                content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
636                content::Source<content::WebContents>(
637                    DevToolsWindowTesting::Get(devtools_window)->
638                        main_web_contents()));
639    close_observers.push_back(close_observer);
640    inspected_web_contents =
641        DevToolsWindowTesting::Get(devtools_window)->main_web_contents();
642  }
643
644  InjectBeforeUnloadListener(
645      DevToolsWindowTesting::Get(windows[0])->main_web_contents());
646  InjectBeforeUnloadListener(
647      DevToolsWindowTesting::Get(windows[2])->main_web_contents());
648  // Try to close second devtools.
649  {
650    content::WindowedNotificationObserver cancel_browser(
651        chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
652        content::NotificationService::AllSources());
653    chrome::CloseWindow(DevToolsWindowTesting::Get(windows[1])->browser());
654    CancelModalDialog();
655    cancel_browser.Wait();
656  }
657  // Try to close browser window.
658  {
659    content::WindowedNotificationObserver cancel_browser(
660        chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED,
661        content::NotificationService::AllSources());
662    chrome::CloseWindow(browser());
663    AcceptModalDialog();
664    CancelModalDialog();
665    cancel_browser.Wait();
666  }
667  // Try to exit application.
668  {
669    content::WindowedNotificationObserver close_observer(
670        chrome::NOTIFICATION_BROWSER_CLOSED,
671        content::Source<Browser>(browser()));
672    chrome::IncrementKeepAliveCount();
673    chrome::CloseAllBrowsers();
674    AcceptModalDialog();
675    AcceptModalDialog();
676    close_observer.Wait();
677  }
678  for (size_t i = 0; i < close_observers.size(); ++i) {
679    close_observers[i]->Wait();
680    delete close_observers[i];
681  }
682}
683
684// Tests scripts panel showing.
685IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) {
686  RunTest("testShowScriptsTab", kDebuggerTestPage);
687}
688
689// Tests that scripts tab is populated with inspected scripts even if it
690// hadn't been shown by the moment inspected paged refreshed.
691// @see http://crbug.com/26312
692IN_PROC_BROWSER_TEST_F(
693    DevToolsSanityTest,
694    TestScriptsTabIsPopulatedOnInspectedPageRefresh) {
695  RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh",
696          kDebuggerTestPage);
697}
698
699// Tests that chrome.devtools extension is correctly exposed.
700IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
701                       TestDevToolsExtensionAPI) {
702  LoadExtension("devtools_extension");
703  RunTest("waitForTestResultsInConsole", std::string());
704}
705
706// Disabled on Windows due to flakiness. http://crbug.com/183649
707#if defined(OS_WIN)
708#define MAYBE_TestDevToolsExtensionMessaging DISABLED_TestDevToolsExtensionMessaging
709#else
710#define MAYBE_TestDevToolsExtensionMessaging TestDevToolsExtensionMessaging
711#endif
712
713// Tests that chrome.devtools extension can communicate with background page
714// using extension messaging.
715IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
716                       MAYBE_TestDevToolsExtensionMessaging) {
717  LoadExtension("devtools_messaging");
718  RunTest("waitForTestResultsInConsole", std::string());
719}
720
721// Tests that chrome.experimental.devtools extension is correctly exposed
722// when the extension has experimental permission.
723IN_PROC_BROWSER_TEST_F(DevToolsExperimentalExtensionTest,
724                       TestDevToolsExperimentalExtensionAPI) {
725  LoadExtension("devtools_experimental");
726  RunTest("waitForTestResultsInConsole", std::string());
727}
728
729// Tests that a content script is in the scripts list.
730IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest,
731                       TestContentScriptIsPresent) {
732  LoadExtension("simple_content_script");
733  RunTest("testContentScriptIsPresent", kPageWithContentScript);
734}
735
736// Tests that scripts are not duplicated after Scripts Panel switch.
737IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
738                       TestNoScriptDuplicatesOnPanelSwitch) {
739  RunTest("testNoScriptDuplicatesOnPanelSwitch", kDebuggerTestPage);
740}
741
742// Tests that debugger works correctly if pause event occurs when DevTools
743// frontend is being loaded.
744IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
745                       TestPauseWhenLoadingDevTools) {
746  RunTest("testPauseWhenLoadingDevTools", kPauseWhenLoadingDevTools);
747}
748
749// Tests that pressing 'Pause' will pause script execution if the script
750// is already running.
751#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
752// Timing out on linux ARM bot: https://crbug/238453
753#define MAYBE_TestPauseWhenScriptIsRunning DISABLED_TestPauseWhenScriptIsRunning
754#else
755#define MAYBE_TestPauseWhenScriptIsRunning TestPauseWhenScriptIsRunning
756#endif
757IN_PROC_BROWSER_TEST_F(DevToolsSanityTest,
758                       MAYBE_TestPauseWhenScriptIsRunning) {
759  RunTest("testPauseWhenScriptIsRunning", kPauseWhenScriptIsRunning);
760}
761
762// Tests network timing.
763IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkTiming) {
764  RunTest("testNetworkTiming", kSlowTestPage);
765}
766
767// Tests network size.
768IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSize) {
769  RunTest("testNetworkSize", kChunkedTestPage);
770}
771
772// Tests raw headers text.
773IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkSyncSize) {
774  RunTest("testNetworkSyncSize", kChunkedTestPage);
775}
776
777// Tests raw headers text.
778IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestNetworkRawHeadersText) {
779  RunTest("testNetworkRawHeadersText", kChunkedTestPage);
780}
781
782// Tests that console messages are not duplicated on navigation back.
783#if defined(OS_WIN)
784// Flaking on windows swarm try runs: crbug.com/409285.
785#define MAYBE_TestConsoleOnNavigateBack DISABLED_TestConsoleOnNavigateBack
786#else
787#define MAYBE_TestConsoleOnNavigateBack TestConsoleOnNavigateBack
788#endif
789IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, MAYBE_TestConsoleOnNavigateBack) {
790  RunTest("testConsoleOnNavigateBack", kNavigateBackTestPage);
791}
792
793// https://crbug.com/397889
794IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, DISABLED_TestDeviceEmulation) {
795  RunTest("testDeviceMetricsOverrides", "about:blank");
796}
797
798
799// Tests that external navigation from inspector page is always handled by
800// DevToolsWindow and results in inspected page navigation.
801IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestDevToolsExternalNavigation) {
802  OpenDevToolsWindow(kDebuggerTestPage, true);
803  GURL url = test_server()->GetURL(kNavigateBackTestPage);
804  ui_test_utils::UrlLoadObserver observer(url,
805      content::NotificationService::AllSources());
806  ASSERT_TRUE(content::ExecuteScript(
807      main_web_contents(),
808      std::string("window.location = \"") + url.spec() + "\""));
809  observer.Wait();
810
811  ASSERT_TRUE(main_web_contents()->GetURL().
812                  SchemeIs(content::kChromeDevToolsScheme));
813  ASSERT_EQ(url, GetInspectedTab()->GetURL());
814  CloseDevToolsWindow();
815}
816
817// Tests that toolbox window is loaded when DevTools window is undocked.
818IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestToolboxLoadedUndocked) {
819  OpenDevToolsWindow(kDebuggerTestPage, false);
820  ASSERT_TRUE(toolbox_web_contents());
821  DevToolsWindow* on_self =
822      DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
823  ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
824  DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
825  CloseDevToolsWindow();
826}
827
828// Tests that toolbox window is not loaded when DevTools window is docked.
829IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestToolboxNotLoadedDocked) {
830  OpenDevToolsWindow(kDebuggerTestPage, true);
831  ASSERT_FALSE(toolbox_web_contents());
832  DevToolsWindow* on_self =
833      DevToolsWindowTesting::OpenDevToolsWindowSync(main_web_contents(), false);
834  ASSERT_FALSE(DevToolsWindowTesting::Get(on_self)->toolbox_web_contents());
835  DevToolsWindowTesting::CloseDevToolsWindowSync(on_self);
836  CloseDevToolsWindow();
837}
838
839// Tests that inspector will reattach to inspected page when it is reloaded
840// after a crash. See http://crbug.com/101952
841IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestReattachAfterCrash) {
842  RunTest("testReattachAfterCrash", std::string());
843}
844
845IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestPageWithNoJavaScript) {
846  OpenDevToolsWindow("about:blank", false);
847  std::string result;
848  ASSERT_TRUE(
849      content::ExecuteScriptAndExtractString(
850          main_web_contents()->GetRenderViewHost(),
851          "window.domAutomationController.send("
852          "    '' + (window.uiTests && (typeof uiTests.runTest)));",
853          &result));
854  ASSERT_EQ("function", result) << "DevTools front-end is broken.";
855  CloseDevToolsWindow();
856}
857
858// Flakily fails: http://crbug.com/403007 http://crbug.com/89845
859IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest, DISABLED_InspectSharedWorker) {
860#if defined(OS_WIN) && defined(USE_ASH)
861  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
862  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
863    return;
864#endif
865
866  RunTest("testSharedWorker", kSharedWorkerTestPage);
867}
868
869// http://crbug.com/100538
870IN_PROC_BROWSER_TEST_F(WorkerDevToolsSanityTest,
871                       DISABLED_PauseInSharedWorkerInitialization) {
872  ASSERT_TRUE(test_server()->Start());
873  GURL url = test_server()->GetURL(kReloadSharedWorkerTestPage);
874  ui_test_utils::NavigateToURL(browser(), url);
875
876  scoped_refptr<WorkerData> worker_data = WaitForFirstSharedWorker();
877  OpenDevToolsWindowForSharedWorker(worker_data.get());
878
879  TerminateWorker(worker_data);
880
881  // Reload page to restart the worker.
882  ui_test_utils::NavigateToURL(browser(), url);
883
884  // Wait until worker script is paused on the debugger statement.
885  RunTestFunction(window_, "testPauseInSharedWorkerInitialization");
886  CloseDevToolsWindow();
887}
888
889class DevToolsAgentHostTest : public InProcessBrowserTest {};
890
891// Tests DevToolsAgentHost retention by its target.
892IN_PROC_BROWSER_TEST_F(DevToolsAgentHostTest, TestAgentHostReleased) {
893  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
894  WebContents* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
895  DevToolsAgentHost* agent_raw =
896      DevToolsAgentHost::GetOrCreateFor(web_contents).get();
897  const std::string agent_id = agent_raw->GetId();
898  ASSERT_EQ(agent_raw, DevToolsAgentHost::GetForId(agent_id).get())
899      << "DevToolsAgentHost cannot be found by id";
900  browser()->tab_strip_model()->
901      CloseWebContentsAt(0, TabStripModel::CLOSE_NONE);
902  ASSERT_FALSE(DevToolsAgentHost::GetForId(agent_id).get())
903      << "DevToolsAgentHost is not released when the tab is closed";
904}
905
906class RemoteDebuggingTest: public ExtensionApiTest {
907  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
908    ExtensionApiTest::SetUpCommandLine(command_line);
909    command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort, "9222");
910
911    // Override the extension root path.
912    PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
913    test_data_dir_ = test_data_dir_.AppendASCII("devtools");
914  }
915};
916
917IN_PROC_BROWSER_TEST_F(RemoteDebuggingTest, RemoteDebugger) {
918#if defined(OS_WIN) && defined(USE_ASH)
919  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
920  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
921    return;
922#endif
923
924  ASSERT_TRUE(RunExtensionTest("target_list")) << message_;
925}
926