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/extensions/process_map.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
14#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_commands.h"
17#include "chrome/browser/ui/browser_finder.h"
18#include "chrome/browser/ui/browser_list.h"
19#include "chrome/browser/ui/browser_window.h"
20#include "chrome/browser/ui/tabs/tab_strip_model.h"
21#include "chrome/common/chrome_switches.h"
22#include "chrome/common/extensions/extension.h"
23#include "chrome/common/extensions/extension_file_util.h"
24#include "chrome/test/base/test_switches.h"
25#include "chrome/test/base/ui_test_utils.h"
26#include "content/public/browser/navigation_entry.h"
27#include "content/public/browser/notification_service.h"
28#include "content/public/browser/render_process_host.h"
29#include "content/public/browser/render_view_host.h"
30#include "content/public/browser/web_contents.h"
31#include "content/public/test/browser_test_utils.h"
32#include "content/public/test/test_navigation_observer.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        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        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// Ensure that re-navigating to a URL after installing or uninstalling it as an
415// app correctly swaps the tab to the app process.  (http://crbug.com/80621)
416//
417// Fails on Windows. http://crbug.com/238670
418// Added logging to help diagnose the location of the problem.
419IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
420  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
421      browser()->profile())->extension_service()->process_map();
422
423  host_resolver()->AddRule("*", "127.0.0.1");
424  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
425
426  // The app under test acts on URLs whose host is "localhost",
427  // so the URLs we navigate to must have host "localhost".
428  GURL base_url = GetTestBaseURL("app_process");
429
430  // Load an app URL before loading the app.
431  LOG(INFO) << "Loading path1/empty.html.";
432  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
433  LOG(INFO) << "Loading path1/empty.html - done.";
434  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
435  EXPECT_FALSE(process_map->Contains(
436      contents->GetRenderProcessHost()->GetID()));
437
438  // Load app and re-navigate to the page.
439  LOG(INFO) << "Loading extension.";
440  const Extension* app =
441      LoadExtension(test_data_dir_.AppendASCII("app_process"));
442  LOG(INFO) << "Loading extension - done.";
443  ASSERT_TRUE(app);
444  LOG(INFO) << "Loading path1/empty.html.";
445  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
446  LOG(INFO) << "Loading path1/empty.html - done.";
447  EXPECT_TRUE(process_map->Contains(
448      contents->GetRenderProcessHost()->GetID()));
449
450  // Disable app and re-navigate to the page.
451  LOG(INFO) << "Disabling extension.";
452  DisableExtension(app->id());
453  LOG(INFO) << "Disabling extension - done.";
454  LOG(INFO) << "Loading path1/empty.html.";
455  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
456  LOG(INFO) << "Loading path1/empty.html - done.";
457  EXPECT_FALSE(process_map->Contains(
458      contents->GetRenderProcessHost()->GetID()));
459}
460
461// Ensure that reloading a URL after installing or uninstalling it as an app
462// correctly swaps the tab to the app process.  (http://crbug.com/80621)
463//
464// Added logging to help diagnose the location of the problem.
465// http://crbug.com/238670
466IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
467  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
468      browser()->profile())->extension_service()->process_map();
469
470  host_resolver()->AddRule("*", "127.0.0.1");
471  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
472
473  // The app under test acts on URLs whose host is "localhost",
474  // so the URLs we navigate to must have host "localhost".
475  GURL base_url = GetTestBaseURL("app_process");
476
477  // Load app, disable it, and navigate to the page.
478  LOG(INFO) << "Loading extension.";
479  const Extension* app =
480      LoadExtension(test_data_dir_.AppendASCII("app_process"));
481  LOG(INFO) << "Loading extension - done.";
482  ASSERT_TRUE(app);
483  LOG(INFO) << "Disabling extension.";
484  DisableExtension(app->id());
485  LOG(INFO) << "Disabling extension - done.";
486  LOG(INFO) << "Navigate to path1/empty.html.";
487  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
488  LOG(INFO) << "Navigate to path1/empty.html - done.";
489  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
490  EXPECT_FALSE(process_map->Contains(
491      contents->GetRenderProcessHost()->GetID()));
492
493  // Enable app and reload the page.
494  LOG(INFO) << "Enabling extension.";
495  EnableExtension(app->id());
496  LOG(INFO) << "Enabling extension - done.";
497  content::WindowedNotificationObserver reload_observer(
498      content::NOTIFICATION_LOAD_STOP,
499      content::Source<NavigationController>(
500          &browser()->tab_strip_model()->GetActiveWebContents()->
501              GetController()));
502  LOG(INFO) << "Reloading.";
503  chrome::Reload(browser(), CURRENT_TAB);
504  reload_observer.Wait();
505  LOG(INFO) << "Reloading - done.";
506  EXPECT_TRUE(process_map->Contains(
507      contents->GetRenderProcessHost()->GetID()));
508
509  // Disable app and reload the page.
510  LOG(INFO) << "Disabling extension.";
511  DisableExtension(app->id());
512  LOG(INFO) << "Disabling extension - done.";
513  content::WindowedNotificationObserver reload_observer2(
514      content::NOTIFICATION_LOAD_STOP,
515      content::Source<NavigationController>(
516          &browser()->tab_strip_model()->GetActiveWebContents()->
517              GetController()));
518  LOG(INFO) << "Reloading.";
519  chrome::Reload(browser(), CURRENT_TAB);
520  reload_observer2.Wait();
521  LOG(INFO) << "Reloading - done.";
522  EXPECT_FALSE(process_map->Contains(
523      contents->GetRenderProcessHost()->GetID()));
524}
525
526// Ensure that reloading a URL with JavaScript after installing or uninstalling
527// it as an app correctly swaps the process.  (http://crbug.com/80621)
528//
529// Crashes on Windows and Mac. http://crbug.com/238670
530// Added logging to help diagnose the location of the problem.
531IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
532  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
533      browser()->profile())->extension_service()->process_map();
534
535  host_resolver()->AddRule("*", "127.0.0.1");
536  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
537
538  // The app under test acts on URLs whose host is "localhost",
539  // so the URLs we navigate to must have host "localhost".
540  GURL base_url = GetTestBaseURL("app_process");
541
542  // Load app, disable it, and navigate to the page.
543  LOG(INFO) << "Loading extension.";
544  const Extension* app =
545      LoadExtension(test_data_dir_.AppendASCII("app_process"));
546  LOG(INFO) << "Loading extension - done.";
547  ASSERT_TRUE(app);
548  LOG(INFO) << "Disabling extension.";
549  DisableExtension(app->id());
550  LOG(INFO) << "Disabling extension - done.";
551  LOG(INFO) << "Navigate to path1/empty.html.";
552  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
553  LOG(INFO) << "Navigate to path1/empty.html - done.";
554  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
555  EXPECT_FALSE(process_map->Contains(
556      contents->GetRenderProcessHost()->GetID()));
557
558  // Enable app and reload via JavaScript.
559  LOG(INFO) << "Enabling extension.";
560  EnableExtension(app->id());
561  LOG(INFO) << "Enabling extension - done.";
562  content::WindowedNotificationObserver js_reload_observer(
563      content::NOTIFICATION_LOAD_STOP,
564      content::Source<NavigationController>(
565          &browser()->tab_strip_model()->GetActiveWebContents()->
566              GetController()));
567  LOG(INFO) << "Executing location.reload().";
568  ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();"));
569  js_reload_observer.Wait();
570  LOG(INFO) << "Executing location.reload() - done.";
571  EXPECT_TRUE(process_map->Contains(
572      contents->GetRenderProcessHost()->GetID()));
573
574  // Disable app and reload via JavaScript.
575  LOG(INFO) << "Disabling extension.";
576  DisableExtension(app->id());
577  LOG(INFO) << "Disabling extension - done.";
578  content::WindowedNotificationObserver js_reload_observer2(
579      content::NOTIFICATION_LOAD_STOP,
580      content::Source<NavigationController>(
581          &browser()->tab_strip_model()->GetActiveWebContents()->
582              GetController()));
583  LOG(INFO) << "Executing location = location.";
584  ASSERT_TRUE(content::ExecuteScript(contents, "location = location;"));
585  js_reload_observer2.Wait();
586  LOG(INFO) << "Executing location = location - done.";
587  EXPECT_FALSE(process_map->Contains(
588      contents->GetRenderProcessHost()->GetID()));
589}
590
591namespace {
592
593void RenderViewHostCreated(std::vector<content::RenderViewHost*>* rvh_vector,
594                           content::RenderViewHost* rvh) {
595  rvh_vector->push_back(rvh);
596}
597
598}  // namespace
599
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.
605IN_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
611
612  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
613      browser()->profile())->extension_service()->process_map();
614
615  host_resolver()->AddRule("*", "127.0.0.1");
616  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
617
618  GURL base_url = GetTestBaseURL("app_process");
619
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);
624
625  std::vector<content::RenderViewHost*> rvh_vector;
626  content::RenderViewHost::CreatedCallback rvh_callback(
627      base::Bind(&RenderViewHostCreated, &rvh_vector));
628  content::RenderViewHost::AddCreatedCallback(rvh_callback);
629  ui_test_utils::NavigateToURL(browser(),
630                               base_url.Resolve("path3/container.html"));
631  content::RenderViewHost::RemoveCreatedCallback(rvh_callback);
632  EXPECT_FALSE(process_map->Contains(
633      browser()->tab_strip_model()->GetWebContentsAt(0)->
634          GetRenderProcessHost()->GetID()));
635
636  // Popup window should be in the app's process.
637  ASSERT_EQ(3U, rvh_vector.size());
638  RenderViewHost* popup_host = rvh_vector[2];
639  EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID()));
640}
641
642// Similar to the previous test, but ensure that popup blocking bypass
643// isn't granted to the iframe.  See crbug.com/117446.
644#if defined(OS_CHROMEOS)
645// http://crbug.com/153513
646#define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe
647#else
648#define MAYBE_OpenAppFromIframe OpenAppFromIframe
649#endif
650IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) {
651  host_resolver()->AddRule("*", "127.0.0.1");
652  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
653
654  // Load app and start URL (not in the app).
655  const Extension* app =
656      LoadExtension(test_data_dir_.AppendASCII("app_process"));
657  ASSERT_TRUE(app);
658
659  ui_test_utils::NavigateToURL(
660      browser(), GetTestBaseURL("app_process").Resolve("path3/container.html"));
661
662  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
663  BlockedContentTabHelper* blocked_content_tab_helper =
664      BlockedContentTabHelper::FromWebContents(tab);
665  PopupBlockerTabHelper* popup_blocker_tab_helper =
666      PopupBlockerTabHelper::FromWebContents(tab);
667  if (!blocked_content_tab_helper->GetBlockedContentsCount() &&
668      (!popup_blocker_tab_helper ||
669       !popup_blocker_tab_helper->GetBlockedPopupsCount())) {
670    content::WindowedNotificationObserver observer(
671        chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
672        content::NotificationService::AllSources());
673    observer.Wait();
674  }
675
676  EXPECT_EQ(1u,
677            blocked_content_tab_helper->GetBlockedContentsCount() +
678                (popup_blocker_tab_helper
679                     ? popup_blocker_tab_helper->GetBlockedPopupsCount()
680                     : 0));
681}
682
683// Tests that if an extension launches an app via chrome.tabs.create with an URL
684// that's not in the app's extent but that server redirects to it, we still end
685// up with an app process. See http://crbug.com/99349 for more details.
686IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
687  host_resolver()->AddRule("*", "127.0.0.1");
688  ASSERT_TRUE(StartEmbeddedTestServer());
689
690  LoadExtension(test_data_dir_.AppendASCII("app_process"));
691  const Extension* launcher =
692      LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
693
694  // There should be two navigations by the time the app page is loaded.
695  // 1. The extension launcher page.
696  // 2. The app's URL (which includes a server redirect).
697  // Note that the server redirect does not generate a navigation event.
698  content::TestNavigationObserver test_navigation_observer(
699      browser()->tab_strip_model()->GetActiveWebContents(),
700      2);
701  test_navigation_observer.StartWatchingNewWebContents();
702
703  // Load the launcher extension, which should launch the app.
704  ui_test_utils::NavigateToURLWithDisposition(
705      browser(),
706      launcher->GetResourceURL("server_redirect.html"),
707      CURRENT_TAB,
708      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
709
710  // Wait for app tab to be created and loaded.
711  test_navigation_observer.WaitForObservation(
712      base::Bind(&content::RunMessageLoop),
713      base::Bind(&base::MessageLoop::Quit,
714                 base::Unretained(base::MessageLoopForUI::current())));
715
716  // App has loaded, and chrome.app.isInstalled should be true.
717  bool is_installed = false;
718  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
719      browser()->tab_strip_model()->GetActiveWebContents(),
720      "window.domAutomationController.send(chrome.app.isInstalled)",
721      &is_installed));
722  ASSERT_TRUE(is_installed);
723}
724
725// Tests that if an extension launches an app via chrome.tabs.create with an URL
726// that's not in the app's extent but that client redirects to it, we still end
727// up with an app process.
728IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
729  host_resolver()->AddRule("*", "127.0.0.1");
730  ASSERT_TRUE(StartEmbeddedTestServer());
731
732  LoadExtension(test_data_dir_.AppendASCII("app_process"));
733  const Extension* launcher =
734      LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
735
736  // There should be three navigations by the time the app page is loaded.
737  // 1. The extension launcher page.
738  // 2. The URL that the extension launches, which client redirects.
739  // 3. The app's URL.
740  content::TestNavigationObserver test_navigation_observer(
741      browser()->tab_strip_model()->GetActiveWebContents(),
742      3);
743  test_navigation_observer.StartWatchingNewWebContents();
744
745  // Load the launcher extension, which should launch the app.
746  ui_test_utils::NavigateToURLWithDisposition(
747      browser(),
748      launcher->GetResourceURL("client_redirect.html"),
749      CURRENT_TAB,
750      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
751
752  // Wait for app tab to be created and loaded.
753  test_navigation_observer.WaitForObservation(
754      base::Bind(&content::RunMessageLoop),
755      base::Bind(&base::MessageLoop::Quit,
756                 base::Unretained(base::MessageLoopForUI::current())));
757
758  // App has loaded, and chrome.app.isInstalled should be true.
759  bool is_installed = false;
760  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
761      browser()->tab_strip_model()->GetActiveWebContents(),
762      "window.domAutomationController.send(chrome.app.isInstalled)",
763      &is_installed));
764  ASSERT_TRUE(is_installed);
765}
766
767// Tests that if we have an app process (path1/container.html) with a non-app
768// iframe (path3/iframe.html), then opening a link from that iframe to a new
769// window to a same-origin non-app URL (path3/empty.html) should keep the window
770// in the app process.
771// This is in contrast to OpenAppFromIframe, since here the popup will not be
772// missing special permissions and should be scriptable from the iframe.
773// See http://crbug.com/92669 for more details.
774IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
775  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
776      browser()->profile())->extension_service()->process_map();
777
778  host_resolver()->AddRule("*", "127.0.0.1");
779  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
780
781  GURL base_url = GetTestBaseURL("app_process");
782
783  // Load app and start URL (in the app).
784  const Extension* app =
785      LoadExtension(test_data_dir_.AppendASCII("app_process"));
786  ASSERT_TRUE(app);
787
788  std::vector<content::RenderViewHost*> rvh_vector;
789  content::RenderViewHost::CreatedCallback rvh_callback(
790      base::Bind(&RenderViewHostCreated, &rvh_vector));
791  content::RenderViewHost::AddCreatedCallback(rvh_callback);
792  ui_test_utils::NavigateToURL(browser(),
793                               base_url.Resolve("path1/container.html"));
794  content::RenderViewHost::RemoveCreatedCallback(rvh_callback);
795  content::RenderProcessHost* process =
796      browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
797  EXPECT_TRUE(process_map->Contains(process->GetID()));
798
799  // Popup window should be in the app's process.
800  ASSERT_EQ(2U, rvh_vector.size());
801  RenderViewHost* popup_host = rvh_vector[1];
802  EXPECT_EQ(process, popup_host->GetProcess());
803}
804
805// http://crbug.com/118502
806#if defined(OS_MACOSX) || defined(OS_LINUX)
807#define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash
808#else
809#define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash
810#endif
811IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) {
812  extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
813      browser()->profile())->extension_service()->process_map();
814
815  host_resolver()->AddRule("*", "127.0.0.1");
816  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
817
818  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
819
820  GURL base_url = GetTestBaseURL("app_process");
821
822  // Load the app, chrome.app.isInstalled should be true.
823  ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
824  WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
825  EXPECT_TRUE(process_map->Contains(
826      contents->GetRenderProcessHost()->GetID()));
827  bool is_installed = false;
828  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
829      contents,
830      "window.domAutomationController.send(chrome.app.isInstalled)",
831      &is_installed));
832  ASSERT_TRUE(is_installed);
833
834  // Crash the tab and reload it, chrome.app.isInstalled should still be true.
835  content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
836  content::WindowedNotificationObserver observer(
837      content::NOTIFICATION_LOAD_STOP,
838      content::Source<NavigationController>(
839          &browser()->tab_strip_model()->GetActiveWebContents()->
840              GetController()));
841  chrome::Reload(browser(), CURRENT_TAB);
842  observer.Wait();
843  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
844      contents,
845      "window.domAutomationController.send(chrome.app.isInstalled)",
846      &is_installed));
847  ASSERT_TRUE(is_installed);
848}
849