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
39using content::NavigationController;
40using content::RenderViewHost;
41using content::SiteInstance;
42using content::WebContents;
43using extensions::Extension;
44
45class 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.
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.
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.
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.
135class BlockedAppApiTest : public AppApiTest {
136 protected:
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
147IN_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
269IN_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
283IN_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
295IN_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.  http://crbug.com/341898
380IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcessRedirectBack) {
381  host_resolver()->AddRule("*", "127.0.0.1");
382  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
383
384  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
385
386  // Open two tabs in the app.
387  GURL base_url = GetTestBaseURL("app_process");
388
389  chrome::NewTab(browser());
390  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
391  chrome::NewTab(browser());
392  // Wait until the second tab finishes its redirect train (2 hops).
393  // 1. We navigate to redirect.html
394  // 2. Renderer navigates and finishes, counting as a load stop.
395  // 3. Renderer issues the meta refresh to navigate to server-redirect.
396  // 4. Renderer is now in a "provisional load", waiting for navigation to
397  //    complete.
398  // 5. Browser sees a redirect response from server-redirect to empty.html, and
399  //    transfers that to a new navigation, using RequestTransferURL.
400  // 6. Renderer navigates to empty.html, and finishes loading, counting as the
401  //    second load stop
402  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
403      browser(), base_url.Resolve("path1/redirect.html"), 2);
404
405  // 3 tabs, including the initial about:blank. The last 2 should be the same
406  // process.
407  ASSERT_EQ(3, browser()->tab_strip_model()->count());
408  EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html",
409            browser()->tab_strip_model()->GetWebContentsAt(2)->
410                GetController().GetLastCommittedEntry()->GetURL().path());
411  EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)->
412                GetRenderProcessHost(),
413            browser()->tab_strip_model()->GetWebContentsAt(2)->
414                GetRenderProcessHost());
415}
416
417// Ensure that re-navigating to a URL after installing or uninstalling it as an
418// app correctly swaps the tab to the app process.  (http://crbug.com/80621)
419//
420// Fails on Windows. http://crbug.com/238670
421// Added logging to help diagnose the location of the problem.
422IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
423  extensions::ProcessMap* process_map =
424      extensions::ProcessMap::Get(browser()->profile());
425
426  host_resolver()->AddRule("*", "127.0.0.1");
427  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
428
429  // The app under test acts on URLs whose host is "localhost",
430  // so the URLs we navigate to must have host "localhost".
431  GURL base_url = GetTestBaseURL("app_process");
432
433  // Load an app URL before loading the app.
434  LOG(INFO) << "Loading path1/empty.html.";
435  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
436  LOG(INFO) << "Loading path1/empty.html - done.";
437  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
438  EXPECT_FALSE(process_map->Contains(
439      contents->GetRenderProcessHost()->GetID()));
440
441  // Load app and re-navigate to the page.
442  LOG(INFO) << "Loading extension.";
443  const Extension* app =
444      LoadExtension(test_data_dir_.AppendASCII("app_process"));
445  LOG(INFO) << "Loading extension - done.";
446  ASSERT_TRUE(app);
447  LOG(INFO) << "Loading path1/empty.html.";
448  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
449  LOG(INFO) << "Loading path1/empty.html - done.";
450  EXPECT_TRUE(process_map->Contains(
451      contents->GetRenderProcessHost()->GetID()));
452
453  // Disable app and re-navigate to the page.
454  LOG(INFO) << "Disabling extension.";
455  DisableExtension(app->id());
456  LOG(INFO) << "Disabling extension - done.";
457  LOG(INFO) << "Loading path1/empty.html.";
458  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
459  LOG(INFO) << "Loading path1/empty.html - done.";
460  EXPECT_FALSE(process_map->Contains(
461      contents->GetRenderProcessHost()->GetID()));
462}
463
464// Ensure that reloading a URL after installing or uninstalling it as an app
465// correctly swaps the tab to the app process.  (http://crbug.com/80621)
466//
467// Added logging to help diagnose the location of the problem.
468// http://crbug.com/238670
469IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
470  extensions::ProcessMap* process_map =
471      extensions::ProcessMap::Get(browser()->profile());
472
473  host_resolver()->AddRule("*", "127.0.0.1");
474  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
475
476  // The app under test acts on URLs whose host is "localhost",
477  // so the URLs we navigate to must have host "localhost".
478  GURL base_url = GetTestBaseURL("app_process");
479
480  // Load app, disable it, and navigate to the page.
481  LOG(INFO) << "Loading extension.";
482  const Extension* app =
483      LoadExtension(test_data_dir_.AppendASCII("app_process"));
484  LOG(INFO) << "Loading extension - done.";
485  ASSERT_TRUE(app);
486  LOG(INFO) << "Disabling extension.";
487  DisableExtension(app->id());
488  LOG(INFO) << "Disabling extension - done.";
489  LOG(INFO) << "Navigate to path1/empty.html.";
490  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
491  LOG(INFO) << "Navigate to path1/empty.html - done.";
492  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
493  EXPECT_FALSE(process_map->Contains(
494      contents->GetRenderProcessHost()->GetID()));
495
496  // Enable app and reload the page.
497  LOG(INFO) << "Enabling extension.";
498  EnableExtension(app->id());
499  LOG(INFO) << "Enabling extension - done.";
500  content::WindowedNotificationObserver reload_observer(
501      content::NOTIFICATION_LOAD_STOP,
502      content::Source<NavigationController>(
503          &browser()->tab_strip_model()->GetActiveWebContents()->
504              GetController()));
505  LOG(INFO) << "Reloading.";
506  chrome::Reload(browser(), CURRENT_TAB);
507  reload_observer.Wait();
508  LOG(INFO) << "Reloading - done.";
509  EXPECT_TRUE(process_map->Contains(
510      contents->GetRenderProcessHost()->GetID()));
511
512  // Disable app and reload the page.
513  LOG(INFO) << "Disabling extension.";
514  DisableExtension(app->id());
515  LOG(INFO) << "Disabling extension - done.";
516  content::WindowedNotificationObserver reload_observer2(
517      content::NOTIFICATION_LOAD_STOP,
518      content::Source<NavigationController>(
519          &browser()->tab_strip_model()->GetActiveWebContents()->
520              GetController()));
521  LOG(INFO) << "Reloading.";
522  chrome::Reload(browser(), CURRENT_TAB);
523  reload_observer2.Wait();
524  LOG(INFO) << "Reloading - done.";
525  EXPECT_FALSE(process_map->Contains(
526      contents->GetRenderProcessHost()->GetID()));
527}
528
529// Ensure that reloading a URL with JavaScript after installing or uninstalling
530// it as an app correctly swaps the process.  (http://crbug.com/80621)
531//
532// Crashes on Windows and Mac. http://crbug.com/238670
533// Added logging to help diagnose the location of the problem.
534IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
535  extensions::ProcessMap* process_map =
536      extensions::ProcessMap::Get(browser()->profile());
537
538  host_resolver()->AddRule("*", "127.0.0.1");
539  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
540
541  // The app under test acts on URLs whose host is "localhost",
542  // so the URLs we navigate to must have host "localhost".
543  GURL base_url = GetTestBaseURL("app_process");
544
545  // Load app, disable it, and navigate to the page.
546  LOG(INFO) << "Loading extension.";
547  const Extension* app =
548      LoadExtension(test_data_dir_.AppendASCII("app_process"));
549  LOG(INFO) << "Loading extension - done.";
550  ASSERT_TRUE(app);
551  LOG(INFO) << "Disabling extension.";
552  DisableExtension(app->id());
553  LOG(INFO) << "Disabling extension - done.";
554  LOG(INFO) << "Navigate to path1/empty.html.";
555  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
556  LOG(INFO) << "Navigate to path1/empty.html - done.";
557  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
558  EXPECT_FALSE(process_map->Contains(
559      contents->GetRenderProcessHost()->GetID()));
560
561  // Enable app and reload via JavaScript.
562  LOG(INFO) << "Enabling extension.";
563  EnableExtension(app->id());
564  LOG(INFO) << "Enabling extension - done.";
565  content::WindowedNotificationObserver js_reload_observer(
566      content::NOTIFICATION_LOAD_STOP,
567      content::Source<NavigationController>(
568          &browser()->tab_strip_model()->GetActiveWebContents()->
569              GetController()));
570  LOG(INFO) << "Executing location.reload().";
571  ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();"));
572  js_reload_observer.Wait();
573  LOG(INFO) << "Executing location.reload() - done.";
574  EXPECT_TRUE(process_map->Contains(
575      contents->GetRenderProcessHost()->GetID()));
576
577  // Disable app and reload via JavaScript.
578  LOG(INFO) << "Disabling extension.";
579  DisableExtension(app->id());
580  LOG(INFO) << "Disabling extension - done.";
581  content::WindowedNotificationObserver js_reload_observer2(
582      content::NOTIFICATION_LOAD_STOP,
583      content::Source<NavigationController>(
584          &browser()->tab_strip_model()->GetActiveWebContents()->
585              GetController()));
586  LOG(INFO) << "Executing location = location.";
587  ASSERT_TRUE(content::ExecuteScript(contents, "location = location;"));
588  js_reload_observer2.Wait();
589  LOG(INFO) << "Executing location = location - done.";
590  EXPECT_FALSE(process_map->Contains(
591      contents->GetRenderProcessHost()->GetID()));
592}
593
594// Tests that if we have a non-app process (path3/container.html) that has an
595// iframe with  a URL in the app's extent (path1/iframe.html), then opening a
596// link from that iframe to a new window to a URL in the app's extent (path1/
597// empty.html) results in the new window being in an app process. See
598// http://crbug.com/89272 for more details.
599IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
600#if defined(OS_WIN) && defined(USE_ASH)
601  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
602  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
603    return;
604#endif
605
606  extensions::ProcessMap* process_map =
607      extensions::ProcessMap::Get(browser()->profile());
608
609  host_resolver()->AddRule("*", "127.0.0.1");
610  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
611
612  GURL base_url = GetTestBaseURL("app_process");
613
614  // Load app and start URL (not in the app).
615  const Extension* app =
616      LoadExtension(test_data_dir_.AppendASCII("app_process"));
617  ASSERT_TRUE(app);
618
619  ui_test_utils::NavigateToURL(browser(),
620                               base_url.Resolve("path3/container.html"));
621  EXPECT_FALSE(process_map->Contains(
622      browser()->tab_strip_model()->GetWebContentsAt(0)->
623          GetRenderProcessHost()->GetID()));
624
625  const BrowserList* active_browser_list =
626      BrowserList::GetInstance(chrome::GetActiveDesktop());
627  EXPECT_EQ(2U, active_browser_list->size());
628  content::WebContents* popup_contents =
629      active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
630  content::WaitForLoadStop(popup_contents);
631
632  // Popup window should be in the app's process.
633  RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
634  EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID()));
635}
636
637// Similar to the previous test, but ensure that popup blocking bypass
638// isn't granted to the iframe.  See crbug.com/117446.
639#if defined(OS_CHROMEOS)
640// http://crbug.com/153513
641#define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe
642#else
643#define MAYBE_OpenAppFromIframe OpenAppFromIframe
644#endif
645IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) {
646  host_resolver()->AddRule("*", "127.0.0.1");
647  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
648
649  // Load app and start URL (not in the app).
650  const Extension* app =
651      LoadExtension(test_data_dir_.AppendASCII("app_process"));
652  ASSERT_TRUE(app);
653
654  ui_test_utils::NavigateToURL(
655      browser(), GetTestBaseURL("app_process").Resolve("path3/container.html"));
656
657  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
658  PopupBlockerTabHelper* popup_blocker_tab_helper =
659      PopupBlockerTabHelper::FromWebContents(tab);
660  if (!popup_blocker_tab_helper->GetBlockedPopupsCount()) {
661    content::WindowedNotificationObserver observer(
662        chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
663        content::NotificationService::AllSources());
664    observer.Wait();
665  }
666
667  EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount());
668}
669
670// Tests that if an extension launches an app via chrome.tabs.create with an URL
671// that's not in the app's extent but that server redirects to it, we still end
672// up with an app process. See http://crbug.com/99349 for more details.
673IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
674  host_resolver()->AddRule("*", "127.0.0.1");
675  ASSERT_TRUE(StartEmbeddedTestServer());
676
677  LoadExtension(test_data_dir_.AppendASCII("app_process"));
678  const Extension* launcher =
679      LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
680
681  // There should be two navigations by the time the app page is loaded.
682  // 1. The extension launcher page.
683  // 2. The app's URL (which includes a server redirect).
684  // Note that the server redirect does not generate a navigation event.
685  content::TestNavigationObserver test_navigation_observer(
686      browser()->tab_strip_model()->GetActiveWebContents(),
687      2);
688  test_navigation_observer.StartWatchingNewWebContents();
689
690  // Load the launcher extension, which should launch the app.
691  ui_test_utils::NavigateToURLWithDisposition(
692      browser(),
693      launcher->GetResourceURL("server_redirect.html"),
694      CURRENT_TAB,
695      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
696
697  // Wait for app tab to be created and loaded.
698  test_navigation_observer.Wait();
699
700  // App has loaded, and chrome.app.isInstalled should be true.
701  bool is_installed = false;
702  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
703      browser()->tab_strip_model()->GetActiveWebContents(),
704      "window.domAutomationController.send(chrome.app.isInstalled)",
705      &is_installed));
706  ASSERT_TRUE(is_installed);
707}
708
709// Tests that if an extension launches an app via chrome.tabs.create with an URL
710// that's not in the app's extent but that client redirects to it, we still end
711// up with an app process.
712IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
713  host_resolver()->AddRule("*", "127.0.0.1");
714  ASSERT_TRUE(StartEmbeddedTestServer());
715
716  LoadExtension(test_data_dir_.AppendASCII("app_process"));
717  const Extension* launcher =
718      LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
719
720  // There should be three navigations by the time the app page is loaded.
721  // 1. The extension launcher page.
722  // 2. The URL that the extension launches, which client redirects.
723  // 3. The app's URL.
724  content::TestNavigationObserver test_navigation_observer(
725      browser()->tab_strip_model()->GetActiveWebContents(),
726      3);
727  test_navigation_observer.StartWatchingNewWebContents();
728
729  // Load the launcher extension, which should launch the app.
730  ui_test_utils::NavigateToURLWithDisposition(
731      browser(),
732      launcher->GetResourceURL("client_redirect.html"),
733      CURRENT_TAB,
734      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
735
736  // Wait for app tab to be created and loaded.
737  test_navigation_observer.Wait();
738
739  // App has loaded, and chrome.app.isInstalled should be true.
740  bool is_installed = false;
741  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
742      browser()->tab_strip_model()->GetActiveWebContents(),
743      "window.domAutomationController.send(chrome.app.isInstalled)",
744      &is_installed));
745  ASSERT_TRUE(is_installed);
746}
747
748// Tests that if we have an app process (path1/container.html) with a non-app
749// iframe (path3/iframe.html), then opening a link from that iframe to a new
750// window to a same-origin non-app URL (path3/empty.html) should keep the window
751// in the app process.
752// This is in contrast to OpenAppFromIframe, since here the popup will not be
753// missing special permissions and should be scriptable from the iframe.
754// See http://crbug.com/92669 for more details.
755IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
756  extensions::ProcessMap* process_map =
757      extensions::ProcessMap::Get(browser()->profile());
758
759  host_resolver()->AddRule("*", "127.0.0.1");
760  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
761
762  GURL base_url = GetTestBaseURL("app_process");
763
764  // Load app and start URL (in the app).
765  const Extension* app =
766      LoadExtension(test_data_dir_.AppendASCII("app_process"));
767  ASSERT_TRUE(app);
768
769  ui_test_utils::NavigateToURL(browser(),
770                               base_url.Resolve("path1/container.html"));
771  content::RenderProcessHost* process =
772      browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
773  EXPECT_TRUE(process_map->Contains(process->GetID()));
774
775  // Popup window should be in the app's process.
776  const BrowserList* active_browser_list =
777      BrowserList::GetInstance(chrome::GetActiveDesktop());
778  EXPECT_EQ(2U, active_browser_list->size());
779  content::WebContents* popup_contents =
780      active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
781  content::WaitForLoadStop(popup_contents);
782
783  RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
784  EXPECT_EQ(process, popup_host->GetProcess());
785}
786
787// http://crbug.com/118502
788#if defined(OS_MACOSX) || defined(OS_LINUX)
789#define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash
790#else
791#define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash
792#endif
793IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) {
794  extensions::ProcessMap* process_map =
795      extensions::ProcessMap::Get(browser()->profile());
796
797  host_resolver()->AddRule("*", "127.0.0.1");
798  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
799
800  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
801
802  GURL base_url = GetTestBaseURL("app_process");
803
804  // Load the app, chrome.app.isInstalled should be true.
805  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
806  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
807  EXPECT_TRUE(process_map->Contains(
808      contents->GetRenderProcessHost()->GetID()));
809  bool is_installed = false;
810  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
811      contents,
812      "window.domAutomationController.send(chrome.app.isInstalled)",
813      &is_installed));
814  ASSERT_TRUE(is_installed);
815
816  // Crash the tab and reload it, chrome.app.isInstalled should still be true.
817  content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
818  content::WindowedNotificationObserver observer(
819      content::NOTIFICATION_LOAD_STOP,
820      content::Source<NavigationController>(
821          &browser()->tab_strip_model()->GetActiveWebContents()->
822              GetController()));
823  chrome::Reload(browser(), CURRENT_TAB);
824  observer.Wait();
825  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
826      contents,
827      "window.domAutomationController.send(chrome.app.isInstalled)",
828      &is_installed));
829  ASSERT_TRUE(is_installed);
830}
831
832// Test that a cross-process navigation away from a hosted app stays in the same
833// BrowsingInstance, so that postMessage calls to the app's other windows still
834// work.
835IN_PROC_BROWSER_TEST_F(AppApiTest, SameBrowsingInstanceAfterSwap) {
836  extensions::ProcessMap* process_map =
837      extensions::ProcessMap::Get(browser()->profile());
838
839  host_resolver()->AddRule("*", "127.0.0.1");
840  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
841
842  GURL base_url = GetTestBaseURL("app_process");
843
844  // Load app and start URL (in the app).
845  const Extension* app =
846      LoadExtension(test_data_dir_.AppendASCII("app_process"));
847  ASSERT_TRUE(app);
848
849  ui_test_utils::NavigateToURL(browser(),
850                               base_url.Resolve("path1/iframe.html"));
851  content::SiteInstance* app_instance =
852      browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance();
853  EXPECT_TRUE(process_map->Contains(app_instance->GetProcess()->GetID()));
854
855  // Popup window should be in the app's process.
856  const BrowserList* active_browser_list =
857      BrowserList::GetInstance(chrome::GetActiveDesktop());
858  EXPECT_EQ(2U, active_browser_list->size());
859  content::WebContents* popup_contents =
860      active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
861  content::WaitForLoadStop(popup_contents);
862
863  SiteInstance* popup_instance = popup_contents->GetSiteInstance();
864  EXPECT_EQ(app_instance, popup_instance);
865
866  // Navigate the popup to another process outside the app.
867  GURL non_app_url(base_url.Resolve("path3/empty.html"));
868  ui_test_utils::NavigateToURL(active_browser_list->get(1), non_app_url);
869  SiteInstance* new_instance = popup_contents->GetSiteInstance();
870  EXPECT_NE(app_instance, new_instance);
871
872  // It should still be in the same BrowsingInstance, allowing postMessage to
873  // work.
874  EXPECT_TRUE(app_instance->IsRelatedSiteInstance(new_instance));
875}
876