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