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