1 // Copyright (c) 2012 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 "chrome/browser/chrome_notification_types.h"
7 #include "chrome/browser/extensions/extension_apitest.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_commands.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/browser_list.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/test/base/test_switches.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/navigation_entry.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/render_view_host.h"
24 #include "content/public/browser/site_instance.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/test/browser_test_utils.h"
27 #include "content/public/test/test_navigation_observer.h"
28 #include "extensions/browser/extension_host.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/browser/install_flag.h"
31 #include "extensions/browser/process_map.h"
32 #include "extensions/common/extension.h"
33 #include "extensions/common/file_util.h"
34 #include "extensions/common/switches.h"
35 #include "net/dns/mock_host_resolver.h"
36 #include "net/test/embedded_test_server/embedded_test_server.h"
37 #include "sync/api/string_ordinal.h"
38
39 using content::NavigationController;
40 using content::RenderViewHost;
41 using content::SiteInstance;
42 using content::WebContents;
43 using extensions::Extension;
44
45 class AppApiTest : public ExtensionApiTest {
46 protected:
47 // Gets the base URL for files for a specific test, making sure that it uses
48 // "localhost" as the hostname, since that is what the extent is declared
49 // as in the test apps manifests.
GetTestBaseURL(const std::string & test_directory)50 GURL GetTestBaseURL(const std::string& test_directory) {
51 GURL::Replacements replace_host;
52 std::string host_str("localhost"); // must stay in scope with replace_host
53 replace_host.SetHostStr(host_str);
54 GURL base_url = embedded_test_server()->GetURL(
55 "/extensions/api_test/" + test_directory + "/");
56 return base_url.ReplaceComponents(replace_host);
57 }
58
59 // Pass flags to make testing apps easier.
SetUpCommandLine(CommandLine * command_line)60 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
61 ExtensionApiTest::SetUpCommandLine(command_line);
62 CommandLine::ForCurrentProcess()->AppendSwitch(
63 switches::kDisablePopupBlocking);
64 CommandLine::ForCurrentProcess()->AppendSwitch(
65 extensions::switches::kAllowHTTPBackgroundPage);
66 }
67
68 // Helper function to test that independent tabs of the named app are loaded
69 // into separate processes.
TestAppInstancesHelper(const std::string & app_name)70 void TestAppInstancesHelper(const std::string& app_name) {
71 LOG(INFO) << "Start of test.";
72
73 extensions::ProcessMap* process_map =
74 extensions::ProcessMap::Get(browser()->profile());
75
76 host_resolver()->AddRule("*", "127.0.0.1");
77 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
78
79 ASSERT_TRUE(LoadExtension(
80 test_data_dir_.AppendASCII(app_name)));
81 const Extension* extension = GetSingleLoadedExtension();
82
83 // Open two tabs in the app, one outside it.
84 GURL base_url = GetTestBaseURL(app_name);
85
86 // Test both opening a URL in a new tab, and opening a tab and then
87 // navigating it. Either way, app tabs should be considered extension
88 // processes, but they have no elevated privileges and thus should not
89 // have WebUI bindings.
90 ui_test_utils::NavigateToURLWithDisposition(
91 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
92 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
93 LOG(INFO) << "Nav 1.";
94 EXPECT_TRUE(process_map->Contains(
95 browser()->tab_strip_model()->GetWebContentsAt(1)->
96 GetRenderProcessHost()->GetID()));
97 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
98
99 content::WindowedNotificationObserver tab_added_observer(
100 chrome::NOTIFICATION_TAB_ADDED,
101 content::NotificationService::AllSources());
102 chrome::NewTab(browser());
103 tab_added_observer.Wait();
104 LOG(INFO) << "New tab.";
105 ui_test_utils::NavigateToURL(browser(),
106 base_url.Resolve("path2/empty.html"));
107 LOG(INFO) << "Nav 2.";
108 EXPECT_TRUE(process_map->Contains(
109 browser()->tab_strip_model()->GetWebContentsAt(2)->
110 GetRenderProcessHost()->GetID()));
111 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
112
113 // We should have opened 2 new extension tabs. Including the original blank
114 // tab, we now have 3 tabs. The two app tabs should not be in the same
115 // process, since they do not have the background permission. (Thus, we
116 // want to separate them to improve responsiveness.)
117 ASSERT_EQ(3, browser()->tab_strip_model()->count());
118 WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
119 WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
120 EXPECT_NE(tab1->GetRenderProcessHost(), tab2->GetRenderProcessHost());
121
122 // Opening tabs with window.open should keep the page in the opener's
123 // process.
124 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
125 browser()->host_desktop_type()));
126 OpenWindow(tab1, base_url.Resolve("path1/empty.html"), true, NULL);
127 LOG(INFO) << "WindowOpenHelper 1.";
128 OpenWindow(tab2, base_url.Resolve("path2/empty.html"), true, NULL);
129 LOG(INFO) << "End of test.";
130 UnloadExtension(extension->id());
131 }
132 };
133
134 // Omits the disable-popup-blocking flag so we can cover that case.
135 class BlockedAppApiTest : public AppApiTest {
136 protected:
SetUpCommandLine(CommandLine * command_line)137 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
138 ExtensionApiTest::SetUpCommandLine(command_line);
139 CommandLine::ForCurrentProcess()->AppendSwitch(
140 extensions::switches::kAllowHTTPBackgroundPage);
141 }
142 };
143
144 // Tests that hosted apps with the background permission get a process-per-app
145 // model, since all pages need to be able to script the background page.
146 // http://crbug.com/172750
IN_PROC_BROWSER_TEST_F(AppApiTest,DISABLED_AppProcess)147 IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcess) {
148 LOG(INFO) << "Start of test.";
149
150 extensions::ProcessMap* process_map =
151 extensions::ProcessMap::Get(browser()->profile());
152
153 host_resolver()->AddRule("*", "127.0.0.1");
154 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
155
156 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
157
158 LOG(INFO) << "Loaded extension.";
159
160 // Open two tabs in the app, one outside it.
161 GURL base_url = GetTestBaseURL("app_process");
162
163 // Test both opening a URL in a new tab, and opening a tab and then navigating
164 // it. Either way, app tabs should be considered extension processes, but
165 // they have no elevated privileges and thus should not have WebUI bindings.
166 ui_test_utils::NavigateToURLWithDisposition(
167 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
168 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
169 EXPECT_TRUE(process_map->Contains(
170 browser()->tab_strip_model()->GetWebContentsAt(1)->
171 GetRenderProcessHost()->GetID()));
172 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
173 LOG(INFO) << "Nav 1.";
174
175 ui_test_utils::NavigateToURLWithDisposition(
176 browser(), base_url.Resolve("path2/empty.html"), NEW_FOREGROUND_TAB,
177 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
178 EXPECT_TRUE(process_map->Contains(
179 browser()->tab_strip_model()->GetWebContentsAt(2)->
180 GetRenderProcessHost()->GetID()));
181 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
182 LOG(INFO) << "Nav 2.";
183
184 content::WindowedNotificationObserver tab_added_observer(
185 chrome::NOTIFICATION_TAB_ADDED,
186 content::NotificationService::AllSources());
187 chrome::NewTab(browser());
188 tab_added_observer.Wait();
189 LOG(INFO) << "New tab.";
190 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));
191 LOG(INFO) << "Nav 3.";
192 EXPECT_FALSE(process_map->Contains(
193 browser()->tab_strip_model()->GetWebContentsAt(3)->
194 GetRenderProcessHost()->GetID()));
195 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(3)->GetWebUI());
196
197 // We should have opened 3 new extension tabs. Including the original blank
198 // tab, we now have 4 tabs. Because the app_process app has the background
199 // permission, all of its instances are in the same process. Thus two tabs
200 // should be part of the extension app and grouped in the same process.
201 ASSERT_EQ(4, browser()->tab_strip_model()->count());
202 WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1);
203
204 EXPECT_EQ(tab->GetRenderProcessHost(),
205 browser()->tab_strip_model()->GetWebContentsAt(2)->
206 GetRenderProcessHost());
207 EXPECT_NE(tab->GetRenderProcessHost(),
208 browser()->tab_strip_model()->GetWebContentsAt(3)->
209 GetRenderProcessHost());
210
211 // Now let's do the same using window.open. The same should happen.
212 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
213 browser()->host_desktop_type()));
214 OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL);
215 LOG(INFO) << "WindowOpenHelper 1.";
216 OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL);
217 LOG(INFO) << "WindowOpenHelper 2.";
218 // TODO(creis): This should open in a new process (i.e., false for the last
219 // argument), but we temporarily avoid swapping processes away from a hosted
220 // app if it has an opener, because some OAuth providers make script calls
221 // between non-app popups and non-app iframes in the app process.
222 // See crbug.com/59285.
223 OpenWindow(tab, base_url.Resolve("path3/empty.html"), true, NULL);
224 LOG(INFO) << "WindowOpenHelper 3.";
225
226 // Now let's have these pages navigate, into or out of the extension web
227 // extent. They should switch processes.
228 const GURL& app_url(base_url.Resolve("path1/empty.html"));
229 const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
230 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
231 non_app_url);
232 LOG(INFO) << "NavigateTabHelper 1.";
233 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(3),
234 app_url);
235 LOG(INFO) << "NavigateTabHelper 2.";
236 EXPECT_NE(tab->GetRenderProcessHost(),
237 browser()->tab_strip_model()->GetWebContentsAt(2)->
238 GetRenderProcessHost());
239 EXPECT_EQ(tab->GetRenderProcessHost(),
240 browser()->tab_strip_model()->GetWebContentsAt(3)->
241 GetRenderProcessHost());
242
243 // If one of the popup tabs navigates back to the app, window.opener should
244 // be valid.
245 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(6),
246 app_url);
247 LOG(INFO) << "NavigateTabHelper 3.";
248 EXPECT_EQ(tab->GetRenderProcessHost(),
249 browser()->tab_strip_model()->GetWebContentsAt(6)->
250 GetRenderProcessHost());
251 bool windowOpenerValid = false;
252 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
253 browser()->tab_strip_model()->GetWebContentsAt(6),
254 "window.domAutomationController.send(window.opener != null)",
255 &windowOpenerValid));
256 ASSERT_TRUE(windowOpenerValid);
257
258 LOG(INFO) << "End of test.";
259 }
260
261 // Test that hosted apps without the background permission use a process per app
262 // instance model, such that separate instances are in separate processes.
263 // Flaky on Windows. http://crbug.com/248047
264 #if defined(OS_WIN)
265 #define MAYBE_AppProcessInstances DISABLED_AppProcessInstances
266 #else
267 #define MAYBE_AppProcessInstances AppProcessInstances
268 #endif
IN_PROC_BROWSER_TEST_F(AppApiTest,MAYBE_AppProcessInstances)269 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessInstances) {
270 TestAppInstancesHelper("app_process_instances");
271 }
272
273 // Test that hosted apps with the background permission but that set
274 // allow_js_access to false also use a process per app instance model.
275 // Separate instances should be in separate processes.
276 // Flaky on XP: http://crbug.com/165834
277 #if defined(OS_WIN)
278 #define MAYBE_AppProcessBackgroundInstances \
279 DISABLED_AppProcessBackgroundInstances
280 #else
281 #define MAYBE_AppProcessBackgroundInstances AppProcessBackgroundInstances
282 #endif
IN_PROC_BROWSER_TEST_F(AppApiTest,MAYBE_AppProcessBackgroundInstances)283 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessBackgroundInstances) {
284 TestAppInstancesHelper("app_process_background_instances");
285 }
286
287 // Tests that bookmark apps do not use the app process model and are treated
288 // like normal web pages instead. http://crbug.com/104636.
289 // Timing out on Windows. http://crbug.com/238777
290 #if defined(OS_WIN)
291 #define MAYBE_BookmarkAppGetsNormalProcess DISABLED_BookmarkAppGetsNormalProcess
292 #else
293 #define MAYBE_BookmarkAppGetsNormalProcess BookmarkAppGetsNormalProcess
294 #endif
IN_PROC_BROWSER_TEST_F(AppApiTest,MAYBE_BookmarkAppGetsNormalProcess)295 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_BookmarkAppGetsNormalProcess) {
296 ExtensionService* service = extensions::ExtensionSystem::Get(
297 browser()->profile())->extension_service();
298 extensions::ProcessMap* process_map =
299 extensions::ProcessMap::Get(browser()->profile());
300
301 host_resolver()->AddRule("*", "127.0.0.1");
302 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
303 GURL base_url = GetTestBaseURL("app_process");
304
305 // Load an app as a bookmark app.
306 std::string error;
307 scoped_refptr<const Extension> extension(extensions::file_util::LoadExtension(
308 test_data_dir_.AppendASCII("app_process"),
309 extensions::Manifest::UNPACKED,
310 Extension::FROM_BOOKMARK,
311 &error));
312 service->OnExtensionInstalled(extension.get(),
313 syncer::StringOrdinal::CreateInitialOrdinal(),
314 extensions::kInstallFlagInstallImmediately);
315 ASSERT_TRUE(extension.get());
316 ASSERT_TRUE(extension->from_bookmark());
317
318 // Test both opening a URL in a new tab, and opening a tab and then navigating
319 // it. Either way, bookmark app tabs should be considered normal processes
320 // with no elevated privileges and no WebUI bindings.
321 ui_test_utils::NavigateToURLWithDisposition(
322 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
323 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
324 EXPECT_FALSE(process_map->Contains(
325 browser()->tab_strip_model()->GetWebContentsAt(1)->
326 GetRenderProcessHost()->GetID()));
327 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
328
329 content::WindowedNotificationObserver tab_added_observer(
330 chrome::NOTIFICATION_TAB_ADDED,
331 content::NotificationService::AllSources());
332 chrome::NewTab(browser());
333 tab_added_observer.Wait();
334 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
335 EXPECT_FALSE(process_map->Contains(
336 browser()->tab_strip_model()->GetWebContentsAt(2)->
337 GetRenderProcessHost()->GetID()));
338 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
339
340 // We should have opened 2 new bookmark app tabs. Including the original blank
341 // tab, we now have 3 tabs. Because normal pages use the
342 // process-per-site-instance model, each should be in its own process.
343 ASSERT_EQ(3, browser()->tab_strip_model()->count());
344 WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1);
345 EXPECT_NE(tab->GetRenderProcessHost(),
346 browser()->tab_strip_model()->GetWebContentsAt(2)->
347 GetRenderProcessHost());
348
349 // Now let's do the same using window.open. The same should happen.
350 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
351 browser()->host_desktop_type()));
352 OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL);
353 OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL);
354
355 // Now let's have a tab navigate out of and back into the app's web
356 // extent. Neither navigation should switch processes.
357 const GURL& app_url(base_url.Resolve("path1/empty.html"));
358 const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
359 RenderViewHost* host2 =
360 browser()->tab_strip_model()->GetWebContentsAt(2)->GetRenderViewHost();
361 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
362 non_app_url);
363 EXPECT_EQ(host2->GetProcess(),
364 browser()->tab_strip_model()->GetWebContentsAt(2)->
365 GetRenderProcessHost());
366 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
367 app_url);
368 EXPECT_EQ(host2->GetProcess(),
369 browser()->tab_strip_model()->GetWebContentsAt(2)->
370 GetRenderProcessHost());
371 }
372
373 // Tests that app process switching works properly in the following scenario:
374 // 1. navigate to a page1 in the app
375 // 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
376 // 3. page2 redirects back to a page in the app
377 // The final navigation should end up in the app process.
378 // See http://crbug.com/61757
379 // Flaky on Linux. http://crbug.com/341898
380 #if defined(OS_LINUX)
381 #define MAYBE_AppProcessRedirectBack DISABLED_AppProcessRedirectBack
382 #else
383 #define MAYBE_AppProcessRedirectBack AppProcessRedirectBack
384 #endif
IN_PROC_BROWSER_TEST_F(AppApiTest,MAYBE_AppProcessRedirectBack)385 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessRedirectBack) {
386 host_resolver()->AddRule("*", "127.0.0.1");
387 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
388
389 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
390
391 // Open two tabs in the app.
392 GURL base_url = GetTestBaseURL("app_process");
393
394 chrome::NewTab(browser());
395 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
396 chrome::NewTab(browser());
397 // Wait until the second tab finishes its redirect train (2 hops).
398 // 1. We navigate to redirect.html
399 // 2. Renderer navigates and finishes, counting as a load stop.
400 // 3. Renderer issues the meta refresh to navigate to server-redirect.
401 // 4. Renderer is now in a "provisional load", waiting for navigation to
402 // complete.
403 // 5. Browser sees a redirect response from server-redirect to empty.html, and
404 // transfers that to a new navigation, using RequestTransferURL.
405 // 6. Renderer navigates to empty.html, and finishes loading, counting as the
406 // second load stop
407 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
408 browser(), base_url.Resolve("path1/redirect.html"), 2);
409
410 // 3 tabs, including the initial about:blank. The last 2 should be the same
411 // process.
412 ASSERT_EQ(3, browser()->tab_strip_model()->count());
413 EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html",
414 browser()->tab_strip_model()->GetWebContentsAt(2)->
415 GetController().GetLastCommittedEntry()->GetURL().path());
416 EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)->
417 GetRenderProcessHost(),
418 browser()->tab_strip_model()->GetWebContentsAt(2)->
419 GetRenderProcessHost());
420 }
421
422 // Ensure that re-navigating to a URL after installing or uninstalling it as an
423 // app correctly swaps the tab to the app process. (http://crbug.com/80621)
424 //
425 // Fails on Windows. http://crbug.com/238670
426 // Added logging to help diagnose the location of the problem.
IN_PROC_BROWSER_TEST_F(AppApiTest,NavigateIntoAppProcess)427 IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
428 extensions::ProcessMap* process_map =
429 extensions::ProcessMap::Get(browser()->profile());
430
431 host_resolver()->AddRule("*", "127.0.0.1");
432 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
433
434 // The app under test acts on URLs whose host is "localhost",
435 // so the URLs we navigate to must have host "localhost".
436 GURL base_url = GetTestBaseURL("app_process");
437
438 // Load an app URL before loading the app.
439 LOG(INFO) << "Loading path1/empty.html.";
440 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
441 LOG(INFO) << "Loading path1/empty.html - done.";
442 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
443 EXPECT_FALSE(process_map->Contains(
444 contents->GetRenderProcessHost()->GetID()));
445
446 // Load app and re-navigate to the page.
447 LOG(INFO) << "Loading extension.";
448 const Extension* app =
449 LoadExtension(test_data_dir_.AppendASCII("app_process"));
450 LOG(INFO) << "Loading extension - done.";
451 ASSERT_TRUE(app);
452 LOG(INFO) << "Loading path1/empty.html.";
453 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
454 LOG(INFO) << "Loading path1/empty.html - done.";
455 EXPECT_TRUE(process_map->Contains(
456 contents->GetRenderProcessHost()->GetID()));
457
458 // Disable app and re-navigate to the page.
459 LOG(INFO) << "Disabling extension.";
460 DisableExtension(app->id());
461 LOG(INFO) << "Disabling extension - done.";
462 LOG(INFO) << "Loading path1/empty.html.";
463 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
464 LOG(INFO) << "Loading path1/empty.html - done.";
465 EXPECT_FALSE(process_map->Contains(
466 contents->GetRenderProcessHost()->GetID()));
467 }
468
469 // Ensure that reloading a URL after installing or uninstalling it as an app
470 // correctly swaps the tab to the app process. (http://crbug.com/80621)
471 //
472 // Added logging to help diagnose the location of the problem.
473 // http://crbug.com/238670
IN_PROC_BROWSER_TEST_F(AppApiTest,ReloadIntoAppProcess)474 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
475 extensions::ProcessMap* process_map =
476 extensions::ProcessMap::Get(browser()->profile());
477
478 host_resolver()->AddRule("*", "127.0.0.1");
479 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
480
481 // The app under test acts on URLs whose host is "localhost",
482 // so the URLs we navigate to must have host "localhost".
483 GURL base_url = GetTestBaseURL("app_process");
484
485 // Load app, disable it, and navigate to the page.
486 LOG(INFO) << "Loading extension.";
487 const Extension* app =
488 LoadExtension(test_data_dir_.AppendASCII("app_process"));
489 LOG(INFO) << "Loading extension - done.";
490 ASSERT_TRUE(app);
491 LOG(INFO) << "Disabling extension.";
492 DisableExtension(app->id());
493 LOG(INFO) << "Disabling extension - done.";
494 LOG(INFO) << "Navigate to path1/empty.html.";
495 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
496 LOG(INFO) << "Navigate to path1/empty.html - done.";
497 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
498 EXPECT_FALSE(process_map->Contains(
499 contents->GetRenderProcessHost()->GetID()));
500
501 // Enable app and reload the page.
502 LOG(INFO) << "Enabling extension.";
503 EnableExtension(app->id());
504 LOG(INFO) << "Enabling extension - done.";
505 content::WindowedNotificationObserver reload_observer(
506 content::NOTIFICATION_LOAD_STOP,
507 content::Source<NavigationController>(
508 &browser()->tab_strip_model()->GetActiveWebContents()->
509 GetController()));
510 LOG(INFO) << "Reloading.";
511 chrome::Reload(browser(), CURRENT_TAB);
512 reload_observer.Wait();
513 LOG(INFO) << "Reloading - done.";
514 EXPECT_TRUE(process_map->Contains(
515 contents->GetRenderProcessHost()->GetID()));
516
517 // Disable app and reload the page.
518 LOG(INFO) << "Disabling extension.";
519 DisableExtension(app->id());
520 LOG(INFO) << "Disabling extension - done.";
521 content::WindowedNotificationObserver reload_observer2(
522 content::NOTIFICATION_LOAD_STOP,
523 content::Source<NavigationController>(
524 &browser()->tab_strip_model()->GetActiveWebContents()->
525 GetController()));
526 LOG(INFO) << "Reloading.";
527 chrome::Reload(browser(), CURRENT_TAB);
528 reload_observer2.Wait();
529 LOG(INFO) << "Reloading - done.";
530 EXPECT_FALSE(process_map->Contains(
531 contents->GetRenderProcessHost()->GetID()));
532 }
533
534 // Ensure that reloading a URL with JavaScript after installing or uninstalling
535 // it as an app correctly swaps the process. (http://crbug.com/80621)
536 //
537 // Crashes on Windows and Mac. http://crbug.com/238670
538 // Added logging to help diagnose the location of the problem.
IN_PROC_BROWSER_TEST_F(AppApiTest,ReloadIntoAppProcessWithJavaScript)539 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
540 extensions::ProcessMap* process_map =
541 extensions::ProcessMap::Get(browser()->profile());
542
543 host_resolver()->AddRule("*", "127.0.0.1");
544 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
545
546 // The app under test acts on URLs whose host is "localhost",
547 // so the URLs we navigate to must have host "localhost".
548 GURL base_url = GetTestBaseURL("app_process");
549
550 // Load app, disable it, and navigate to the page.
551 LOG(INFO) << "Loading extension.";
552 const Extension* app =
553 LoadExtension(test_data_dir_.AppendASCII("app_process"));
554 LOG(INFO) << "Loading extension - done.";
555 ASSERT_TRUE(app);
556 LOG(INFO) << "Disabling extension.";
557 DisableExtension(app->id());
558 LOG(INFO) << "Disabling extension - done.";
559 LOG(INFO) << "Navigate to path1/empty.html.";
560 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
561 LOG(INFO) << "Navigate to path1/empty.html - done.";
562 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
563 EXPECT_FALSE(process_map->Contains(
564 contents->GetRenderProcessHost()->GetID()));
565
566 // Enable app and reload via JavaScript.
567 LOG(INFO) << "Enabling extension.";
568 EnableExtension(app->id());
569 LOG(INFO) << "Enabling extension - done.";
570 content::WindowedNotificationObserver js_reload_observer(
571 content::NOTIFICATION_LOAD_STOP,
572 content::Source<NavigationController>(
573 &browser()->tab_strip_model()->GetActiveWebContents()->
574 GetController()));
575 LOG(INFO) << "Executing location.reload().";
576 ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();"));
577 js_reload_observer.Wait();
578 LOG(INFO) << "Executing location.reload() - done.";
579 EXPECT_TRUE(process_map->Contains(
580 contents->GetRenderProcessHost()->GetID()));
581
582 // Disable app and reload via JavaScript.
583 LOG(INFO) << "Disabling extension.";
584 DisableExtension(app->id());
585 LOG(INFO) << "Disabling extension - done.";
586 content::WindowedNotificationObserver js_reload_observer2(
587 content::NOTIFICATION_LOAD_STOP,
588 content::Source<NavigationController>(
589 &browser()->tab_strip_model()->GetActiveWebContents()->
590 GetController()));
591 LOG(INFO) << "Executing location = location.";
592 ASSERT_TRUE(content::ExecuteScript(contents, "location = location;"));
593 js_reload_observer2.Wait();
594 LOG(INFO) << "Executing location = location - done.";
595 EXPECT_FALSE(process_map->Contains(
596 contents->GetRenderProcessHost()->GetID()));
597 }
598
599 // Tests that if we have a non-app process (path3/container.html) that has an
600 // iframe with a URL in the app's extent (path1/iframe.html), then opening a
601 // link from that iframe to a new window to a URL in the app's extent (path1/
602 // empty.html) results in the new window being in an app process. See
603 // http://crbug.com/89272 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest,OpenAppFromIframe)604 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
605 #if defined(OS_WIN) && defined(USE_ASH)
606 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
607 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
608 return;
609 #endif
610
611 extensions::ProcessMap* process_map =
612 extensions::ProcessMap::Get(browser()->profile());
613
614 host_resolver()->AddRule("*", "127.0.0.1");
615 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
616
617 GURL base_url = GetTestBaseURL("app_process");
618
619 // Load app and start URL (not in the app).
620 const Extension* app =
621 LoadExtension(test_data_dir_.AppendASCII("app_process"));
622 ASSERT_TRUE(app);
623
624 ui_test_utils::NavigateToURL(browser(),
625 base_url.Resolve("path3/container.html"));
626 EXPECT_FALSE(process_map->Contains(
627 browser()->tab_strip_model()->GetWebContentsAt(0)->
628 GetRenderProcessHost()->GetID()));
629
630 const BrowserList* active_browser_list =
631 BrowserList::GetInstance(chrome::GetActiveDesktop());
632 EXPECT_EQ(2U, active_browser_list->size());
633 content::WebContents* popup_contents =
634 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
635 content::WaitForLoadStop(popup_contents);
636
637 // Popup window should be in the app's process.
638 RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
639 EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID()));
640 }
641
642 // Similar to the previous test, but ensure that popup blocking bypass
643 // isn't granted to the iframe. See crbug.com/117446.
644 #if defined(OS_CHROMEOS)
645 // http://crbug.com/153513
646 #define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe
647 #else
648 #define MAYBE_OpenAppFromIframe OpenAppFromIframe
649 #endif
IN_PROC_BROWSER_TEST_F(BlockedAppApiTest,MAYBE_OpenAppFromIframe)650 IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) {
651 host_resolver()->AddRule("*", "127.0.0.1");
652 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
653
654 // Load app and start URL (not in the app).
655 const Extension* app =
656 LoadExtension(test_data_dir_.AppendASCII("app_process"));
657 ASSERT_TRUE(app);
658
659 ui_test_utils::NavigateToURL(
660 browser(), GetTestBaseURL("app_process").Resolve("path3/container.html"));
661
662 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
663 PopupBlockerTabHelper* popup_blocker_tab_helper =
664 PopupBlockerTabHelper::FromWebContents(tab);
665 if (!popup_blocker_tab_helper->GetBlockedPopupsCount()) {
666 content::WindowedNotificationObserver observer(
667 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
668 content::NotificationService::AllSources());
669 observer.Wait();
670 }
671
672 EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount());
673 }
674
675 // Tests that if an extension launches an app via chrome.tabs.create with an URL
676 // that's not in the app's extent but that server redirects to it, we still end
677 // up with an app process. See http://crbug.com/99349 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest,ServerRedirectToAppFromExtension)678 IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
679 host_resolver()->AddRule("*", "127.0.0.1");
680 ASSERT_TRUE(StartEmbeddedTestServer());
681
682 LoadExtension(test_data_dir_.AppendASCII("app_process"));
683 const Extension* launcher =
684 LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
685
686 // There should be two navigations by the time the app page is loaded.
687 // 1. The extension launcher page.
688 // 2. The app's URL (which includes a server redirect).
689 // Note that the server redirect does not generate a navigation event.
690 content::TestNavigationObserver test_navigation_observer(
691 browser()->tab_strip_model()->GetActiveWebContents(),
692 2);
693 test_navigation_observer.StartWatchingNewWebContents();
694
695 // Load the launcher extension, which should launch the app.
696 ui_test_utils::NavigateToURLWithDisposition(
697 browser(),
698 launcher->GetResourceURL("server_redirect.html"),
699 CURRENT_TAB,
700 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
701
702 // Wait for app tab to be created and loaded.
703 test_navigation_observer.Wait();
704
705 // App has loaded, and chrome.app.isInstalled should be true.
706 bool is_installed = false;
707 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
708 browser()->tab_strip_model()->GetActiveWebContents(),
709 "window.domAutomationController.send(chrome.app.isInstalled)",
710 &is_installed));
711 ASSERT_TRUE(is_installed);
712 }
713
714 // Tests that if an extension launches an app via chrome.tabs.create with an URL
715 // that's not in the app's extent but that client redirects to it, we still end
716 // up with an app process.
IN_PROC_BROWSER_TEST_F(AppApiTest,ClientRedirectToAppFromExtension)717 IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
718 host_resolver()->AddRule("*", "127.0.0.1");
719 ASSERT_TRUE(StartEmbeddedTestServer());
720
721 LoadExtension(test_data_dir_.AppendASCII("app_process"));
722 const Extension* launcher =
723 LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
724
725 // There should be three navigations by the time the app page is loaded.
726 // 1. The extension launcher page.
727 // 2. The URL that the extension launches, which client redirects.
728 // 3. The app's URL.
729 content::TestNavigationObserver test_navigation_observer(
730 browser()->tab_strip_model()->GetActiveWebContents(),
731 3);
732 test_navigation_observer.StartWatchingNewWebContents();
733
734 // Load the launcher extension, which should launch the app.
735 ui_test_utils::NavigateToURLWithDisposition(
736 browser(),
737 launcher->GetResourceURL("client_redirect.html"),
738 CURRENT_TAB,
739 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
740
741 // Wait for app tab to be created and loaded.
742 test_navigation_observer.Wait();
743
744 // App has loaded, and chrome.app.isInstalled should be true.
745 bool is_installed = false;
746 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
747 browser()->tab_strip_model()->GetActiveWebContents(),
748 "window.domAutomationController.send(chrome.app.isInstalled)",
749 &is_installed));
750 ASSERT_TRUE(is_installed);
751 }
752
753 // Tests that if we have an app process (path1/container.html) with a non-app
754 // iframe (path3/iframe.html), then opening a link from that iframe to a new
755 // window to a same-origin non-app URL (path3/empty.html) should keep the window
756 // in the app process.
757 // This is in contrast to OpenAppFromIframe, since here the popup will not be
758 // missing special permissions and should be scriptable from the iframe.
759 // See http://crbug.com/92669 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest,OpenWebPopupFromWebIframe)760 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
761 extensions::ProcessMap* process_map =
762 extensions::ProcessMap::Get(browser()->profile());
763
764 host_resolver()->AddRule("*", "127.0.0.1");
765 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
766
767 GURL base_url = GetTestBaseURL("app_process");
768
769 // Load app and start URL (in the app).
770 const Extension* app =
771 LoadExtension(test_data_dir_.AppendASCII("app_process"));
772 ASSERT_TRUE(app);
773
774 ui_test_utils::NavigateToURL(browser(),
775 base_url.Resolve("path1/container.html"));
776 content::RenderProcessHost* process =
777 browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
778 EXPECT_TRUE(process_map->Contains(process->GetID()));
779
780 // Popup window should be in the app's process.
781 const BrowserList* active_browser_list =
782 BrowserList::GetInstance(chrome::GetActiveDesktop());
783 EXPECT_EQ(2U, active_browser_list->size());
784 content::WebContents* popup_contents =
785 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
786 content::WaitForLoadStop(popup_contents);
787
788 RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
789 EXPECT_EQ(process, popup_host->GetProcess());
790 }
791
792 // http://crbug.com/118502
793 #if defined(OS_MACOSX) || defined(OS_LINUX)
794 #define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash
795 #else
796 #define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash
797 #endif
IN_PROC_BROWSER_TEST_F(AppApiTest,MAYBE_ReloadAppAfterCrash)798 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) {
799 extensions::ProcessMap* process_map =
800 extensions::ProcessMap::Get(browser()->profile());
801
802 host_resolver()->AddRule("*", "127.0.0.1");
803 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
804
805 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
806
807 GURL base_url = GetTestBaseURL("app_process");
808
809 // Load the app, chrome.app.isInstalled should be true.
810 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
811 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
812 EXPECT_TRUE(process_map->Contains(
813 contents->GetRenderProcessHost()->GetID()));
814 bool is_installed = false;
815 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
816 contents,
817 "window.domAutomationController.send(chrome.app.isInstalled)",
818 &is_installed));
819 ASSERT_TRUE(is_installed);
820
821 // Crash the tab and reload it, chrome.app.isInstalled should still be true.
822 content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
823 content::WindowedNotificationObserver observer(
824 content::NOTIFICATION_LOAD_STOP,
825 content::Source<NavigationController>(
826 &browser()->tab_strip_model()->GetActiveWebContents()->
827 GetController()));
828 chrome::Reload(browser(), CURRENT_TAB);
829 observer.Wait();
830 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
831 contents,
832 "window.domAutomationController.send(chrome.app.isInstalled)",
833 &is_installed));
834 ASSERT_TRUE(is_installed);
835 }
836
837 // Test that a cross-process navigation away from a hosted app stays in the same
838 // BrowsingInstance, so that postMessage calls to the app's other windows still
839 // work.
IN_PROC_BROWSER_TEST_F(AppApiTest,SameBrowsingInstanceAfterSwap)840 IN_PROC_BROWSER_TEST_F(AppApiTest, SameBrowsingInstanceAfterSwap) {
841 extensions::ProcessMap* process_map =
842 extensions::ProcessMap::Get(browser()->profile());
843
844 host_resolver()->AddRule("*", "127.0.0.1");
845 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
846
847 GURL base_url = GetTestBaseURL("app_process");
848
849 // Load app and start URL (in the app).
850 const Extension* app =
851 LoadExtension(test_data_dir_.AppendASCII("app_process"));
852 ASSERT_TRUE(app);
853
854 ui_test_utils::NavigateToURL(browser(),
855 base_url.Resolve("path1/iframe.html"));
856 content::SiteInstance* app_instance =
857 browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance();
858 EXPECT_TRUE(process_map->Contains(app_instance->GetProcess()->GetID()));
859
860 // Popup window should be in the app's process.
861 const BrowserList* active_browser_list =
862 BrowserList::GetInstance(chrome::GetActiveDesktop());
863 EXPECT_EQ(2U, active_browser_list->size());
864 content::WebContents* popup_contents =
865 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
866 content::WaitForLoadStop(popup_contents);
867
868 SiteInstance* popup_instance = popup_contents->GetSiteInstance();
869 EXPECT_EQ(app_instance, popup_instance);
870
871 // Navigate the popup to another process outside the app.
872 GURL non_app_url(base_url.Resolve("path3/empty.html"));
873 ui_test_utils::NavigateToURL(active_browser_list->get(1), non_app_url);
874 SiteInstance* new_instance = popup_contents->GetSiteInstance();
875 EXPECT_NE(app_instance, new_instance);
876
877 // It should still be in the same BrowsingInstance, allowing postMessage to
878 // work.
879 EXPECT_TRUE(app_instance->IsRelatedSiteInstance(new_instance));
880 }
881