isolated_app_browsertest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/automation/automation_util.h"
8#include "chrome/browser/extensions/extension_apitest.h"
9#include "chrome/browser/extensions/extension_host.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/profiles/profile.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_commands.h"
14#include "chrome/browser/ui/tabs/tab_strip_model.h"
15#include "chrome/common/chrome_switches.h"
16#include "chrome/common/url_constants.h"
17#include "chrome/test/base/ui_test_utils.h"
18#include "content/public/browser/render_process_host.h"
19#include "content/public/browser/render_view_host.h"
20#include "content/public/browser/site_instance.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/test/browser_test_utils.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_response.h"
26#include "net/test/embedded_test_server/http_request.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    automation_util::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          service->process_map()->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(switches::kEnableExperimentalExtensionApis);
133  }
134};
135
136}  // namespace
137
138IN_PROC_BROWSER_TEST_F(IsolatedAppTest, CrossProcessClientRedirect) {
139  host_resolver()->AddRule("*", "127.0.0.1");
140  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
141
142  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
143  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
144
145  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
146  GURL::Replacements replace_host;
147  std::string host_str("localhost");  // Must stay in scope with replace_host.
148  replace_host.SetHostStr(host_str);
149  base_url = base_url.ReplaceComponents(replace_host);
150  ui_test_utils::NavigateToURLWithDisposition(
151      browser(), base_url.Resolve("app1/main.html"),
152      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
153
154  // Redirect to app2.
155  GURL redirect_url(embedded_test_server()->GetURL(
156      "/extensions/isolated_apps/app2/redirect.html"));
157  ui_test_utils::NavigateToURLWithDisposition(
158      browser(), redirect_url,
159      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
160
161  // Go back twice.
162  // If bug fixed, we cannot go back anymore.
163  // If not fixed, we will redirect back to app2 and can go back again.
164  EXPECT_TRUE(chrome::CanGoBack(browser()));
165  chrome::GoBack(browser(), CURRENT_TAB);
166  EXPECT_TRUE(chrome::CanGoBack(browser()));
167  chrome::GoBack(browser(), CURRENT_TAB);
168  EXPECT_FALSE(chrome::CanGoBack(browser()));
169
170  // We also need to test script-initialized navigation (document.location.href)
171  // happened after page finishes loading. This one will also triggered the
172  // willPerformClientRedirect hook in RenderViewImpl but should not replace
173  // the previous history entry.
174  ui_test_utils::NavigateToURLWithDisposition(
175      browser(), base_url.Resolve("non_app/main.html"),
176      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
177
178  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(1);
179
180  // Using JavaScript to navigate to app2 page,
181  // after the non_app page has finished loading.
182  content::WindowedNotificationObserver observer1(
183      content::NOTIFICATION_LOAD_STOP,
184      content::Source<NavigationController>(
185          &browser()->tab_strip_model()->GetActiveWebContents()->
186              GetController()));
187  std::string script = base::StringPrintf(
188        "document.location.href=\"%s\";",
189        base_url.Resolve("app2/main.html").spec().c_str());
190  EXPECT_TRUE(ExecuteScript(tab0, script));
191  observer1.Wait();
192
193  // This kind of navigation should not replace previous navigation entry.
194  EXPECT_TRUE(chrome::CanGoBack(browser()));
195  chrome::GoBack(browser(), CURRENT_TAB);
196  EXPECT_FALSE(chrome::CanGoBack(browser()));
197}
198
199// Tests that cookies set within an isolated app are not visible to normal
200// pages or other apps.
201//
202// TODO(ajwong): Also test what happens if an app spans multiple sites in its
203// extent.  These origins should also be isolated, but still have origin-based
204// separation as you would expect.
205IN_PROC_BROWSER_TEST_F(IsolatedAppTest, CookieIsolation) {
206  host_resolver()->AddRule("*", "127.0.0.1");
207  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
208
209  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
210  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
211
212  // The app under test acts on URLs whose host is "localhost",
213  // so the URLs we navigate to must have host "localhost".
214  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
215  GURL::Replacements replace_host;
216  std::string host_str("localhost");  // Must stay in scope with replace_host.
217  replace_host.SetHostStr(host_str);
218  base_url = base_url.ReplaceComponents(replace_host);
219
220  ui_test_utils::NavigateToURLWithDisposition(
221      browser(), base_url.Resolve("app1/main.html"),
222      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
223  ui_test_utils::NavigateToURLWithDisposition(
224      browser(), base_url.Resolve("app2/main.html"),
225      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
226  ui_test_utils::NavigateToURLWithDisposition(
227      browser(), base_url.Resolve("non_app/main.html"),
228      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
229
230  ASSERT_EQ(3, browser()->tab_strip_model()->count());
231
232  // Ensure first two tabs have installed apps.
233  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(0);
234  WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
235  WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
236  ASSERT_TRUE(GetInstalledApp(tab0));
237  ASSERT_TRUE(GetInstalledApp(tab1));
238  ASSERT_TRUE(!GetInstalledApp(tab2));
239
240  // Check that tabs see cannot each other's localStorage even though they are
241  // in the same origin.
242  ASSERT_TRUE(ExecuteScript(
243      tab0, "window.localStorage.setItem('testdata', 'ls_app1');"));
244  ASSERT_TRUE(ExecuteScript(
245      tab1, "window.localStorage.setItem('testdata', 'ls_app2');"));
246  ASSERT_TRUE(ExecuteScript(
247      tab2, "window.localStorage.setItem('testdata', 'ls_normal');"));
248
249  const std::string& kRetrieveLocalStorage =
250      WrapForJavascriptAndExtract(
251          "window.localStorage.getItem('testdata') || 'badval'");
252  std::string result;
253  ASSERT_TRUE(ExecuteScriptAndExtractString(
254      tab0, kRetrieveLocalStorage.c_str(), &result));
255  EXPECT_EQ("ls_app1", result);
256  ASSERT_TRUE(ExecuteScriptAndExtractString(
257      tab1, kRetrieveLocalStorage.c_str(), &result));
258  EXPECT_EQ("ls_app2", result);
259  ASSERT_TRUE(ExecuteScriptAndExtractString(
260      tab2, kRetrieveLocalStorage.c_str(), &result));
261  EXPECT_EQ("ls_normal", result);
262
263  // Check that each tab sees its own cookie.
264  EXPECT_TRUE(HasCookie(tab0, "app1=3"));
265  EXPECT_TRUE(HasCookie(tab1, "app2=4"));
266  EXPECT_TRUE(HasCookie(tab2, "normalPage=5"));
267
268  // Check that app1 tab cannot see the other cookies.
269  EXPECT_FALSE(HasCookie(tab0, "app2"));
270  EXPECT_FALSE(HasCookie(tab0, "normalPage"));
271
272  // Check that app2 tab cannot see the other cookies.
273  EXPECT_FALSE(HasCookie(tab1, "app1"));
274  EXPECT_FALSE(HasCookie(tab1, "normalPage"));
275
276  // Check that normal tab cannot see the other cookies.
277  EXPECT_FALSE(HasCookie(tab2, "app1"));
278  EXPECT_FALSE(HasCookie(tab2, "app2"));
279
280  // Check that the non_app iframe cookie is associated with app1 and not the
281  // normal tab.  (For now, iframes are always rendered in their parent
282  // process, even if they aren't in the app manifest.)
283  EXPECT_TRUE(HasCookie(tab0, "nonAppFrame=6"));
284  EXPECT_FALSE(HasCookie(tab2, "nonAppFrame"));
285
286  // Check that isolation persists even if the tab crashes and is reloaded.
287  chrome::SelectNumberedTab(browser(), 0);
288  content::CrashTab(tab0);
289  content::WindowedNotificationObserver observer(
290      content::NOTIFICATION_LOAD_STOP,
291      content::Source<NavigationController>(
292          &browser()->tab_strip_model()->GetActiveWebContents()->
293              GetController()));
294  chrome::Reload(browser(), CURRENT_TAB);
295  observer.Wait();
296  EXPECT_TRUE(HasCookie(tab0, "app1=3"));
297  EXPECT_FALSE(HasCookie(tab0, "app2"));
298  EXPECT_FALSE(HasCookie(tab0, "normalPage"));
299
300}
301
302// This test is disabled due to being flaky. http://crbug.com/145588
303// Ensure that cookies are not isolated if the isolated apps are not installed.
304IN_PROC_BROWSER_TEST_F(IsolatedAppTest, DISABLED_NoCookieIsolationWithoutApp) {
305  host_resolver()->AddRule("*", "127.0.0.1");
306  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
307
308  // The app under test acts on URLs whose host is "localhost",
309  // so the URLs we navigate to must have host "localhost".
310  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
311  GURL::Replacements replace_host;
312  std::string host_str("localhost");  // Must stay in scope with replace_host.
313  replace_host.SetHostStr(host_str);
314  base_url = base_url.ReplaceComponents(replace_host);
315
316  ui_test_utils::NavigateToURLWithDisposition(
317      browser(), base_url.Resolve("app1/main.html"),
318      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
319  ui_test_utils::NavigateToURLWithDisposition(
320      browser(), base_url.Resolve("app2/main.html"),
321      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
322  ui_test_utils::NavigateToURLWithDisposition(
323      browser(), base_url.Resolve("non_app/main.html"),
324      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
325
326  ASSERT_EQ(3, browser()->tab_strip_model()->count());
327
328  // Check that tabs see each other's cookies.
329  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
330                        "app2=4"));
331  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
332                        "normalPage=5"));
333  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(0),
334                        "nonAppFrame=6"));
335  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
336                        "app1=3"));
337  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
338                        "normalPage=5"));
339  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(1),
340                        "nonAppFrame=6"));
341  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
342                        "app1=3"));
343  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
344                        "app2=4"));
345  EXPECT_TRUE(HasCookie(browser()->tab_strip_model()->GetWebContentsAt(2),
346                        "nonAppFrame=6"));
347
348  // Check that all tabs share the same localStorage if they have the same
349  // origin.
350  WebContents* app1_wc = browser()->tab_strip_model()->GetWebContentsAt(0);
351  WebContents* app2_wc = browser()->tab_strip_model()->GetWebContentsAt(1);
352  WebContents* non_app_wc = browser()->tab_strip_model()->GetWebContentsAt(2);
353  ASSERT_TRUE(ExecuteScript(
354      app1_wc, "window.localStorage.setItem('testdata', 'ls_app1');"));
355  ASSERT_TRUE(ExecuteScript(
356      app2_wc, "window.localStorage.setItem('testdata', 'ls_app2');"));
357  ASSERT_TRUE(ExecuteScript(
358      non_app_wc, "window.localStorage.setItem('testdata', 'ls_normal');"));
359
360  const std::string& kRetrieveLocalStorage =
361      WrapForJavascriptAndExtract("window.localStorage.getItem('testdata')");
362  std::string result;
363  ASSERT_TRUE(ExecuteScriptAndExtractString(
364      app1_wc, kRetrieveLocalStorage.c_str(), &result));
365  EXPECT_EQ("ls_normal", result);
366  ASSERT_TRUE(ExecuteScriptAndExtractString(
367      app2_wc, kRetrieveLocalStorage.c_str(), &result));
368  EXPECT_EQ("ls_normal", result);
369  ASSERT_TRUE(ExecuteScriptAndExtractString(
370      non_app_wc, kRetrieveLocalStorage.c_str(), &result));
371  EXPECT_EQ("ls_normal", result);
372}
373
374// Test timing out on Windows debug bots.
375// http://crbug.com/174926
376#if defined(OS_WIN) && !defined(NDEBUG)
377#define MAYBE_SubresourceCookieIsolation DISABLED_SubresourceCookieIsolation
378#else
379#define MAYBE_SubresourceCookieIsolation SubresourceCookieIsolation
380#endif  // defined(OS_WIN) && !defined(NDEBUG)
381
382// Tests that subresource and media requests use the app's cookie store.
383// See http://crbug.com/141172.
384IN_PROC_BROWSER_TEST_F(IsolatedAppTest, MAYBE_SubresourceCookieIsolation) {
385  embedded_test_server()->RegisterRequestHandler(
386      base::Bind(&HandleExpectAndSetCookieRequest, embedded_test_server()));
387
388  host_resolver()->AddRule("*", "127.0.0.1");
389  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
390
391  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
392
393  // The app under test acts on URLs whose host is "localhost",
394  // so the URLs we navigate to must have host "localhost".
395  GURL root_url = embedded_test_server()->GetURL("/");
396  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
397  GURL::Replacements replace_host;
398  std::string host_str("localhost");  // Must stay in scope with replace_host.
399  replace_host.SetHostStr(host_str);
400  root_url = root_url.ReplaceComponents(replace_host);
401  base_url = base_url.ReplaceComponents(replace_host);
402
403  // First set cookies inside and outside the app.
404  ui_test_utils::NavigateToURLWithDisposition(
405      browser(), root_url.Resolve("expect-and-set-cookie?set=nonApp%3d1"),
406      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
407  WebContents* tab0 = browser()->tab_strip_model()->GetWebContentsAt(0);
408  ASSERT_FALSE(GetInstalledApp(tab0));
409  ui_test_utils::NavigateToURLWithDisposition(
410      browser(), base_url.Resolve("app1/main.html"),
411      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
412  WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
413  ASSERT_TRUE(GetInstalledApp(tab1));
414
415  // Check that each tab sees its own cookie.
416  EXPECT_TRUE(HasCookie(tab0, "nonApp=1"));
417  EXPECT_FALSE(HasCookie(tab0, "app1=3"));
418  EXPECT_FALSE(HasCookie(tab1, "nonApp=1"));
419  EXPECT_TRUE(HasCookie(tab1, "app1=3"));
420
421  // Now visit an app page that loads subresources located outside the app.
422  // For both images and video tags, it loads two URLs:
423  //  - One will set nonApp{Media,Image}=1 cookies if nonApp=1 is set.
424  //  - One will set app1{Media,Image}=1 cookies if app1=3 is set.
425  // We expect only the app's cookies to be present.
426  // We must wait for the onload event, to allow the subresources to finish.
427  content::WindowedNotificationObserver observer(
428      content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
429      content::Source<WebContents>(
430          browser()->tab_strip_model()->GetActiveWebContents()));
431  ui_test_utils::NavigateToURLWithDisposition(
432      browser(), base_url.Resolve("app1/app_subresources.html"),
433      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
434  observer.Wait();
435  EXPECT_FALSE(HasCookie(tab1, "nonAppMedia=1"));
436  EXPECT_TRUE(HasCookie(tab1, "app1Media=1"));
437  EXPECT_FALSE(HasCookie(tab1, "nonAppImage=1"));
438  EXPECT_TRUE(HasCookie(tab1, "app1Image=1"));
439
440  // Also create a non-app tab to ensure no new cookies were set in that jar.
441  ui_test_utils::NavigateToURLWithDisposition(
442      browser(), root_url,
443      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
444  WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
445  EXPECT_FALSE(HasCookie(tab2, "nonAppMedia=1"));
446  EXPECT_FALSE(HasCookie(tab2, "app1Media=1"));
447  EXPECT_FALSE(HasCookie(tab2, "nonAppImage=1"));
448  EXPECT_FALSE(HasCookie(tab2, "app1Image=1"));
449}
450
451// Test is flaky on Windows.
452// http://crbug.com/247667
453#if defined(OS_WIN)
454#define MAYBE_IsolatedAppProcessModel DISABLED_IsolatedAppProcessModel
455#else
456#define MAYBE_IsolatedAppProcessModel IsolatedAppProcessModel
457#endif  // defined(OS_WIN)
458
459// Tests that isolated apps processes do not render top-level non-app pages.
460// This is true even in the case of the OAuth workaround for hosted apps,
461// where non-app popups may be kept in the hosted app process.
462IN_PROC_BROWSER_TEST_F(IsolatedAppTest, MAYBE_IsolatedAppProcessModel) {
463  host_resolver()->AddRule("*", "127.0.0.1");
464  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
465
466  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
467
468  // The app under test acts on URLs whose host is "localhost",
469  // so the URLs we navigate to must have host "localhost".
470  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
471  GURL::Replacements replace_host;
472  std::string host_str("localhost");  // Must stay in scope with replace_host.
473  replace_host.SetHostStr(host_str);
474  base_url = base_url.ReplaceComponents(replace_host);
475
476  // Create three tabs in the isolated app in different ways.
477  ui_test_utils::NavigateToURLWithDisposition(
478      browser(), base_url.Resolve("app1/main.html"),
479      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
480  ui_test_utils::NavigateToURLWithDisposition(
481      browser(), base_url.Resolve("app1/main.html"),
482      NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
483  // For the third tab, use window.open to keep it in process with an opener.
484  OpenWindow(browser()->tab_strip_model()->GetWebContentsAt(0),
485             base_url.Resolve("app1/main.html"), true, NULL);
486
487  // In a fourth tab, use window.open to a non-app URL.  It should open in a
488  // separate process, even though this would trigger the OAuth workaround
489  // for hosted apps (from http://crbug.com/59285).
490  OpenWindow(browser()->tab_strip_model()->GetWebContentsAt(0),
491             base_url.Resolve("non_app/main.html"), false, NULL);
492
493  // We should now have four tabs, the first and third sharing a process.
494  // The second one is an independent instance in a separate process.
495  ASSERT_EQ(4, browser()->tab_strip_model()->count());
496  int process_id_0 = browser()->tab_strip_model()->GetWebContentsAt(0)->
497      GetRenderProcessHost()->GetID();
498  int process_id_1 = browser()->tab_strip_model()->GetWebContentsAt(1)->
499      GetRenderProcessHost()->GetID();
500  EXPECT_NE(process_id_0, process_id_1);
501  EXPECT_EQ(process_id_0,
502            browser()->tab_strip_model()->GetWebContentsAt(2)->
503                GetRenderProcessHost()->GetID());
504  EXPECT_NE(process_id_0,
505            browser()->tab_strip_model()->GetWebContentsAt(3)->
506                GetRenderProcessHost()->GetID());
507
508  // Navigating the second tab out of the app should cause a process swap.
509  const GURL& non_app_url(base_url.Resolve("non_app/main.html"));
510  NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(1),
511                     non_app_url);
512  EXPECT_NE(process_id_1,
513            browser()->tab_strip_model()->GetWebContentsAt(1)->
514                GetRenderProcessHost()->GetID());
515}
516
517// This test no longer passes, since we don't properly isolate sessionStorage
518// for isolated apps. This was broken as part of the changes for storage
519// partition support for webview tags.
520// TODO(nasko): If isolated apps is no longer developed, this test should be
521// removed. http://crbug.com/159932
522IN_PROC_BROWSER_TEST_F(IsolatedAppTest, DISABLED_SessionStorage) {
523  host_resolver()->AddRule("*", "127.0.0.1");
524  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
525
526  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
527  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
528
529  // The app under test acts on URLs whose host is "localhost",
530  // so the URLs we navigate to must have host "localhost".
531  GURL base_url = embedded_test_server()->GetURL("/extensions/isolated_apps/");
532  GURL::Replacements replace_host;
533  std::string host_str("localhost");  // Must stay in scope with replace_host.
534  replace_host.SetHostStr(host_str);
535  base_url = base_url.ReplaceComponents(replace_host);
536
537  // Enter some state into sessionStorage three times on the same origin, but
538  // for three URLs that correspond to app1, app2, and a non-isolated site.
539  ui_test_utils::NavigateToURLWithDisposition(
540      browser(), base_url.Resolve("app1/main.html"),
541      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
542  ASSERT_TRUE(ExecuteScript(
543      browser()->tab_strip_model()->GetWebContentsAt(0),
544      "window.sessionStorage.setItem('testdata', 'ss_app1');"));
545
546  ui_test_utils::NavigateToURLWithDisposition(
547      browser(), base_url.Resolve("app2/main.html"),
548      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
549  ASSERT_TRUE(ExecuteScript(
550      browser()->tab_strip_model()->GetWebContentsAt(0),
551      "window.sessionStorage.setItem('testdata', 'ss_app2');"));
552
553  ui_test_utils::NavigateToURLWithDisposition(
554      browser(), base_url.Resolve("non_app/main.html"),
555      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
556  ASSERT_TRUE(ExecuteScript(
557      browser()->tab_strip_model()->GetWebContentsAt(0),
558      "window.sessionStorage.setItem('testdata', 'ss_normal');"));
559
560  // Now, ensure that the sessionStorage is correctly partitioned, and persists
561  // when we navigate around all over the dang place.
562  const std::string& kRetrieveSessionStorage =
563      WrapForJavascriptAndExtract(
564          "window.sessionStorage.getItem('testdata') || 'badval'");
565  std::string result;
566  ui_test_utils::NavigateToURLWithDisposition(
567      browser(), base_url.Resolve("app1/main.html"),
568      CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
569  ASSERT_TRUE(ExecuteScriptAndExtractString(
570      browser()->tab_strip_model()->GetWebContentsAt(0),
571      kRetrieveSessionStorage.c_str(), &result));
572  EXPECT_EQ("ss_app1", result);
573
574  ui_test_utils::NavigateToURLWithDisposition(
575      browser(), base_url.Resolve("app2/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_app2", result);
581
582  ui_test_utils::NavigateToURLWithDisposition(
583      browser(), base_url.Resolve("non_app/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_normal", result);
589}
590