1 // Copyright 2014 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/macros.h"
7 #include "base/strings/stringprintf.h"
8 #include "content/public/browser/web_contents.h"
9 #include "content/public/browser/web_contents_delegate.h"
10 #include "content/public/test/browser_test.h"
11 #include "content/public/test/browser_test_utils.h"
12 #include "content/public/test/content_browser_test.h"
13 #include "content/public/test/content_browser_test_utils.h"
14 #include "content/public/test/test_navigation_observer.h"
15 #include "content/shell/browser/shell.h"
16 #include "net/dns/mock_host_resolver.h"
17 #include "url/gurl.h"
18
19 namespace content {
20
21 namespace {
22
23 // A dummy WebContentsDelegate which tracks whether CloseContents() has been
24 // called. It refuses the actual close but keeps track of whether the renderer
25 // requested it.
26 class CloseTrackingDelegate : public WebContentsDelegate {
27 public:
CloseTrackingDelegate()28 CloseTrackingDelegate() : close_contents_called_(false) {}
29
close_contents_called() const30 bool close_contents_called() const { return close_contents_called_; }
31
CloseContents(WebContents * source)32 void CloseContents(WebContents* source) override {
33 close_contents_called_ = true;
34 }
35
36 private:
37 bool close_contents_called_;
38
39 DISALLOW_COPY_AND_ASSIGN(CloseTrackingDelegate);
40 };
41
42 } // namespace
43
44 class OpenedByDOMTest : public ContentBrowserTest {
45 protected:
SetUpCommandLine(base::CommandLine * command_line)46 void SetUpCommandLine(base::CommandLine* command_line) override {
47 // Use --site-per-process to force process swaps on cross-site navigations.
48 IsolateAllSitesForTesting(command_line);
49 }
50
SetUpOnMainThread()51 void SetUpOnMainThread() override {
52 host_resolver()->AddRule("*", "127.0.0.1");
53 }
54
AttemptCloseFromJavaScript(WebContents * web_contents)55 bool AttemptCloseFromJavaScript(WebContents* web_contents) {
56 CloseTrackingDelegate close_tracking_delegate;
57 WebContentsDelegate* old_delegate = web_contents->GetDelegate();
58 web_contents->SetDelegate(&close_tracking_delegate);
59
60 const char kCloseWindowScript[] =
61 // Close the window.
62 "window.close();"
63 // Report back after an event loop iteration; the close IPC isn't sent
64 // immediately.
65 "setTimeout(function() {"
66 "window.domAutomationController.send(0);"
67 "});";
68 int dummy;
69 CHECK(ExecuteScriptAndExtractInt(web_contents, kCloseWindowScript, &dummy));
70
71 web_contents->SetDelegate(old_delegate);
72 return close_tracking_delegate.close_contents_called();
73 }
74
OpenWindowFromJavaScript(Shell * shell,const GURL & url)75 Shell* OpenWindowFromJavaScript(Shell* shell, const GURL& url) {
76 // Wait for the popup to be created and for it to have navigated.
77 ShellAddedObserver new_shell_observer;
78 TestNavigationObserver nav_observer(nullptr);
79 nav_observer.StartWatchingNewWebContents();
80 CHECK(ExecuteScript(
81 shell, base::StringPrintf("window.open('%s')", url.spec().c_str())));
82 nav_observer.Wait();
83 return new_shell_observer.GetShell();
84 }
85 };
86
87 // Tests that window.close() does not work on a normal window that has navigated
88 // a few times.
IN_PROC_BROWSER_TEST_F(OpenedByDOMTest,NormalWindow)89 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, NormalWindow) {
90 ASSERT_TRUE(embedded_test_server()->Start());
91
92 // window.close is allowed if the window was opened by DOM OR the back/forward
93 // list has only one element. Navigate a bit so the second condition is false.
94 GURL url1 = embedded_test_server()->GetURL("/site_isolation/blank.html?1");
95 GURL url2 = embedded_test_server()->GetURL("/site_isolation/blank.html?2");
96 EXPECT_TRUE(NavigateToURL(shell(), url1));
97 EXPECT_TRUE(NavigateToURL(shell(), url2));
98
99 // This window was not opened by DOM, so close does not reach the browser
100 // process.
101 EXPECT_FALSE(AttemptCloseFromJavaScript(shell()->web_contents()));
102 }
103
104 // Tests that window.close() works in a popup window that has navigated a few
105 // times.
IN_PROC_BROWSER_TEST_F(OpenedByDOMTest,Popup)106 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, Popup) {
107 ASSERT_TRUE(embedded_test_server()->Start());
108
109 GURL url1 = embedded_test_server()->GetURL("/site_isolation/blank.html?1");
110 GURL url2 = embedded_test_server()->GetURL("/site_isolation/blank.html?2");
111 GURL url3 = embedded_test_server()->GetURL("/site_isolation/blank.html?3");
112 EXPECT_TRUE(NavigateToURL(shell(), url1));
113
114 Shell* popup = OpenWindowFromJavaScript(shell(), url2);
115 EXPECT_TRUE(NavigateToURL(popup, url3));
116 EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents()));
117 }
118
119 // Tests that window.close() works in a popup window that has navigated a few
120 // times and swapped processes.
IN_PROC_BROWSER_TEST_F(OpenedByDOMTest,CrossProcessPopup)121 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, CrossProcessPopup) {
122 ASSERT_TRUE(embedded_test_server()->Start());
123
124 GURL url1 = embedded_test_server()->GetURL("/site_isolation/blank.html?1");
125
126 GURL url2 = embedded_test_server()->GetURL("/site_isolation/blank.html?2");
127 GURL::Replacements replace_host;
128 replace_host.SetHostStr("foo.com");
129 url2 = url2.ReplaceComponents(replace_host);
130
131 GURL url3 = embedded_test_server()->GetURL("/site_isolation/blank.html?3");
132 url3 = url3.ReplaceComponents(replace_host);
133
134 EXPECT_TRUE(NavigateToURL(shell(), url1));
135
136 Shell* popup = OpenWindowFromJavaScript(shell(), url2);
137 EXPECT_TRUE(NavigateToURL(popup, url3));
138 EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents()));
139 }
140
141 } // namespace content
142