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