1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 53345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_apitest.h" 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_host.h" 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_process_manager.h" 921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h" 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/test/ui_test_utils.h" 14dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host.h" 15dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/mock_host_resolver.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass AppApiTest : public ExtensionApiTest { 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Simulates a page calling window.open on an URL, and waits for the navigation. 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void WindowOpenHelper(Browser* browser, 233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick RenderViewHost* opener_host, 243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const GURL& url, 253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick bool newtab_process_should_equal_opener) { 26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ASSERT_TRUE(ui_test_utils::ExecuteJavaScript( 27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick opener_host, L"", L"window.open('" + UTF8ToWide(url.spec()) + L"');")); 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // The above window.open call is not user-initiated, it will create 303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // a popup window instead of a new tab in current window. 313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Now the active tab in last active window should be the new tab. 323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick Browser* last_active_browser = BrowserList::GetLastActive(); 333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_TRUE(last_active_browser); 343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick TabContents* newtab = last_active_browser->GetSelectedTabContents(); 353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_TRUE(newtab); 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!newtab->controller().GetLastCommittedEntry() || 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch newtab->controller().GetLastCommittedEntry()->url() != url) 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ui_test_utils::WaitForNavigation(&newtab->controller()); 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url()); 403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (newtab_process_should_equal_opener) 413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_EQ(opener_host->process(), newtab->render_view_host()->process()); 423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick else 433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_NE(opener_host->process(), newtab->render_view_host()->process()); 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Simulates a page navigating itself to an URL, and waits for the navigation. 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void NavigateTabHelper(TabContents* contents, const GURL& url) { 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool result = false; 49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch contents->render_view_host(), L"", 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"window.addEventListener('unload', function() {" 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L" window.domAutomationController.send(true);" 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"}, false);" 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"window.location = '" + UTF8ToWide(url.spec()) + L"';", 55731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &result)); 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ASSERT_TRUE(result); 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!contents->controller().GetLastCommittedEntry() || 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch contents->controller().GetLastCommittedEntry()->url() != url) 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ui_test_utils::WaitForNavigation(&contents->controller()); 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url()); 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 643f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenIN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) { 653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick CommandLine::ForCurrentProcess()->AppendSwitch( 663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick switches::kDisablePopupBlocking); 673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_resolver()->AddRule("*", "127.0.0.1"); 693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ASSERT_TRUE(test_server()->Start()); 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Open two tabs in the app, one outside it. 74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick GURL base_url = test_server()->GetURL( 75731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "files/extensions/api_test/app_process/"); 76731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 77731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // The app under test acts on URLs whose host is "localhost", 78731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick // so the URLs we navigate to must have host "localhost". 79731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick GURL::Replacements replace_host; 8021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen std::string host_str("localhost"); // must stay in scope with replace_host 8121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen replace_host.SetHostStr(host_str); 82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick base_url = base_url.ReplaceComponents(replace_host); 83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser()->NewTab(); 853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser()->NewTab(); 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html")); 883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser()->NewTab(); 893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html")); 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The extension should have opened 3 new tabs. Including the original blank 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // tab, we now have 4 tabs. Two should be part of the extension app, and 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // grouped in the same process. 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ASSERT_EQ(4, browser()->tab_count()); 953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host(); 963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_EQ(host->process(), 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser()->GetTabContentsAt(2)->render_view_host()->process()); 993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_NE(host->process(), 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser()->GetTabContentsAt(3)->render_view_host()->process()); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Now let's do the same using window.open. The same should happen. 1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile())); 1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick WindowOpenHelper(browser(), host, 1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick base_url.Resolve("path1/empty.html"), true); 1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick WindowOpenHelper(browser(), host, 1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick base_url.Resolve("path2/empty.html"), true); 108201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // TODO(creis): This should open in a new process (i.e., false for the last 109201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // argument), but we temporarily avoid swapping processes away from an app 110201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // until we're able to restore window.opener if the page later returns to an 111201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // in-app URL. See crbug.com/65953. 1123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick WindowOpenHelper(browser(), host, 113201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch base_url.Resolve("path3/empty.html"), true); 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Now let's have these pages navigate, into or out of the extension web 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // extent. They should switch processes. 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& app_url(base_url.Resolve("path1/empty.html")); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const GURL& non_app_url(base_url.Resolve("path3/empty.html")); 1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick NavigateTabHelper(browser()->GetTabContentsAt(2), non_app_url); 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NavigateTabHelper(browser()->GetTabContentsAt(3), app_url); 121201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // TODO(creis): This should swap out of the app's process (i.e., EXPECT_NE), 122201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // but we temporarily avoid swapping away from an app in case it needs to 123201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // communicate with window.opener later. See crbug.com/65953. 124201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch EXPECT_EQ(host->process(), 1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick browser()->GetTabContentsAt(2)->render_view_host()->process()); 1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick EXPECT_EQ(host->process(), 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch browser()->GetTabContentsAt(3)->render_view_host()->process()); 128201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 129201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // If one of the popup tabs navigates back to the app, window.opener should 130201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // be valid. 131201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch NavigateTabHelper(browser()->GetTabContentsAt(6), app_url); 132201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch EXPECT_EQ(host->process(), 133201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch browser()->GetTabContentsAt(6)->render_view_host()->process()); 134201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch bool windowOpenerValid = false; 135201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool( 136201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch browser()->GetTabContentsAt(6)->render_view_host(), L"", 137201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch L"window.domAutomationController.send(window.opener != null)", 138201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch &windowOpenerValid)); 139201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch ASSERT_TRUE(windowOpenerValid); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 1413f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1423f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// Tests that app process switching works properly in the following scenario: 1433f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// 1. navigate to a page1 in the app 1443f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect") 1453f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// 3. page2 redirects back to a page in the app 1463f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// The final navigation should end up in the app process. 1473f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen// See http://crbug.com/61757 1483f50c38dc070f4bb515c1b64450dae14f316474eKristian MonsenIN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) { 1493f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen CommandLine::ForCurrentProcess()->AppendSwitch( 1503f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen switches::kDisablePopupBlocking); 1513f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1523f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen host_resolver()->AddRule("*", "127.0.0.1"); 1533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen ASSERT_TRUE(test_server()->Start()); 1543f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1553f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 1563f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1573f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // Open two tabs in the app. 1583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen GURL base_url = test_server()->GetURL( 1593f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen "files/extensions/api_test/app_process/"); 1603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1613f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // The app under test acts on URLs whose host is "localhost", 1623f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // so the URLs we navigate to must have host "localhost". 1633f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen GURL::Replacements replace_host; 1643f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen std::string host_str("localhost"); // must stay in scope with replace_host 1653f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen replace_host.SetHostStr(host_str); 1663f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen base_url = base_url.ReplaceComponents(replace_host); 1673f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1683f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen browser()->NewTab(); 1693f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 1703f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen browser()->NewTab(); 1713f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // Wait until the second tab finishes its redirect train (2 hops). 1723f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( 1733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen browser(), base_url.Resolve("path1/redirect.html"), 2); 1743f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 1753f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // 3 tabs, including the initial about:blank. The last 2 should be the same 1763f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen // process. 1773f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen ASSERT_EQ(3, browser()->tab_count()); 1783f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen EXPECT_EQ("/files/extensions/api_test/app_process/path1/empty.html", 1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen browser()->GetTabContentsAt(2)->controller(). 1803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen GetLastCommittedEntry()->url().path()); 1813f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host(); 1823f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen EXPECT_EQ(host->process(), 1833f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen browser()->GetTabContentsAt(2)->render_view_host()->process()); 1843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen} 185