summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authornasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-08 08:31:53 +0000
committernasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-08 08:31:53 +0000
commit1bc2831891813f9cf9191c7d5deeccf246eff672 (patch)
treef4eb830fe3b63f5a891e802cbfa74e6616a62ce6 /chrome
parenteea2e67034a223c74ce0e8c0517f6066cc1ee143 (diff)
downloadchromium_src-1bc2831891813f9cf9191c7d5deeccf246eff672.zip
chromium_src-1bc2831891813f9cf9191c7d5deeccf246eff672.tar.gz
chromium_src-1bc2831891813f9cf9191c7d5deeccf246eff672.tar.bz2
Webview tag creation should be using storage partitions.
BUG=145500,149726 Review URL: https://chromiumcodereview.appspot.com/11234032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166639 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/chrome_content_browser_client.cc128
-rw-r--r--chrome/browser/chrome_content_browser_client.h15
-rw-r--r--chrome/browser/extensions/isolated_app_browsertest.cc7
-rw-r--r--chrome/browser/extensions/web_view_browsertest.cc351
-rw-r--r--chrome/browser/profiles/profile_impl.cc21
-rw-r--r--chrome/browser/profiles/profile_impl_io_data.cc11
-rw-r--r--chrome/test/base/testing_profile.cc11
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view_isolation/main.js11
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view_isolation/storage.js86
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view_isolation/storage1.html13
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view_isolation/storage2.html13
11 files changed, 518 insertions, 149 deletions
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 585a8a2..758eb51 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -103,6 +103,7 @@
#include "content/public/common/content_descriptors.h"
#include "grit/generated_resources.h"
#include "grit/ui_resources.h"
+#include "net/base/escape.h"
#include "net/base/ssl_cert_request_info.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
@@ -472,61 +473,79 @@ content::WebContentsView*
return NULL;
}
-std::string ChromeContentBrowserClient::GetStoragePartitionIdForChildProcess(
- content::BrowserContext* browser_context,
- int child_process_id) {
- const Extension* extension = NULL;
- Profile* profile = Profile::FromBrowserContext(browser_context);
- ExtensionService* extension_service =
- extensions::ExtensionSystem::Get(profile)->extension_service();
- if (extension_service) {
- std::set<std::string> extension_ids =
- extension_service->process_map()->
- GetExtensionsInProcess(child_process_id);
- if (!extension_ids.empty())
- // Since All the apps in a process share the same storage partition,
- // we can pick any of them to retrieve the storage partition id.
- extension =
- extension_service->extensions()->GetByID(*(extension_ids.begin()));
- }
- return GetStoragePartitionIdForExtension(browser_context, extension);
-}
-
std::string ChromeContentBrowserClient::GetStoragePartitionIdForSite(
content::BrowserContext* browser_context,
const GURL& site) {
- const Extension* extension = NULL;
- Profile* profile = Profile::FromBrowserContext(browser_context);
- ExtensionService* extension_service =
- extensions::ExtensionSystem::Get(profile)->extension_service();
- if (extension_service) {
- extension = extension_service->extensions()->
- GetExtensionOrAppByURL(ExtensionURLInfo(site));
- }
+ std::string partition_id;
+
+ // The partition ID for webview guest processes is the string value of its
+ // SiteInstance URL - "chrome-guest://app_id/persist?partition".
+ if (site.SchemeIs(chrome::kGuestScheme))
+ partition_id = site.spec();
- return GetStoragePartitionIdForExtension(browser_context, extension);
+ DCHECK(IsValidStoragePartitionId(browser_context,partition_id));
+ return partition_id;
}
bool ChromeContentBrowserClient::IsValidStoragePartitionId(
content::BrowserContext* browser_context,
const std::string& partition_id) {
- // The default ID is empty which is always allowed.
+ // The default ID is empty and is always valid.
if (partition_id.empty())
return true;
- // If it isn't empty, then it must belong to an extension of some sort. Parse
- // out the extension ID and make sure it is still installed.
+ return GURL(partition_id).is_valid();
+}
+
+void ChromeContentBrowserClient::GetStoragePartitionConfigForSite(
+ content::BrowserContext* browser_context,
+ const GURL& site,
+ std::string* partition_domain,
+ std::string* partition_name,
+ bool* in_memory) {
+ // For the webview tag, we create special guest processes, which host the
+ // tag content separately from the main application that embeds the tag.
+ // A webview tag can specify both the partition name and whether the storage
+ // for that partition should be persisted. Each tag gets a SiteInstance with
+ // a specially formatted URL, based on the application it is hosted by and
+ // the partition requested by it. The format for that URL is:
+ // chrome-guest://partition_domain/persist?partition_name
+ if (site.SchemeIs(chrome::kGuestScheme)) {
+ // Since guest URLs are only used for packaged apps, there must be an app
+ // id in the URL.
+ CHECK(site.has_host());
+ *partition_domain = site.host();
+ // Since persistence is optional, the path must either be empty or the
+ // literal string.
+ *in_memory = (site.path() != "/persist");
+ // The partition name is user supplied value, which we have encoded when the
+ // URL was created, so it needs to be decoded.
+ *partition_name = net::UnescapeURLComponent(site.query(),
+ net::UnescapeRule::NORMAL);
+ return;
+ }
+
+ const Extension* extension = NULL;
Profile* profile = Profile::FromBrowserContext(browser_context);
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
- if (!extension_service) {
- // No extension service means no storage partitions in Chrome.
- return false;
+ if (extension_service) {
+ extension = extension_service->extensions()->
+ GetExtensionOrAppByURL(ExtensionURLInfo(site));
+ if (extension && extension->is_storage_isolated()) {
+ // Extensions which have storage isolation enabled (e.g., apps), use
+ // the extension id as the |partition_domain|.
+ *partition_domain = extension->id();
+ partition_name->clear();
+ *in_memory = false;
+ return;
+ }
}
- // See if we can find an extension. The |partition_id| is the extension ID so
- // no parsing needed to be done.
- return extension_service->GetExtensionById(partition_id, false) != NULL;
+ // All other cases use the default, browser-wide, storage partition.
+ partition_domain->clear();
+ partition_name->clear();
+ *in_memory = false;
}
content::WebContentsViewDelegate*
@@ -1611,10 +1630,16 @@ void ChromeContentBrowserClient::OverrideWebkitPrefs(
chrome::ViewType view_type = chrome::GetViewType(web_contents);
ExtensionService* service = profile->GetExtensionService();
if (service) {
- const Extension* extension = service->extensions()->GetByID(
- rvh->GetSiteInstance()->GetSiteURL().host());
- extension_webkit_preferences::SetPreferences(
- extension, view_type, web_prefs);
+ const GURL& url = rvh->GetSiteInstance()->GetSiteURL();
+ const Extension* extension = service->extensions()->GetByID(url.host());
+ // Ensure that we are only granting extension preferences to URLs with
+ // the correct scheme. Without this check, chrome-guest:// schemes used by
+ // webview tags as well as hosts that happen to match the id of an
+ // installed extension would get the wrong preferences.
+ if (url.SchemeIs(chrome::kExtensionScheme)) {
+ extension_webkit_preferences::SetPreferences(
+ extension, view_type, web_prefs);
+ }
}
if (content::IsForceCompositingModeEnabled())
@@ -1892,23 +1917,4 @@ void ChromeContentBrowserClient::SetApplicationLocaleOnIOThread(
io_thread_application_locale_ = locale;
}
-std::string ChromeContentBrowserClient::GetStoragePartitionIdForExtension(
- content::BrowserContext* browser_context, const Extension* extension) {
- // In chrome, we use the extension ID as the partition ID. This works well
- // because the extension ID fits the partition ID pattern and currently only
- // apps can designate that storage should be isolated.
- //
- // If |extension| is NULL, then the default, empty string, partition id is
- // used.
- std::string partition_id;
- if (extension && extension->is_storage_isolated()) {
- partition_id = extension->id();
- }
-
- // Enforce that IsValidStoragePartitionId() implementation stays in sync.
- DCHECK(IsValidStoragePartitionId(browser_context, partition_id));
- return partition_id;
-}
-
-
} // namespace chrome
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 9a8cd77..78b741c 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -43,15 +43,18 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
content::WebContents* web_contents,
content::RenderViewHostDelegateView** render_view_host_delegate_view)
OVERRIDE;
- virtual std::string GetStoragePartitionIdForChildProcess(
- content::BrowserContext* browser_context,
- int child_process_id) OVERRIDE;
virtual std::string GetStoragePartitionIdForSite(
content::BrowserContext* browser_context,
const GURL& site) OVERRIDE;
virtual bool IsValidStoragePartitionId(
content::BrowserContext* browser_context,
const std::string& partition_id) OVERRIDE;
+ virtual void GetStoragePartitionConfigForSite(
+ content::BrowserContext* browser_context,
+ const GURL& site,
+ std::string* partition_domain,
+ std::string* partition_name,
+ bool* in_memory) OVERRIDE;
virtual content::WebContentsViewDelegate* GetWebContentsViewDelegate(
content::WebContents* web_contents) OVERRIDE;
virtual void RenderViewHostCreated(
@@ -237,12 +240,6 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
// Sets io_thread_application_locale_ to the given value.
void SetApplicationLocaleOnIOThread(const std::string& locale);
- // Helper function for getting the storage partition id from an Extension
- // object.
- std::string GetStoragePartitionIdForExtension(
- content::BrowserContext* browser_context,
- const extensions::Extension* extension);
-
#if defined(OS_ANDROID)
void InitCrashDumpManager();
diff --git a/chrome/browser/extensions/isolated_app_browsertest.cc b/chrome/browser/extensions/isolated_app_browsertest.cc
index 266d5a4..5d42e9f 100644
--- a/chrome/browser/extensions/isolated_app_browsertest.cc
+++ b/chrome/browser/extensions/isolated_app_browsertest.cc
@@ -367,7 +367,12 @@ IN_PROC_BROWSER_TEST_F(IsolatedAppTest, IsolatedAppProcessModel) {
chrome::GetWebContentsAt(browser(), 1)->GetRenderProcessHost()->GetID());
}
-IN_PROC_BROWSER_TEST_F(IsolatedAppTest, SessionStorage) {
+// This test no longer passes, since we don't properly isolate sessionStorage
+// for isolated apps. This was broken as part of the changes for storage
+// partition support for webview tags.
+// TODO(nasko): If isolated apps is no longer developed, this test should be
+// removed. http://crbug.com/159932
+IN_PROC_BROWSER_TEST_F(IsolatedAppTest, DISABLED_SessionStorage) {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(test_server()->Start());
diff --git a/chrome/browser/extensions/web_view_browsertest.cc b/chrome/browser/extensions/web_view_browsertest.cc
index d8ac13e..759017b 100644
--- a/chrome/browser/extensions/web_view_browsertest.cc
+++ b/chrome/browser/extensions/web_view_browsertest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/utf_string_conversions.h"
#include "chrome/browser/automation/automation_util.h"
#include "chrome/browser/extensions/platform_app_browsertest_util.h"
#include "chrome/browser/ui/browser_tabstrip.h"
@@ -24,6 +25,107 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
#endif
ui::DisableTestCompositor();
}
+
+ // This method is responsible for initializing a packaged app, which contains
+ // multiple webview tags. The tags have different partition identifiers and
+ // their WebContent objects are returned as output. The method also verifies
+ // the expected process allocation and storage partition assignment.
+ // The |navigate_to_url| parameter is used to navigate the main browser
+ // window.
+ void NavigateAndOpenAppForIsolation(
+ GURL navigate_to_url,
+ content::WebContents** default_tag_contents1,
+ content::WebContents** default_tag_contents2,
+ content::WebContents** named_partition_contents1,
+ content::WebContents** named_partition_contents2) {
+ GURL::Replacements replace_host;
+ std::string host_str("localhost"); // Must stay in scope with replace_host.
+ replace_host.SetHostStr(host_str);
+
+ navigate_to_url = navigate_to_url.ReplaceComponents(replace_host);
+
+ GURL tag_url1 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/cookie.html");
+ tag_url1 = tag_url1.ReplaceComponents(replace_host);
+ GURL tag_url2 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/cookie2.html");
+ tag_url2 = tag_url2.ReplaceComponents(replace_host);
+ GURL tag_url3 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/storage1.html");
+ tag_url3 = tag_url3.ReplaceComponents(replace_host);
+ GURL tag_url4 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/storage2.html");
+ tag_url4 = tag_url4.ReplaceComponents(replace_host);
+
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), navigate_to_url, CURRENT_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+
+ ui_test_utils::UrlLoadObserver observer1(
+ tag_url1, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer2(
+ tag_url2, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer3(
+ tag_url3, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer4(
+ tag_url4, content::NotificationService::AllSources());
+ LoadAndLaunchPlatformApp("web_view_isolation");
+ observer1.Wait();
+ observer2.Wait();
+ observer3.Wait();
+ observer4.Wait();
+
+ content::Source<content::NavigationController> source1 = observer1.source();
+ EXPECT_TRUE(source1->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source2 = observer2.source();
+ EXPECT_TRUE(source2->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source3 = observer3.source();
+ EXPECT_TRUE(source3->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source4 = observer4.source();
+ EXPECT_TRUE(source4->GetWebContents()->GetRenderProcessHost()->IsGuest());
+
+ // Tags with the same storage partition are not yet combined in the same
+ // process. Check this until http://crbug.com/138296 is fixed.
+ EXPECT_NE(source1->GetWebContents()->GetRenderProcessHost()->GetID(),
+ source2->GetWebContents()->GetRenderProcessHost()->GetID());
+
+ // Check that the storage partitions of the first two tags match and are
+ // different than the other two.
+ EXPECT_EQ(
+ source1->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source2->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+ EXPECT_EQ(
+ source3->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source4->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+ EXPECT_NE(
+ source1->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source3->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+
+ *default_tag_contents1 = source1->GetWebContents();
+ *default_tag_contents2 = source2->GetWebContents();
+ *named_partition_contents1 = source3->GetWebContents();
+ *named_partition_contents2 = source4->GetWebContents();
+ }
+
+ void ExecuteScriptWaitForTitle(content::WebContents* web_contents,
+ const char* script,
+ const char* title) {
+ std::wstring js_script = ASCIIToWide(script);
+ string16 expected_title(ASCIIToUTF16(title));
+ string16 error_title(ASCIIToUTF16("error"));
+
+ content::TitleWatcher title_watcher(web_contents, expected_title);
+ title_watcher.AlsoWaitForTitle(error_title);
+ EXPECT_TRUE(content::ExecuteJavaScript(web_contents->GetRenderViewHost(),
+ std::wstring(), js_script));
+ EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+ }
};
IN_PROC_BROWSER_TEST_F(WebViewTest, Shim) {
@@ -35,7 +137,11 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, ShimSrcAttribute) {
<< message_;
}
-IN_PROC_BROWSER_TEST_F(WebViewTest, Isolation) {
+// This tests cookie isolation for packaged apps with webview tags. It navigates
+// the main browser window to a page that sets a cookie and loads an app with
+// multiple webview tags. Each tag sets a cookie and the test checks the proper
+// storage isolation is enforced.
+IN_PROC_BROWSER_TEST_F(WebViewTest, CookieIsolation) {
ASSERT_TRUE(StartTestServer());
const std::wstring kExpire =
L"var expire = new Date(Date.now() + 24 * 60 * 60 * 1000);";
@@ -53,48 +159,28 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Isolation) {
GURL set_cookie_url = test_server()->GetURL(
"files/extensions/platform_apps/isolation/set_cookie.html");
set_cookie_url = set_cookie_url.ReplaceComponents(replace_host);
- GURL tag_url1 = test_server()->GetURL(
- "files/extensions/platform_apps/web_view_isolation/cookie.html");
- tag_url1 = tag_url1.ReplaceComponents(replace_host);
- GURL tag_url2 = test_server()->GetURL(
- "files/extensions/platform_apps/web_view_isolation/cookie2.html");
- tag_url2 = tag_url2.ReplaceComponents(replace_host);
-
- // Load a (non-app) page under the "localhost" origin that sets a cookie.
- ui_test_utils::NavigateToURLWithDisposition(
- browser(), set_cookie_url,
- CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
- // Make sure the cookie is set.
- int cookie_size;
- std::string cookie_value;
- automation_util::GetCookies(set_cookie_url,
- chrome::GetWebContentsAt(browser(), 0),
- &cookie_size, &cookie_value);
- EXPECT_EQ("testCookie=1", cookie_value);
- ui_test_utils::UrlLoadObserver observer1(
- tag_url1, content::NotificationService::AllSources());
- ui_test_utils::UrlLoadObserver observer2(
- tag_url2, content::NotificationService::AllSources());
- LoadAndLaunchPlatformApp("web_view_isolation");
- observer1.Wait();
- observer2.Wait();
-
- content::Source<content::NavigationController> source1 = observer1.source();
- EXPECT_TRUE(source1->GetWebContents()->GetRenderProcessHost()->IsGuest());
- content::Source<content::NavigationController> source2 = observer2.source();
- EXPECT_TRUE(source2->GetWebContents()->GetRenderProcessHost()->IsGuest());
- EXPECT_NE(source1->GetWebContents()->GetRenderProcessHost()->GetID(),
- source2->GetWebContents()->GetRenderProcessHost()->GetID());
+ // The first two partitions will be used to set cookies and ensure they are
+ // shared. The named partition is used to ensure that cookies are isolated
+ // between partitions within the same app.
+ content::WebContents* cookie_contents1;
+ content::WebContents* cookie_contents2;
+ content::WebContents* named_partition_contents1;
+ content::WebContents* named_partition_contents2;
+
+ NavigateAndOpenAppForIsolation(set_cookie_url, &cookie_contents1,
+ &cookie_contents2, &named_partition_contents1,
+ &named_partition_contents2);
EXPECT_TRUE(content::ExecuteJavaScript(
- source1->GetWebContents()->GetRenderViewHost(), std::wstring(),
- cookie_script1));
+ cookie_contents1->GetRenderViewHost(), std::wstring(), cookie_script1));
EXPECT_TRUE(content::ExecuteJavaScript(
- source2->GetWebContents()->GetRenderViewHost(), std::wstring(),
- cookie_script2));
+ cookie_contents2->GetRenderViewHost(), std::wstring(), cookie_script2));
- // Test the regular browser context to ensure we still have only one cookie.
+ int cookie_size;
+ std::string cookie_value;
+
+ // Test the regular browser context to ensure we have only one cookie.
automation_util::GetCookies(GURL("http://localhost"),
chrome::GetWebContentsAt(browser(), 0),
&cookie_size, &cookie_value);
@@ -105,13 +191,198 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, Isolation) {
// ensure we have properly set the cookies and we have both cookies in both
// tags.
automation_util::GetCookies(GURL("http://localhost"),
- source1->GetWebContents(),
+ cookie_contents1,
&cookie_size, &cookie_value);
EXPECT_EQ("guest1=true; guest2=true", cookie_value);
automation_util::GetCookies(GURL("http://localhost"),
- source2->GetWebContents(),
+ cookie_contents2,
&cookie_size, &cookie_value);
EXPECT_EQ("guest1=true; guest2=true", cookie_value);
+
+ // The third tag should not have any cookies as it is in a separate partition.
+ automation_util::GetCookies(GURL("http://localhost"),
+ named_partition_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("", cookie_value);
+
+ CloseShellWindowsAndWaitForAppToExit();
+}
+
+// This tests DOM storage isolation for packaged apps with webview tags. It
+// loads an app with multiple webview tags and each tag sets DOM storage
+// entries, which the test checks to ensure proper storage isolation is
+// enforced.
+IN_PROC_BROWSER_TEST_F(WebViewTest, DOMStorageIsolation) {
+ ASSERT_TRUE(StartTestServer());
+ GURL regular_url = test_server()->GetURL("files/title1.html");
+
+ std::string output;
+ std::wstring get_local_storage(L"window.domAutomationController.send("
+ L"window.localStorage.getItem('foo') || 'badval')");
+ std::wstring get_session_storage(L"window.domAutomationController.send("
+ L"window.sessionStorage.getItem('bar') || 'badval')");
+
+ content::WebContents* default_tag_contents1;
+ content::WebContents* default_tag_contents2;
+ content::WebContents* storage_contents1;
+ content::WebContents* storage_contents2;
+
+ NavigateAndOpenAppForIsolation(regular_url, &default_tag_contents1,
+ &default_tag_contents2, &storage_contents1,
+ &storage_contents2);
+
+ // Initialize the storage for the first of the two tags that share a storage
+ // partition.
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ L"initDomStorage('page1')"));
+
+ // Let's test that the expected values are present in the first tag, as they
+ // will be overwritten once we call the initDomStorage on the second tag.
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_local_storage.c_str(), &output));
+ EXPECT_STREQ("local-page1", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_session_storage.c_str(), &output));
+ EXPECT_STREQ("session-page1", output.c_str());
+
+ // Now, init the storage in the second tag in the same storage partition,
+ // which will overwrite the shared localStorage.
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ storage_contents2->GetRenderViewHost(), std::wstring(),
+ L"initDomStorage('page2')"));
+
+ // The localStorage value now should reflect the one written through the
+ // second tag.
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_local_storage.c_str(), &output));
+ EXPECT_STREQ("local-page2", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents2->GetRenderViewHost(), std::wstring(),
+ get_local_storage.c_str(), &output));
+ EXPECT_STREQ("local-page2", output.c_str());
+
+ // Session storage is not shared though, as each webview tag has separate
+ // instance, even if they are in the same storage partition.
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_session_storage.c_str(), &output));
+ EXPECT_STREQ("session-page1", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents2->GetRenderViewHost(), std::wstring(),
+ get_session_storage.c_str(), &output));
+ EXPECT_STREQ("session-page2", output.c_str());
+
+ // Also, let's check that the main browser and another tag that doesn't share
+ // the same partition don't have those values stored.
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ chrome::GetWebContentsAt(browser(), 0)->GetRenderViewHost(),
+ std::wstring(), get_local_storage.c_str(), &output));
+ EXPECT_STREQ("badval", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ chrome::GetWebContentsAt(browser(), 0)->GetRenderViewHost(),
+ std::wstring(), get_session_storage.c_str(), &output));
+ EXPECT_STREQ("badval", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ default_tag_contents1->GetRenderViewHost(), std::wstring(),
+ get_local_storage.c_str(), &output));
+ EXPECT_STREQ("badval", output.c_str());
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ default_tag_contents1->GetRenderViewHost(), std::wstring(),
+ get_session_storage.c_str(), &output));
+ EXPECT_STREQ("badval", output.c_str());
+
+ CloseShellWindowsAndWaitForAppToExit();
+}
+
+// This tests IndexedDB isolation for packaged apps with webview tags. It loads
+// an app with multiple webview tags and each tag creates an IndexedDB record,
+// which the test checks to ensure proper storage isolation is enforced.
+IN_PROC_BROWSER_TEST_F(WebViewTest, IndexedDBIsolation) {
+ ASSERT_TRUE(StartTestServer());
+ GURL regular_url = test_server()->GetURL("files/title1.html");
+
+ content::WebContents* default_tag_contents1;
+ content::WebContents* default_tag_contents2;
+ content::WebContents* storage_contents1;
+ content::WebContents* storage_contents2;
+
+ NavigateAndOpenAppForIsolation(regular_url, &default_tag_contents1,
+ &default_tag_contents2, &storage_contents1,
+ &storage_contents2);
+
+ // Initialize the storage for the first of the two tags that share a storage
+ // partition.
+ ExecuteScriptWaitForTitle(storage_contents1, "initIDB()", "idb created");
+ ExecuteScriptWaitForTitle(storage_contents1, "addItemIDB(7, 'page1')",
+ "addItemIDB complete");
+ ExecuteScriptWaitForTitle(storage_contents1, "readItemIDB(7)",
+ "readItemIDB complete");
+
+ std::string output;
+ std::wstring get_value(
+ L"window.domAutomationController.send(getValueIDB() || 'badval')");
+
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_value.c_str(), &output));
+ EXPECT_STREQ("page1", output.c_str());
+
+ // Initialize the db in the second tag.
+ ExecuteScriptWaitForTitle(storage_contents2, "initIDB()", "idb open");
+
+ // Since we share a partition, reading the value should return the existing
+ // one.
+ ExecuteScriptWaitForTitle(storage_contents2, "readItemIDB(7)",
+ "readItemIDB complete");
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents2->GetRenderViewHost(), std::wstring(),
+ get_value.c_str(), &output));
+ EXPECT_STREQ("page1", output.c_str());
+
+ // Now write through the second tag and read it back.
+ ExecuteScriptWaitForTitle(storage_contents2, "addItemIDB(7, 'page2')",
+ "addItemIDB complete");
+ ExecuteScriptWaitForTitle(storage_contents2, "readItemIDB(7)",
+ "readItemIDB complete");
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents2->GetRenderViewHost(), std::wstring(),
+ get_value.c_str(), &output));
+ EXPECT_STREQ("page2", output.c_str());
+
+ // Reset the document title, otherwise the next call will not see a change and
+ // will hang waiting for it.
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ L"document.title = 'foo'"));
+
+ // Read through the first tag to ensure we have the second value.
+ ExecuteScriptWaitForTitle(storage_contents1, "readItemIDB(7)",
+ "readItemIDB complete");
+ EXPECT_TRUE(ExecuteJavaScriptAndExtractString(
+ storage_contents1->GetRenderViewHost(), std::wstring(),
+ get_value.c_str(), &output));
+ EXPECT_STREQ("page2", output.c_str());
+
+ // Now, let's confirm there is no database in the main browser and another
+ // tag that doesn't share the same partition. Due to the IndexedDB API design,
+ // open will succeed, but the version will be 1, since it creates the database
+ // if it is not found. The two tags use database version 3, so we avoid
+ // ambiguity.
+ const char* script =
+ "indexedDB.open('isolation').onsuccess = function(e) {"
+ " if (e.target.result.version == 1)"
+ " document.title = 'db not found';"
+ " else "
+ " document.title = 'error';"
+ "}";
+ ExecuteScriptWaitForTitle(chrome::GetWebContentsAt(browser(), 0),
+ script, "db not found");
+ ExecuteScriptWaitForTitle(default_tag_contents1, script, "db not found");
+
CloseShellWindowsAndWaitForAppToExit();
}
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 5edbb68..7411d64 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -782,18 +782,8 @@ net::URLRequestContextGetter* ProfileImpl::GetRequestContextForRenderProcess(
int renderer_child_id) {
content::RenderProcessHost* rph = content::RenderProcessHost::FromID(
renderer_child_id);
- content::StoragePartition* storage_partition = rph->GetStoragePartition();
-
- // TODO(nasko): Remove this conditional, once webview tag creates a proper
- // storage partition.
- if (rph->IsGuest()) {
- // For guest processes, we only allow in-memory partitions for now, so
- // hardcode the parameter here.
- return GetRequestContextForStoragePartition(
- storage_partition->GetPath(), true);
- }
- return storage_partition->GetURLRequestContext();
+ return rph->GetStoragePartition()->GetURLRequestContext();
}
net::URLRequestContextGetter* ProfileImpl::GetMediaRequestContext() {
@@ -808,15 +798,6 @@ ProfileImpl::GetMediaRequestContextForRenderProcess(
renderer_child_id);
content::StoragePartition* storage_partition = rph->GetStoragePartition();
- // TODO(nasko): Remove this conditional, once webview tag creates a proper
- // storage partition.
- if (rph->IsGuest()) {
- // For guest processes, we only allow in-memory partitions for now, so
- // hardcode the parameter here.
- return GetMediaRequestContextForStoragePartition(
- storage_partition->GetPath(), true);
- }
-
return storage_partition->GetMediaURLRequestContext();
}
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 65e8822..35377cb 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -188,9 +188,9 @@ ProfileImplIOData::Handle::GetIsolatedAppRequestContextGetter(
const FilePath& partition_path,
bool in_memory) const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // TODO(nasko): Check that the partition_path is not the same as the
- // base profile path. We expect isolated partition, which will never go
- // to the default profile path.
+ // Check that the partition_path is not the same as the base profile path. We
+ // expect isolated partition, which will never go to the default profile path.
+ CHECK(partition_path != profile_->GetPath());
LazyInitialize();
// Keep a map of request context getters, one per requested storage partition.
@@ -220,10 +220,7 @@ ProfileImplIOData::Handle::GetIsolatedMediaRequestContextGetter(
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We must have a non-default path, or this will act like the default media
// context.
- //
- // TODO(nasko): Check that the partition_path is not the same as the
- // base profile path. We expect isolated partition, which will never go
- // to the default profile path.
+ CHECK(partition_path != profile_->GetPath());
LazyInitialize();
// Keep a map of request context getters, one per requested storage partition.
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index bfc8bec..fdb8bbb 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -612,16 +612,7 @@ net::URLRequestContextGetter* TestingProfile::GetRequestContextForRenderProcess(
int renderer_child_id) {
content::RenderProcessHost* rph = content::RenderProcessHost::FromID(
renderer_child_id);
- content::StoragePartition* storage_partition = rph->GetStoragePartition();
-
- // TODO(nasko): Remove this conditional, once webview tag creates a proper
- // storage partition.
- if (rph->IsGuest()) {
- return GetRequestContextForStoragePartition(
- storage_partition->GetPath(), true);
- }
-
- return storage_partition->GetURLRequestContext();
+ return rph->GetStoragePartition()->GetURLRequestContext();
}
void TestingProfile::CreateRequestContext() {
diff --git a/chrome/test/data/extensions/platform_apps/web_view_isolation/main.js b/chrome/test/data/extensions/platform_apps/web_view_isolation/main.js
index 16f1b2d..ad95ea9 100644
--- a/chrome/test/data/extensions/platform_apps/web_view_isolation/main.js
+++ b/chrome/test/data/extensions/platform_apps/web_view_isolation/main.js
@@ -7,10 +7,19 @@ chrome.test.getConfig(function(config) {
'/files/extensions/platform_apps/web_view_isolation/cookie.html';
var url2 = 'http://localhost:' + config.testServer.port +
'/files/extensions/platform_apps/web_view_isolation/cookie2.html';
+ var url3 = 'http://localhost:' + config.testServer.port +
+ '/files/extensions/platform_apps/web_view_isolation/storage1.html';
+ var url4 = 'http://localhost:' + config.testServer.port +
+ '/files/extensions/platform_apps/web_view_isolation/storage2.html';
var node = document.getElementById('web_view_container');
- node.innerHTML = "<object id='webview' src=" + url +
+ node.innerHTML =
+ "<object id='webview' src=" + url +
" type='application/browser-plugin' width=500 height=550></object>" +
"<object id='webview2' src=" + url2 +
+ " type='application/browser-plugin' width=500 height=550></object>" +
+ "<object id='webview2' partition='partition1' src=" + url3 +
+ " type='application/browser-plugin' width=500 height=550></object>" +
+ "<object id='webview2' partition='partition1' src=" + url4 +
" type='application/browser-plugin' width=500 height=550></object>";
chrome.test.sendMessage('Launched');
});
diff --git a/chrome/test/data/extensions/platform_apps/web_view_isolation/storage.js b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage.js
new file mode 100644
index 0000000..a9bc99e
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage.js
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This method initializes the two types of DOM storage.
+function initDomStorage(value) {
+ window.localStorage.setItem('foo', 'local-' + value);
+ window.sessionStorage.setItem('bar', 'session-' + value);
+}
+
+// The code below is used for testing IndexedDB isolation.
+// The test uses three basic operations -- open, read, write -- to verify proper
+// isolation across webview tags with different storage partitions.
+// Each of the basic functions below sets document.title to a specific text,
+// which the main browser test is waiting for. This is needed because all
+// the functions get their results through callbacks and cannot return the
+// values directly.
+var isolation = {};
+window.indexedDB = window.indexedDB || window.webkitIndexedDB;
+
+isolation.db = null;
+isolation.onerror = function(e) {
+ document.title = "error";
+};
+
+// This method opens the database and creates the objectStore if it doesn't
+// exist. It sets the document.title to a string referring to which
+// operation has been performed - open vs create.
+function initIDB() {
+ var request = indexedDB.open("isolation");
+ request.onsuccess = function(e) {
+ var v = 3;
+ isolation.db = e.target.result;
+ if (v != isolation.db.version) {
+ var setVrequest = isolation.db.setVersion(v);
+ setVrequest.onerror = isolation.onerror;
+ setVrequest.onsuccess = function(e) {
+ var store = isolation.db.createObjectStore(
+ "partitions", {keyPath: "id"});
+ e.target.transaction.oncomplete = function() {
+ document.title = "idb created";
+ }
+ };
+ } else {
+ document.title = "idb open";
+ }
+ };
+ request.onerror = isolation.onerror;
+}
+
+// This method adds a |value| to the database identified by |id|.
+function addItemIDB(id, value) {
+ var trans = isolation.db.transaction(["partitions"], "readwrite");
+ var store = trans.objectStore("partitions");
+ var data = { "partition": value, "id": id };
+
+ var request = store.put(data);
+ request.onsuccess = function(e) {
+ document.title = "addItemIDB complete";
+ };
+ request.onerror = isolation.onerror;
+};
+
+var storedValue = null;
+
+// This method reads an item from the database, identified by |id|. Since
+// the value cannot be returned directly, it is saved into the global
+// "storedValue" variable, which is then read through getValueIDB().
+function readItemIDB(id) {
+ storedValue = null;
+ var trans = isolation.db.transaction(["partitions"], "readwrite");
+ var store = trans.objectStore("partitions");
+
+ var request = store.get(id);
+ request.onsuccess = function(e) {
+ if (!!e.target.result != false) {
+ storedValue = request.result.partition;
+ }
+ document.title = "readItemIDB complete";
+ };
+ request.onerror = isolation.onerror;
+}
+
+function getValueIDB() {
+ return storedValue;
+}
diff --git a/chrome/test/data/extensions/platform_apps/web_view_isolation/storage1.html b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage1.html
new file mode 100644
index 0000000..8443a27
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage1.html
@@ -0,0 +1,13 @@
+<!--
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved. Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<html>
+<head>
+<script src="storage.js"></script>
+</head>
+<body>
+Empty page 1, used for testing storage isolation.
+</body>
+</html>
diff --git a/chrome/test/data/extensions/platform_apps/web_view_isolation/storage2.html b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage2.html
new file mode 100644
index 0000000..d12b17c
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view_isolation/storage2.html
@@ -0,0 +1,13 @@
+<!--
+ * Copyright (c) 2012 The Chromium Authors. All rights reserved. Use of this
+ * source code is governed by a BSD-style license that can be found in the
+ * LICENSE file.
+-->
+<html>
+<head>
+<script src="storage.js"></script>
+</head>
+<body>
+Empty page 2, used for testing storage isolation.
+</body>
+</html>