blob: d0dc12fa10b26c4a1172d43e53c4cc740714e066 [file] [log] [blame]
[email protected]71b73f02011-04-06 15:57:291// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]3a8eecb2010-04-22 23:56:302// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]ce7f62e32010-08-10 23:43:595#include "base/utf_string_conversions.h"
[email protected]3a8eecb2010-04-22 23:56:306#include "chrome/browser/extensions/extension_apitest.h"
7#include "chrome/browser/extensions/extension_host.h"
8#include "chrome/browser/extensions/extension_process_manager.h"
[email protected]8ecad5e2010-12-02 21:18:339#include "chrome/browser/profiles/profile.h"
[email protected]7b5dc002010-11-16 23:08:1010#include "chrome/browser/ui/browser.h"
[email protected]71b73f02011-04-06 15:57:2911#include "chrome/browser/ui/browser_list.h"
[email protected]3a8eecb2010-04-22 23:56:3012#include "chrome/common/chrome_switches.h"
[email protected]af44e7fb2011-07-29 18:32:3213#include "chrome/test/base/ui_test_utils.h"
[email protected]a035dfda2011-03-02 01:01:4914#include "content/browser/renderer_host/render_view_host.h"
15#include "content/browser/tab_contents/tab_contents.h"
[email protected]3a8eecb2010-04-22 23:56:3016#include "net/base/mock_host_resolver.h"
17
18class AppApiTest : public ExtensionApiTest {
[email protected]3a8eecb2010-04-22 23:56:3019};
20
21// Simulates a page calling window.open on an URL, and waits for the navigation.
22static void WindowOpenHelper(Browser* browser,
[email protected]12ea9b272010-08-24 11:31:4023 RenderViewHost* opener_host,
24 const GURL& url,
25 bool newtab_process_should_equal_opener) {
[email protected]9fabbf72010-09-30 21:50:0526 ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
27 opener_host, L"", L"window.open('" + UTF8ToWide(url.spec()) + L"');"));
[email protected]3a8eecb2010-04-22 23:56:3028
[email protected]12ea9b272010-08-24 11:31:4029 // The above window.open call is not user-initiated, it will create
30 // a popup window instead of a new tab in current window.
31 // Now the active tab in last active window should be the new tab.
32 Browser* last_active_browser = BrowserList::GetLastActive();
33 EXPECT_TRUE(last_active_browser);
34 TabContents* newtab = last_active_browser->GetSelectedTabContents();
35 EXPECT_TRUE(newtab);
[email protected]3a8eecb2010-04-22 23:56:3036 if (!newtab->controller().GetLastCommittedEntry() ||
37 newtab->controller().GetLastCommittedEntry()->url() != url)
38 ui_test_utils::WaitForNavigation(&newtab->controller());
39 EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url());
[email protected]12ea9b272010-08-24 11:31:4040 if (newtab_process_should_equal_opener)
41 EXPECT_EQ(opener_host->process(), newtab->render_view_host()->process());
42 else
43 EXPECT_NE(opener_host->process(), newtab->render_view_host()->process());
[email protected]3a8eecb2010-04-22 23:56:3044}
45
46// Simulates a page navigating itself to an URL, and waits for the navigation.
47static void NavigateTabHelper(TabContents* contents, const GURL& url) {
48 bool result = false;
[email protected]9fabbf72010-09-30 21:50:0549 ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
[email protected]3a8eecb2010-04-22 23:56:3050 contents->render_view_host(), L"",
[email protected]d38f83f2010-04-30 23:25:5751 L"window.addEventListener('unload', function() {"
52 L" window.domAutomationController.send(true);"
53 L"}, false);"
54 L"window.location = '" + UTF8ToWide(url.spec()) + L"';",
[email protected]9fabbf72010-09-30 21:50:0555 &result));
[email protected]3a8eecb2010-04-22 23:56:3056 ASSERT_TRUE(result);
57
[email protected]d38f83f2010-04-30 23:25:5758 if (!contents->controller().GetLastCommittedEntry() ||
[email protected]3a8eecb2010-04-22 23:56:3059 contents->controller().GetLastCommittedEntry()->url() != url)
60 ui_test_utils::WaitForNavigation(&contents->controller());
61 EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url());
62}
63
[email protected]d54ade62011-07-03 00:32:1364#if defined(OS_WIN)
65// AppProcess sometimes hangs on Windows
66// http://crbug.com/88316
67#define MAYBE_AppProcess DISABLED_AppProcess
68#else
69#define MAYBE_AppProcess AppProcess
70#endif
71
72IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcess) {
[email protected]12ea9b272010-08-24 11:31:4073 CommandLine::ForCurrentProcess()->AppendSwitch(
74 switches::kDisablePopupBlocking);
75
[email protected]3a8eecb2010-04-22 23:56:3076 host_resolver()->AddRule("*", "127.0.0.1");
[email protected]95409e12010-08-17 20:07:1177 ASSERT_TRUE(test_server()->Start());
[email protected]3a8eecb2010-04-22 23:56:3078
[email protected]cbf4d1912010-08-12 18:24:5779 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
[email protected]3a8eecb2010-04-22 23:56:3080
[email protected]cbf4d1912010-08-12 18:24:5781 // Open two tabs in the app, one outside it.
[email protected]fe3048872010-10-18 14:58:5982 GURL base_url = test_server()->GetURL(
83 "files/extensions/api_test/app_process/");
84
85 // The app under test acts on URLs whose host is "localhost",
86 // so the URLs we navigate to must have host "localhost".
87 GURL::Replacements replace_host;
[email protected]6101c342010-12-16 22:44:3788 std::string host_str("localhost"); // must stay in scope with replace_host
89 replace_host.SetHostStr(host_str);
[email protected]fe3048872010-10-18 14:58:5990 base_url = base_url.ReplaceComponents(replace_host);
91
[email protected]f0e13332011-05-20 22:41:1492 // Test both opening a URL in a new tab, and opening a tab and then navigating
93 // it. Either way, app tabs should be considered extension processes, but
94 // they have no elevated privileges and thus should not have WebUI bindings.
95 ui_test_utils::NavigateToURLWithDisposition(
96 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
97 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
98 EXPECT_TRUE(browser()->GetTabContentsAt(1)->render_view_host()->process()->
99 is_extension_process());
100 EXPECT_FALSE(browser()->GetTabContentsAt(1)->web_ui());
[email protected]cbf4d1912010-08-12 18:24:57101 browser()->NewTab();
102 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
[email protected]f0e13332011-05-20 22:41:14103 EXPECT_TRUE(browser()->GetTabContentsAt(2)->render_view_host()->process()->
104 is_extension_process());
105 EXPECT_FALSE(browser()->GetTabContentsAt(2)->web_ui());
[email protected]cbf4d1912010-08-12 18:24:57106 browser()->NewTab();
107 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));
[email protected]f0e13332011-05-20 22:41:14108 EXPECT_FALSE(browser()->GetTabContentsAt(3)->render_view_host()->process()->
109 is_extension_process());
110 EXPECT_FALSE(browser()->GetTabContentsAt(3)->web_ui());
[email protected]3a8eecb2010-04-22 23:56:30111
[email protected]056ad2a2011-07-12 02:13:55112 // We should have opened 3 new extension tabs. Including the original blank
113 // tab, we now have 4 tabs. Because the app_process app has the background
114 // permission, all of its instances are in the same process. Thus two tabs
115 // should be part of the extension app and grouped in the same process.
[email protected]3a8eecb2010-04-22 23:56:30116 ASSERT_EQ(4, browser()->tab_count());
[email protected]cbf4d1912010-08-12 18:24:57117 RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
[email protected]cbf4d1912010-08-12 18:24:57118
119 EXPECT_EQ(host->process(),
[email protected]3a8eecb2010-04-22 23:56:30120 browser()->GetTabContentsAt(2)->render_view_host()->process());
[email protected]cbf4d1912010-08-12 18:24:57121 EXPECT_NE(host->process(),
[email protected]3a8eecb2010-04-22 23:56:30122 browser()->GetTabContentsAt(3)->render_view_host()->process());
123
124 // Now let's do the same using window.open. The same should happen.
[email protected]12ea9b272010-08-24 11:31:40125 ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
[email protected]cbf4d1912010-08-12 18:24:57126 WindowOpenHelper(browser(), host,
[email protected]12ea9b272010-08-24 11:31:40127 base_url.Resolve("path1/empty.html"), true);
[email protected]cbf4d1912010-08-12 18:24:57128 WindowOpenHelper(browser(), host,
[email protected]12ea9b272010-08-24 11:31:40129 base_url.Resolve("path2/empty.html"), true);
[email protected]992db4c2011-05-12 15:37:15130 // This should open in a new process (i.e., false for the last argument).
[email protected]cbf4d1912010-08-12 18:24:57131 WindowOpenHelper(browser(), host,
[email protected]992db4c2011-05-12 15:37:15132 base_url.Resolve("path3/empty.html"), false);
[email protected]3a8eecb2010-04-22 23:56:30133
134 // Now let's have these pages navigate, into or out of the extension web
135 // extent. They should switch processes.
[email protected]9a1e6d42010-04-26 22:29:36136 const GURL& app_url(base_url.Resolve("path1/empty.html"));
137 const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
[email protected]cbf4d1912010-08-12 18:24:57138 NavigateTabHelper(browser()->GetTabContentsAt(2), non_app_url);
[email protected]3a8eecb2010-04-22 23:56:30139 NavigateTabHelper(browser()->GetTabContentsAt(3), app_url);
[email protected]992db4c2011-05-12 15:37:15140 EXPECT_NE(host->process(),
[email protected]cbf4d1912010-08-12 18:24:57141 browser()->GetTabContentsAt(2)->render_view_host()->process());
142 EXPECT_EQ(host->process(),
[email protected]3a8eecb2010-04-22 23:56:30143 browser()->GetTabContentsAt(3)->render_view_host()->process());
[email protected]08e94b82010-12-15 22:51:04144
145 // If one of the popup tabs navigates back to the app, window.opener should
146 // be valid.
147 NavigateTabHelper(browser()->GetTabContentsAt(6), app_url);
148 EXPECT_EQ(host->process(),
149 browser()->GetTabContentsAt(6)->render_view_host()->process());
150 bool windowOpenerValid = false;
151 ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
152 browser()->GetTabContentsAt(6)->render_view_host(), L"",
153 L"window.domAutomationController.send(window.opener != null)",
154 &windowOpenerValid));
155 ASSERT_TRUE(windowOpenerValid);
[email protected]3a8eecb2010-04-22 23:56:30156}
[email protected]faf407b2011-01-05 01:24:32157
[email protected]727db1f2011-07-19 19:35:02158
159#if defined(OS_WIN)
160// Seems to timeout sometimes on Windows: http://crbug.com/89766
161#define MAYBE_AppProcessInstances FLAKY_AppProcessInstances
162#else
163#define MAYBE_AppProcessInstances AppProcessInstances
164#endif
165
[email protected]056ad2a2011-07-12 02:13:55166// Test that hosted apps without the background permission use a process per app
167// instance model, such that separate instances are in separate processes.
[email protected]727db1f2011-07-19 19:35:02168IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessInstances) {
[email protected]056ad2a2011-07-12 02:13:55169 CommandLine::ForCurrentProcess()->AppendSwitch(
170 switches::kDisablePopupBlocking);
171
172 host_resolver()->AddRule("*", "127.0.0.1");
173 ASSERT_TRUE(test_server()->Start());
174
175 ASSERT_TRUE(LoadExtension(
176 test_data_dir_.AppendASCII("app_process_instances")));
177
178 // Open two tabs in the app, one outside it.
179 GURL base_url = test_server()->GetURL(
180 "files/extensions/api_test/app_process_instances/");
181
182 // The app under test acts on URLs whose host is "localhost",
183 // so the URLs we navigate to must have host "localhost".
184 GURL::Replacements replace_host;
185 std::string host_str("localhost"); // must stay in scope with replace_host
186 replace_host.SetHostStr(host_str);
187 base_url = base_url.ReplaceComponents(replace_host);
188
189 // Test both opening a URL in a new tab, and opening a tab and then navigating
190 // it. Either way, app tabs should be considered extension processes, but
191 // they have no elevated privileges and thus should not have WebUI bindings.
192 ui_test_utils::NavigateToURLWithDisposition(
193 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
194 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
195 EXPECT_TRUE(browser()->GetTabContentsAt(1)->render_view_host()->process()->
196 is_extension_process());
197 EXPECT_FALSE(browser()->GetTabContentsAt(1)->web_ui());
198 browser()->NewTab();
199 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
200 EXPECT_TRUE(browser()->GetTabContentsAt(2)->render_view_host()->process()->
201 is_extension_process());
202 EXPECT_FALSE(browser()->GetTabContentsAt(2)->web_ui());
203
204 // We should have opened 2 new extension tabs. Including the original blank
205 // tab, we now have 3 tabs. The two app tabs should not be in the same
206 // process, since they do not have the background permission. (Thus, we want
207 // to separate them to improve responsiveness.)
208 ASSERT_EQ(3, browser()->tab_count());
209 RenderViewHost* host1 = browser()->GetTabContentsAt(1)->render_view_host();
210 RenderViewHost* host2 = browser()->GetTabContentsAt(2)->render_view_host();
211 EXPECT_NE(host1->process(), host2->process());
212
213 // Opening tabs with window.open should keep the page in the opener's process.
214 ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
215 WindowOpenHelper(browser(), host1,
216 base_url.Resolve("path1/empty.html"), true);
217 WindowOpenHelper(browser(), host2,
218 base_url.Resolve("path2/empty.html"), true);
219}
220
[email protected]faf407b2011-01-05 01:24:32221// Tests that app process switching works properly in the following scenario:
222// 1. navigate to a page1 in the app
223// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
224// 3. page2 redirects back to a page in the app
225// The final navigation should end up in the app process.
226// See http://crbug.com/61757
227IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) {
228 CommandLine::ForCurrentProcess()->AppendSwitch(
229 switches::kDisablePopupBlocking);
230
231 host_resolver()->AddRule("*", "127.0.0.1");
232 ASSERT_TRUE(test_server()->Start());
233
234 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
235
236 // Open two tabs in the app.
237 GURL base_url = test_server()->GetURL(
238 "files/extensions/api_test/app_process/");
239
240 // The app under test acts on URLs whose host is "localhost",
241 // so the URLs we navigate to must have host "localhost".
242 GURL::Replacements replace_host;
243 std::string host_str("localhost"); // must stay in scope with replace_host
244 replace_host.SetHostStr(host_str);
245 base_url = base_url.ReplaceComponents(replace_host);
246
247 browser()->NewTab();
248 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
249 browser()->NewTab();
[email protected]faf407b2011-01-05 01:24:32250 // Wait until the second tab finishes its redirect train (2 hops).
[email protected]089e8c332011-01-06 21:37:29251 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
252 browser(), base_url.Resolve("path1/redirect.html"), 2);
[email protected]faf407b2011-01-05 01:24:32253
254 // 3 tabs, including the initial about:blank. The last 2 should be the same
255 // process.
256 ASSERT_EQ(3, browser()->tab_count());
[email protected]089e8c332011-01-06 21:37:29257 EXPECT_EQ("/files/extensions/api_test/app_process/path1/empty.html",
258 browser()->GetTabContentsAt(2)->controller().
259 GetLastCommittedEntry()->url().path());
[email protected]faf407b2011-01-05 01:24:32260 RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
261 EXPECT_EQ(host->process(),
262 browser()->GetTabContentsAt(2)->render_view_host()->process());
263}
[email protected]d292d8a2011-05-25 03:47:11264
265// Ensure that reloading a URL after installing or uninstalling it as an app
266// correctly swaps the process. (http://crbug.com/80621)
267IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
268 CommandLine::ForCurrentProcess()->AppendSwitch(
269 switches::kDisablePopupBlocking);
270
271 host_resolver()->AddRule("*", "127.0.0.1");
272 ASSERT_TRUE(test_server()->Start());
273
274 // The app under test acts on URLs whose host is "localhost",
275 // so the URLs we navigate to must have host "localhost".
276 GURL::Replacements replace_host;
277 std::string host_str("localhost"); // must stay in scope with replace_host
278 replace_host.SetHostStr(host_str);
279 GURL base_url = test_server()->GetURL(
280 "files/extensions/api_test/app_process/");
281 base_url = base_url.ReplaceComponents(replace_host);
282
283 // Load an app URL before loading the app.
284 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
285 TabContents* contents = browser()->GetTabContentsAt(0);
286 EXPECT_FALSE(contents->render_view_host()->process()->is_extension_process());
287
288 // Load app and reload page.
289 const Extension* app =
290 LoadExtension(test_data_dir_.AppendASCII("app_process"));
291 ASSERT_TRUE(app);
292 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
293 EXPECT_TRUE(contents->render_view_host()->process()->is_extension_process());
294
295 // Disable app and reload page.
296 DisableExtension(app->id());
297 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
298 EXPECT_FALSE(contents->render_view_host()->process()->is_extension_process());
299
300 // Enable app and reload via JavaScript.
301 EnableExtension(app->id());
302 ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(contents->render_view_host(),
303 L"", L"location.reload();"));
304 ui_test_utils::WaitForNavigation(&contents->controller());
305 EXPECT_TRUE(contents->render_view_host()->process()->is_extension_process());
306
307 // Disable app and reload via JavaScript.
308 DisableExtension(app->id());
309 ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(contents->render_view_host(),
310 L"", L"location.reload();"));
311 ui_test_utils::WaitForNavigation(&contents->controller());
312 EXPECT_FALSE(contents->render_view_host()->process()->is_extension_process());
313}