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/strings/stringprintf.h" 7 #include "content/public/browser/web_contents.h" 8 #include "content/public/browser/web_contents_delegate.h" 9 #include "content/public/common/content_switches.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: 27 CloseTrackingDelegate() : close_contents_called_(false) {} 28 29 bool close_contents_called() const { return close_contents_called_; } 30 31 virtual 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: 45 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 46 // Use --site-per-process to force process swaps on cross-site navigations. 47 command_line->AppendSwitch(switches::kSitePerProcess); 48 } 49 50 bool AttemptCloseFromJavaScript(WebContents* web_contents) { 51 CloseTrackingDelegate close_tracking_delegate; 52 WebContentsDelegate* old_delegate = web_contents->GetDelegate(); 53 web_contents->SetDelegate(&close_tracking_delegate); 54 55 const char kCloseWindowScript[] = 56 // Close the window. 57 "window.close();" 58 // Report back after an event loop iteration; the close IPC isn't sent 59 // immediately. 60 "setTimeout(function() {" 61 "window.domAutomationController.send(0);" 62 "});"; 63 int dummy; 64 CHECK(ExecuteScriptAndExtractInt(web_contents, kCloseWindowScript, &dummy)); 65 66 web_contents->SetDelegate(old_delegate); 67 return close_tracking_delegate.close_contents_called(); 68 } 69 70 Shell* OpenWindowFromJavaScript(Shell* shell, const GURL& url) { 71 // Wait for the popup to be created and for it to have navigated. 72 ShellAddedObserver new_shell_observer; 73 TestNavigationObserver nav_observer(NULL); 74 nav_observer.StartWatchingNewWebContents(); 75 CHECK(ExecuteScript( 76 shell->web_contents(), 77 base::StringPrintf("window.open('%s')", url.spec().c_str()))); 78 nav_observer.Wait(); 79 return new_shell_observer.GetShell(); 80 } 81 }; 82 83 // Tests that window.close() does not work on a normal window that has navigated 84 // a few times. 85 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, NormalWindow) { 86 ASSERT_TRUE(test_server()->Start()); 87 88 // window.close is allowed if the window was opened by DOM OR the back/forward 89 // list has only one element. Navigate a bit so the second condition is false. 90 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); 91 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); 92 NavigateToURL(shell(), url1); 93 NavigateToURL(shell(), url2); 94 95 // This window was not opened by DOM, so close does not reach the browser 96 // process. 97 EXPECT_FALSE(AttemptCloseFromJavaScript(shell()->web_contents())); 98 } 99 100 // Tests that window.close() works in a popup window that has navigated a few 101 // times. 102 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, Popup) { 103 ASSERT_TRUE(test_server()->Start()); 104 105 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); 106 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); 107 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); 108 NavigateToURL(shell(), url1); 109 110 Shell* popup = OpenWindowFromJavaScript(shell(), url2); 111 NavigateToURL(popup, url3); 112 EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents())); 113 } 114 115 // Tests that window.close() works in a popup window that has navigated a few 116 // times and swapped processes. 117 IN_PROC_BROWSER_TEST_F(OpenedByDOMTest, CrossProcessPopup) { 118 host_resolver()->AddRule("*", "127.0.0.1"); 119 ASSERT_TRUE(test_server()->Start()); 120 121 GURL url1 = test_server()->GetURL("files/site_isolation/blank.html?1"); 122 123 GURL url2 = test_server()->GetURL("files/site_isolation/blank.html?2"); 124 GURL::Replacements replace_host; 125 std::string foo_com("foo.com"); 126 replace_host.SetHostStr(foo_com); 127 url2 = url2.ReplaceComponents(replace_host); 128 129 GURL url3 = test_server()->GetURL("files/site_isolation/blank.html?3"); 130 url3 = url3.ReplaceComponents(replace_host); 131 132 NavigateToURL(shell(), url1); 133 134 Shell* popup = OpenWindowFromJavaScript(shell(), url2); 135 NavigateToURL(popup, url3); 136 EXPECT_TRUE(AttemptCloseFromJavaScript(popup->web_contents())); 137 } 138 139 } // namespace content 140