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