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