isolated_app_browsertest.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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/strings/stringprintf.h"
6#include "base/strings/utf_string_conversions.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/browser.h"
11#include "chrome/browser/ui/browser_commands.h"
12#include "chrome/browser/ui/tabs/tab_strip_model.h"
13#include "chrome/common/url_constants.h"
14#include "chrome/test/base/ui_test_utils.h"
15#include "content/public/browser/render_process_host.h"
16#include "content/public/browser/render_view_host.h"
17#include "content/public/browser/site_instance.h"
18#include "content/public/browser/web_contents.h"
19#include "content/public/test/browser_test_utils.h"
20#include "extensions/browser/extension_host.h"
21#include "extensions/browser/process_map.h"
22#include "extensions/common/switches.h"
23#include "net/dns/mock_host_resolver.h"
24#include "net/test/embedded_test_server/embedded_test_server.h"
25#include "net/test/embedded_test_server/http_request.h"
26#include "net/test/embedded_test_server/http_response.h"
27
28using content::ExecuteScript;
29using content::ExecuteScriptAndExtractString;
30using content::NavigationController;
31using content::WebContents;
32using content::RenderViewHost;
33
34namespace {
35
36std::string WrapForJavascriptAndExtract(const char* javascript_expression) {
37  return std::string("window.domAutomationController.send(") +
38      javascript_expression + ")";
39}
40
41scoped_ptr<net::test_server::HttpResponse> HandleExpectAndSetCookieRequest(
42    const net::test_server::EmbeddedTestServer* test_server,
43    const net::test_server::HttpRequest& request) {
44  if (!StartsWithASCII(request.relative_url, "/expect-and-set-cookie?", true))
45    return scoped_ptr<net::test_server::HttpResponse>();
46
47  scoped_ptr<net::test_server::BasicHttpResponse> http_response(
48      new net::test_server::BasicHttpResponse);
49  http_response->set_code(net::HTTP_OK);
50
51  std::string request_cookies;
52  std::map<std::string, std::string>::const_iterator it =
53      request.headers.find("Cookie");
54  if (it != request.headers.end())
55    request_cookies = it->second;
56
57  size_t query_string_pos = request.relative_url.find('?');
58  std::string query_string =
59      request.relative_url.substr(query_string_pos + 1);
60  url_parse::Component query(0, query_string.length()), key_pos, value_pos;
61  bool expectations_satisfied = true;
62  std::vector<std::string> cookies_to_set;
63  while (url_parse::ExtractQueryKeyValue(
64             query_string.c_str(), &query, &key_pos, &value_pos)) {
65    std::string escaped_key(query_string.substr(key_pos.begin, key_pos.len));
66    std::string escaped_value(
67        query_string.substr(value_pos.begin, value_pos.len));
68
69    std::string key =
70        net::UnescapeURLComponent(escaped_key,
71                                  net::UnescapeRule::NORMAL |
72                                  net::UnescapeRule::SPACES |
73                                  net::UnescapeRule::URL_SPECIAL_CHARS);
74
75    std::string value =
76        net::UnescapeURLComponent(escaped_value,
77                                  net::UnescapeRule::NORMAL |
78                                  net::UnescapeRule::SPACES |
79                                  net::UnescapeRule::URL_SPECIAL_CHARS);
80
81    if (key == "expect") {
82      if (request_cookies.find(value) == std::string::npos)
83        expectations_satisfied = false;
84    } else if (key == "set") {
85      cookies_to_set.push_back(value);
86    } else {
87      return scoped_ptr<net::test_server::HttpResponse>();
88    }
89  }
90
91  if (expectations_satisfied) {
92    for (size_t i = 0; i < cookies_to_set.size(); i++)
93      http_response->AddCustomHeader("Set-Cookie", cookies_to_set[i]);
94  }
95
96  return http_response.PassAs<net::test_server::HttpResponse>();
97}
98
99class IsolatedAppTest : public ExtensionBrowserTest {
100 public:
101  // Returns whether the given tab's current URL has the given cookie.
102  bool WARN_UNUSED_RESULT HasCookie(WebContents* contents, std::string cookie) {
103    int value_size;
104    std::string actual_cookie;
105    ui_test_utils::GetCookies(contents->GetURL(), contents, &value_size,
106                              &actual_cookie);
107    return actual_cookie.find(cookie) != std::string::npos;
108  }
109
110  const extensions::Extension* GetInstalledApp(WebContents* contents) {
111    const extensions::Extension* installed_app = NULL;
112    Profile* profile =
113        Profile::FromBrowserContext(contents->GetBrowserContext());
114    ExtensionService* service = profile->GetExtensionService();
115    if (service) {
116      std::set<std::string> extension_ids =
117          extensions::ProcessMap::Get(profile)->GetExtensionsInProcess(
118              contents->GetRenderViewHost()->GetProcess()->GetID());
119      for (std::set<std::string>::iterator iter = extension_ids.begin();
120           iter != extension_ids.end(); ++iter) {
121        installed_app = service->extensions()->GetByID(*iter);
122        if (installed_app && installed_app->is_app())
123          return installed_app;
124      }
125    }
126    return NULL;
127  }
128
129 private:
130  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
131    ExtensionBrowserTest::SetUpCommandLine(command_line);
132    command_line->AppendSwitch(
133        extensions::switches::kEnableExperimentalExtensionApis);
134  }
135};
136
137}  // namespace
138
139IN_PROC_BROWSER_TEST_F(IsolatedAppTest, CrossProcessClientRedirect) {
140  host_resolver()->AddRule("*", "127.0.0.1");
141  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
142
143  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
144  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
145
146  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
147  GURL::Replacements replace_host;
148  std::string host_str("localhost");  // Must stay in scope with replace_host.
149  replace_host.SetHostStr(host_str);
150  base_url = base_url.ReplaceComponents(replace_host);
151  ui_test_utils::NavigateToURLWithDisposition(
152      browser(), base_url.Resolve("app1/main.html"),
153      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
154
155  // Redirect to app2.
156  GURL redirect_url(embedded_test_server()->GetURL(
157      "/extensions/isolated_apps/app2/redirect.html"));
158  ui_test_utils::NavigateToURLWithDisposition(
159      browser(), redirect_url,
160      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
161
162  // Go back twice.
163  // If bug fixed, we cannot go back anymore.
164  // If not fixed, we will redirect back to app2 and can go back again.
165  EXPECT_TRUE(chrome::CanGoBack(browser()));
166  chrome::GoBack(browser(), CURRENT_TAB);
167  EXPECT_TRUE(chrome::CanGoBack(browser()));
168  chrome::GoBack(browser(), CURRENT_TAB);
169  EXPECT_FALSE(chrome::CanGoBack(browser()));
170
171  // We also need to test script-initialized navigation (document.location.href)
172  // happened after page finishes loading. This one will also triggered the
173  // willPerformClientRedirect hook in RenderViewImpl but should not replace
174  // the previous history entry.
175  ui_test_utils::NavigateToURLWithDisposition(
176      browser(), base_url.Resolve("non_app/main.html"),
177      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
178
179  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(1);
180
181  // Using JavaScript to navigate to app2 page,
182  // after the non_app page has finished loading.
183  content::WindowedNotificationObserver observer1(
184      content::NOTIFICATION_LOAD_STOP,
185      content::Source<NavigationController>(
186          &browser()->tab_strip_model()->GetActiveWebContents()->
187              GetController()));
188  std::string script = base::StringPrintf(
189        "document.location.href=\"%s\";",
190        base_url.Resolve("app2/main.html").spec().c_str());
191  EXPECT_TRUE(ExecuteScript(tab0, script));
192  observer1.Wait();
193
194  // This kind of navigation should not replace previous navigation entry.
195  EXPECT_TRUE(chrome::CanGoBack(browser()));
196  chrome::GoBack(browser(), CURRENT_TAB);
197  EXPECT_FALSE(chrome::CanGoBack(browser()));
198}
199
200// Tests that cookies set within an isolated app are not visible to normal
201// pages or other apps.
202//
203// TODO(ajwong): Also test what happens if an app spans multiple sites in its
204// extent.  These origins should also be isolated, but still have origin-based
205// separation as you would expect.
206//
207// This test is disabled due to being flaky. http://crbug.com/86562
208#if defined(OS_WIN)
209#define MAYBE_CookieIsolation DISABLED_CookieIsolation
210#else
211#define MAYBE_CookieIsolation CookieIsolation
212#endif
213IN_PROC_BROWSER_TEST_F(IsolatedAppTest, MAYBE_CookieIsolation) {
214  host_resolver()->AddRule("*", "127.0.0.1");
215  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
216
217  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
218  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
219
220  // The app under test acts on URLs whose host is "localhost",
221  // so the URLs we navigate to must have host "localhost".
222  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
223  GURL::Replacements replace_host;
224  std::string host_str("localhost");  // Must stay in scope with replace_host.
225  replace_host.SetHostStr(host_str);
226  base_url = base_url.ReplaceComponents(replace_host);
227
228  ui_test_utils::NavigateToURLWithDisposition(
229      browser(), base_url.Resolve("app1/main.html"),
230      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
231  ui_test_utils::NavigateToURLWithDisposition(
232      browser(), base_url.Resolve("app2/main.html"),
233      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
234  ui_test_utils::NavigateToURLWithDisposition(
235      browser(), base_url.Resolve("non_app/main.html"),
236      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
237
238  ASSERT_EQ(3, browser()->tab_strip_model()->count());
239
240  // Ensure first two tabs have installed apps.
241  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(0);
242  WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
243  WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
244  ASSERT_TRUE(GetInstalledApp(tab0));
245  ASSERT_TRUE(GetInstalledApp(tab1));
246  ASSERT_TRUE(!GetInstalledApp(tab2));
247
248  // Check that tabs see cannot each other's localStorage even though they are
249  // in the same origin.
250  ASSERT_TRUE(ExecuteScript(
251      tab0, "window.localStorage.setItem('testdata', 'ls_app1');"));
252  ASSERT_TRUE(ExecuteScript(
253      tab1, "window.localStorage.setItem('testdata', 'ls_app2');"));
254  ASSERT_TRUE(ExecuteScript(
255      tab2, "window.localStorage.setItem('testdata', 'ls_normal');"));
256
257  const std::string& kRetrieveLocalStorage =
258      WrapForJavascriptAndExtract(
259          "window.localStorage.getItem('testdata') || 'badval'");
260  std::string result;
261  ASSERT_TRUE(ExecuteScriptAndExtractString(
262      tab0, kRetrieveLocalStorage.c_str(), &result));
263  EXPECT_EQ("ls_app1", result);
264  ASSERT_TRUE(ExecuteScriptAndExtractString(
265      tab1, kRetrieveLocalStorage.c_str(), &result));
266  EXPECT_EQ("ls_app2", result);
267  ASSERT_TRUE(ExecuteScriptAndExtractString(
268      tab2, kRetrieveLocalStorage.c_str(), &result));
269  EXPECT_EQ("ls_normal", result);
270
271  // Check that each tab sees its own cookie.
272  EXPECT_TRUE(HasCookie(tab0, "app1=3"));
273  EXPECT_TRUE(HasCookie(tab1, "app2=4"));
274  EXPECT_TRUE(HasCookie(tab2, "normalPage=5"));
275
276  // Check that app1 tab cannot see the other cookies.
277  EXPECT_FALSE(HasCookie(tab0, "app2"));
278  EXPECT_FALSE(HasCookie(tab0, "normalPage"));
279
280  // Check that app2 tab cannot see the other cookies.
281  EXPECT_FALSE(HasCookie(tab1, "app1"));
282  EXPECT_FALSE(HasCookie(tab1, "normalPage"));
283
284  // Check that normal tab cannot see the other cookies.
285  EXPECT_FALSE(HasCookie(tab2, "app1"));
286  EXPECT_FALSE(HasCookie(tab2, "app2"));
287
288  // Check that the non_app iframe cookie is associated with app1 and not the
289  // normal tab.  (For now, iframes are always rendered in their parent
290  // process, even if they aren't in the app manifest.)
291  EXPECT_TRUE(HasCookie(tab0, "nonAppFrame=6"));
292  EXPECT_FALSE(HasCookie(tab2, "nonAppFrame"));
293
294  // Check that isolation persists even if the tab crashes and is reloaded.
295  chrome::SelectNumberedTab(browser(), 0);
296  content::CrashTab(tab0);
297  content::WindowedNotificationObserver observer(
298      content::NOTIFICATION_LOAD_STOP,
299      content::Source<NavigationController>(
300          &browser()->tab_strip_model()->GetActiveWebContents()->
301              GetController()));
302  chrome::Reload(browser(), CURRENT_TAB);
303  observer.Wait();
304  EXPECT_TRUE(HasCookie(tab0, "app1=3"));
305  EXPECT_FALSE(HasCookie(tab0, "app2"));
306  EXPECT_FALSE(HasCookie(tab0, "normalPage"));
307
308}
309
310// This test is disabled due to being flaky. http://crbug.com/145588
311// Ensure that cookies are not isolated if the isolated apps are not installed.
312IN_PROC_BROWSER_TEST_F(IsolatedAppTest, DISABLED_NoCookieIsolationWithoutApp) {
313  host_resolver()->AddRule("*", "127.0.0.1");
314  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
315
316  // The app under test acts on URLs whose host is "localhost",
317  // so the URLs we navigate to must have host "localhost".
318  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
319  GURL::Replacements replace_host;
320  std::string host_str("localhost");  // Must stay in scope with replace_host.
321  replace_host.SetHostStr(host_str);
322  base_url = base_url.ReplaceComponents(replace_host);
323
324  ui_test_utils::NavigateToURLWithDisposition(
325      browser(), base_url.Resolve("app1/main.html"),
326      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
327  ui_test_utils::NavigateToURLWithDisposition(
328      browser(), base_url.Resolve("app2/main.html"),
329      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
330  ui_test_utils::NavigateToURLWithDisposition(
331      browser(), base_url.Resolve("non_app/main.html"),
332      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
333
334  ASSERT_EQ(3, browser()->tab_strip_model()->count());
335
336  // Check that tabs see each other's cookies.
337  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
338                        "app2=4"));
339  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
340                        "normalPage=5"));
341  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
342                        "nonAppFrame=6"));
343  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
344                        "app1=3"));
345  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
346                        "normalPage=5"));
347  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
348                        "nonAppFrame=6"));
349  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
350                        "app1=3"));
351  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
352                        "app2=4"));
353  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
354                        "nonAppFrame=6"));
355
356  // Check that all tabs share the same localStorage if they have the same
357  // origin.
358  WebContents* app1_wc = browser()->tab_strip_model()->GetWebContentsAt(0);
359  WebContents* app2_wc = browser()->tab_strip_model()->GetWebContentsAt(1);
360  WebContents* non_app_wc = browser()->tab_strip_model()->GetWebContentsAt(2);
361  ASSERT_TRUE(ExecuteScript(
362      app1_wc, "window.localStorage.setItem('testdata', 'ls_app1');"));
363  ASSERT_TRUE(ExecuteScript(
364      app2_wc, "window.localStorage.setItem('testdata', 'ls_app2');"));
365  ASSERT_TRUE(ExecuteScript(
366      non_app_wc, "window.localStorage.setItem('testdata', 'ls_normal');"));
367
368  const std::string& kRetrieveLocalStorage =
369      WrapForJavascriptAndExtract("window.localStorage.getItem('testdata')");
370  std::string result;
371  ASSERT_TRUE(ExecuteScriptAndExtractString(
372      app1_wc, kRetrieveLocalStorage.c_str(), &result));
373  EXPECT_EQ("ls_normal", result);
374  ASSERT_TRUE(ExecuteScriptAndExtractString(
375      app2_wc, kRetrieveLocalStorage.c_str(), &result));
376  EXPECT_EQ("ls_normal", result);
377  ASSERT_TRUE(ExecuteScriptAndExtractString(
378      non_app_wc, kRetrieveLocalStorage.c_str(), &result));
379  EXPECT_EQ("ls_normal", result);
380}
381
382// Test timing out on Windows debug bots.
383// http://crbug.com/174926
384#if defined(OS_WIN) && !defined(NDEBUG)
385#define MAYBE_SubresourceCookieIsolation DISABLED_SubresourceCookieIsolation
386#else
387#define MAYBE_SubresourceCookieIsolation SubresourceCookieIsolation
388#endif  // defined(OS_WIN) && !defined(NDEBUG)
389
390// Tests that subresource and media requests use the app's cookie store.
391// See http://crbug.com/141172.
392IN_PROC_BROWSER_TEST_F(IsolatedAppTest, MAYBE_SubresourceCookieIsolation) {
393  embedded_test_server()->RegisterRequestHandler(
394      base::Bind(&HandleExpectAndSetCookieRequest, embedded_test_server()));
395
396  host_resolver()->AddRule("*", "127.0.0.1");
397  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
398
399  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
400
401  // The app under test acts on URLs whose host is "localhost",
402  // so the URLs we navigate to must have host "localhost".
403  GURL root_url = embedded_test_server()->GetURL("/");
404  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
405  GURL::Replacements replace_host;
406  std::string host_str("localhost");  // Must stay in scope with replace_host.
407  replace_host.SetHostStr(host_str);
408  root_url = root_url.ReplaceComponents(replace_host);
409  base_url = base_url.ReplaceComponents(replace_host);
410
411  // First set cookies inside and outside the app.
412  ui_test_utils::NavigateToURLWithDisposition(
413      browser(), root_url.Resolve("expect-and-set-cookie?set=nonApp%3d1"),
414      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
415  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(0);
416  ASSERT_FALSE(GetInstalledApp(tab0));
417  ui_test_utils::NavigateToURLWithDisposition(
418      browser(), base_url.Resolve("app1/main.html"),
419      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
420  WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
421  ASSERT_TRUE(GetInstalledApp(tab1));
422
423  // Check that each tab sees its own cookie.
424  EXPECT_TRUE(HasCookie(tab0, "nonApp=1"));
425  EXPECT_FALSE(HasCookie(tab0, "app1=3"));
426  EXPECT_FALSE(HasCookie(tab1, "nonApp=1"));
427  EXPECT_TRUE(HasCookie(tab1, "app1=3"));
428
429  // Now visit an app page that loads subresources located outside the app.
430  // For both images and video tags, it loads two URLs:
431  //  - One will set nonApp{Media,Image}=1 cookies if nonApp=1 is set.
432  //  - One will set app1{Media,Image}=1 cookies if app1=3 is set.
433  // We expect only the app's cookies to be present.
434  // We must wait for the onload event, to allow the subresources to finish.
435  content::WindowedNotificationObserver observer(
436      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
437      content::Source<WebContents>(
438          browser()->tab_strip_model()->GetActiveWebContents()));
439  ui_test_utils::NavigateToURLWithDisposition(
440      browser(), base_url.Resolve("app1/app_subresources.html"),
441      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
442  observer.Wait();
443  EXPECT_FALSE(HasCookie(tab1, "nonAppMedia=1"));
444  EXPECT_TRUE(HasCookie(tab1, "app1Media=1"));
445  EXPECT_FALSE(HasCookie(tab1, "nonAppImage=1"));
446  EXPECT_TRUE(HasCookie(tab1, "app1Image=1"));
447
448  // Also create a non-app tab to ensure no new cookies were set in that jar.
449  ui_test_utils::NavigateToURLWithDisposition(
450      browser(), root_url,
451      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
452  WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
453  EXPECT_FALSE(HasCookie(tab2, "nonAppMedia=1"));
454  EXPECT_FALSE(HasCookie(tab2, "app1Media=1"));
455  EXPECT_FALSE(HasCookie(tab2, "nonAppImage=1"));
456  EXPECT_FALSE(HasCookie(tab2, "app1Image=1"));
457}
458
459// Test is flaky on Windows.
460// http://crbug.com/247667
461#if defined(OS_WIN)
462#define MAYBE_IsolatedAppProcessModel DISABLED_IsolatedAppProcessModel
463#else
464#define MAYBE_IsolatedAppProcessModel IsolatedAppProcessModel
465#endif  // defined(OS_WIN)
466
467// Tests that isolated apps processes do not render top-level non-app pages.
468// This is true even in the case of the OAuth workaround for hosted apps,
469// where non-app popups may be kept in the hosted app process.
470IN_PROC_BROWSER_TEST_F(IsolatedAppTest, MAYBE_IsolatedAppProcessModel) {
471  host_resolver()->AddRule("*", "127.0.0.1");
472  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
473
474  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
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 = embedded_test_server()->GetURL("/extensions/isolated_apps/");
479  GURL::Replacements replace_host;
480  std::string host_str("localhost");  // Must stay in scope with replace_host.
481  replace_host.SetHostStr(host_str);
482  base_url = base_url.ReplaceComponents(replace_host);
483
484  // Create three tabs in the isolated app in different ways.
485  ui_test_utils::NavigateToURLWithDisposition(
486      browser(), base_url.Resolve("app1/main.html"),
487      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
488  ui_test_utils::NavigateToURLWithDisposition(
489      browser(), base_url.Resolve("app1/main.html"),
490      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
491  // For the third tab, use window.open to keep it in process with an opener.
492  OpenWindow(browser()->tab_strip_model()->GetWebContentsAt(0),
493             base_url.Resolve("app1/main.html"), true, NULL);
494
495  // In a fourth tab, use window.open to a non-app URL.  It should open in a
496  // separate process, even though this would trigger the OAuth workaround
497  // for hosted apps (from http://crbug.com/59285).
498  OpenWindow(browser()->tab_strip_model()->GetWebContentsAt(0),
499             base_url.Resolve("non_app/main.html"), false, NULL);
500
501  // We should now have four tabs, the first and third sharing a process.
502  // The second one is an independent instance in a separate process.
503  ASSERT_EQ(4, browser()->tab_strip_model()->count());
504  int process_id_0 = browser()->tab_strip_model()->GetWebContentsAt(0)->
505      GetRenderProcessHost()->GetID();
506  int process_id_1 = browser()->tab_strip_model()->GetWebContentsAt(1)->
507      GetRenderProcessHost()->GetID();
508  EXPECT_NE(process_id_0, process_id_1);
509  EXPECT_EQ(process_id_0,
510            browser()->tab_strip_model()->GetWebContentsAt(2)->
511                GetRenderProcessHost()->GetID());
512  EXPECT_NE(process_id_0,
513            browser()->tab_strip_model()->GetWebContentsAt(3)->
514                GetRenderProcessHost()->GetID());
515
516  // Navigating the second tab out of the app should cause a process swap.
517  const GURL& non_app_url(base_url.Resolve("non_app/main.html"));
518  NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(1),
519                     non_app_url);
520  EXPECT_NE(process_id_1,
521            browser()->tab_strip_model()->GetWebContentsAt(1)->
522                GetRenderProcessHost()->GetID());
523}
524
525// This test no longer passes, since we don't properly isolate sessionStorage
526// for isolated apps. This was broken as part of the changes for storage
527// partition support for webview tags.
528// TODO(nasko): If isolated apps is no longer developed, this test should be
529// removed. http://crbug.com/159932
530IN_PROC_BROWSER_TEST_F(IsolatedAppTest, DISABLED_SessionStorage) {
531  host_resolver()->AddRule("*", "127.0.0.1");
532  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
533
534  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
535  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
536
537  // The app under test acts on URLs whose host is "localhost",
538  // so the URLs we navigate to must have host "localhost".
539  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
540  GURL::Replacements replace_host;
541  std::string host_str("localhost");  // Must stay in scope with replace_host.
542  replace_host.SetHostStr(host_str);
543  base_url = base_url.ReplaceComponents(replace_host);
544
545  // Enter some state into sessionStorage three times on the same origin, but
546  // for three URLs that correspond to app1, app2, and a non-isolated site.
547  ui_test_utils::NavigateToURLWithDisposition(
548      browser(), base_url.Resolve("app1/main.html"),
549      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
550  ASSERT_TRUE(ExecuteScript(
551      browser()->tab_strip_model()->GetWebContentsAt(0),
552      "window.sessionStorage.setItem('testdata', 'ss_app1');"));
553
554  ui_test_utils::NavigateToURLWithDisposition(
555      browser(), base_url.Resolve("app2/main.html"),
556      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
557  ASSERT_TRUE(ExecuteScript(
558      browser()->tab_strip_model()->GetWebContentsAt(0),
559      "window.sessionStorage.setItem('testdata', 'ss_app2');"));
560
561  ui_test_utils::NavigateToURLWithDisposition(
562      browser(), base_url.Resolve("non_app/main.html"),
563      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
564  ASSERT_TRUE(ExecuteScript(
565      browser()->tab_strip_model()->GetWebContentsAt(0),
566      "window.sessionStorage.setItem('testdata', 'ss_normal');"));
567
568  // Now, ensure that the sessionStorage is correctly partitioned, and persists
569  // when we navigate around all over the dang place.
570  const std::string& kRetrieveSessionStorage =
571      WrapForJavascriptAndExtract(
572          "window.sessionStorage.getItem('testdata') || 'badval'");
573  std::string result;
574  ui_test_utils::NavigateToURLWithDisposition(
575      browser(), base_url.Resolve("app1/main.html"),
576      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
577  ASSERT_TRUE(ExecuteScriptAndExtractString(
578      browser()->tab_strip_model()->GetWebContentsAt(0),
579      kRetrieveSessionStorage.c_str(), &result));
580  EXPECT_EQ("ss_app1", result);
581
582  ui_test_utils::NavigateToURLWithDisposition(
583      browser(), base_url.Resolve("app2/main.html"),
584      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
585  ASSERT_TRUE(ExecuteScriptAndExtractString(
586      browser()->tab_strip_model()->GetWebContentsAt(0),
587      kRetrieveSessionStorage.c_str(), &result));
588  EXPECT_EQ("ss_app2", result);
589
590  ui_test_utils::NavigateToURLWithDisposition(
591      browser(), base_url.Resolve("non_app/main.html"),
592      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
593  ASSERT_TRUE(ExecuteScriptAndExtractString(
594      browser()->tab_strip_model()->GetWebContentsAt(0),
595      kRetrieveSessionStorage.c_str(), &result));
596  EXPECT_EQ("ss_normal", result);
597}
598