summaryrefslogtreecommitdiffstats
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
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
-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
-rw-r--r--content/browser/browser_context.cc44
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder.cc30
-rw-r--r--content/browser/storage_partition_impl.cc39
-rw-r--r--content/browser/storage_partition_impl.h62
-rw-r--r--content/browser/storage_partition_impl_map.cc47
-rw-r--r--content/browser/storage_partition_impl_map.h17
-rw-r--r--content/browser/storage_partition_impl_unittest.cc58
-rw-r--r--content/browser/web_contents/web_contents_impl.cc11
-rw-r--r--content/browser/web_contents/web_contents_impl.h2
-rw-r--r--content/common/url_schemes.cc1
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/browser/browser_context.h3
-rw-r--r--content/public/browser/content_browser_client.cc17
-rw-r--r--content/public/browser/content_browser_client.h34
-rw-r--r--content/public/browser/storage_partition.h8
26 files changed, 788 insertions, 253 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>
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index cbfd2ca..99905f8 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -37,9 +37,11 @@ namespace {
const char* kDownloadManagerKeyName = "download_manager";
const char* kStorageParitionMapKeyName = "content_storage_partition_map";
-StoragePartition* GetStoragePartitionByPartitionId(
+StoragePartition* GetStoragePartitionFromConfig(
BrowserContext* browser_context,
- const std::string& partition_id) {
+ const std::string& partition_domain,
+ const std::string& partition_name,
+ bool in_memory) {
StoragePartitionImplMap* partition_map =
static_cast<StoragePartitionImplMap*>(
browser_context->GetUserData(kStorageParitionMapKeyName));
@@ -48,12 +50,20 @@ StoragePartition* GetStoragePartitionByPartitionId(
browser_context->SetUserData(kStorageParitionMapKeyName, partition_map);
}
- return partition_map->Get(partition_id);
+ if (browser_context->IsOffTheRecord())
+ in_memory = true;
+
+ // TODO(nasko): Webview tags with named partitions will have both
+ // partition_domain and partition_name set. In this case, mark them in-memory
+ // until the on-disk storage code has landed. http://crbug.com/159464
+ if (!partition_domain.empty() && !partition_name.empty())
+ in_memory = true;
+
+ return partition_map->Get(partition_domain, partition_name, in_memory);
}
// Run |callback| on each DOMStorageContextImpl in |browser_context|.
-void PurgeDOMStorageContextInPartition(const std::string& id,
- StoragePartition* storage_partition) {
+void PurgeDOMStorageContextInPartition(StoragePartition* storage_partition) {
static_cast<StoragePartitionImpl*>(storage_partition)->
GetDOMStorageContext()->PurgeMemory();
}
@@ -104,26 +114,34 @@ DownloadManager* BrowserContext::GetDownloadManager(
StoragePartition* BrowserContext::GetStoragePartition(
BrowserContext* browser_context,
SiteInstance* site_instance) {
- std::string partition_id; // Default to "" for NULL |site_instance|.
+ std::string partition_domain;
+ std::string partition_name;
+ bool in_memory = false;
// TODO(ajwong): After GetDefaultStoragePartition() is removed, get rid of
// this conditional and require that |site_instance| is non-NULL.
if (site_instance) {
- partition_id = GetContentClient()->browser()->
- GetStoragePartitionIdForSite(browser_context,
- site_instance->GetSiteURL());
+ GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+ browser_context, site_instance->GetSiteURL(),
+ &partition_domain, &partition_name, &in_memory);
}
- return GetStoragePartitionByPartitionId(browser_context, partition_id);
+ return GetStoragePartitionFromConfig(
+ browser_context, partition_domain, partition_name, in_memory);
}
StoragePartition* BrowserContext::GetStoragePartitionForSite(
BrowserContext* browser_context,
const GURL& site) {
- std::string partition_id = GetContentClient()->browser()->
- GetStoragePartitionIdForSite(browser_context, site);
+ std::string partition_domain;
+ std::string partition_name;
+ bool in_memory;
+
+ GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+ browser_context, site, &partition_domain, &partition_name, &in_memory);
- return GetStoragePartitionByPartitionId(browser_context, partition_id);
+ return GetStoragePartitionFromConfig(
+ browser_context, partition_domain, partition_name, in_memory);
}
void BrowserContext::ForEachStoragePartition(
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc
index 1f4b64b..b789f88 100644
--- a/content/browser/browser_plugin/browser_plugin_embedder.cc
+++ b/content/browser/browser_plugin/browser_plugin_embedder.cc
@@ -15,7 +15,10 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
+#include "content/public/browser/user_metrics.h"
+#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
+#include "net/base/escape.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "ui/gfx/size.h"
@@ -80,11 +83,36 @@ void BrowserPluginEmbedder::CreateGuest(
BrowserPluginGuest* guest = GetGuestByInstanceID(instance_id);
CHECK(!guest);
+ // Validate that the partition id coming from the renderer is valid UTF-8,
+ // since we depend on this in other parts of the code, such as FilePath
+ // creation. If the validation fails, treat it as a bad message and kill the
+ // renderer process.
+ if (!IsStringUTF8(params.storage_partition_id)) {
+ content::RecordAction(UserMetricsAction("BadMessageTerminate_BPE"));
+ base::KillProcess(render_view_host->GetProcess()->GetHandle(),
+ content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
+ return;
+ }
+
const std::string& host =
render_view_host->GetSiteInstance()->GetSiteURL().host();
+ std::string url_encoded_partition = net::EscapeQueryParamValue(
+ params.storage_partition_id, false);
+
+ // The SiteInstance of a given webview tag is based on the fact that it's a
+ // guest process in addition to which platform application the tag belongs to
+ // and what storage partition is in use, rather than the URL that the tag is
+ // being navigated to.
+ GURL guest_site(
+ base::StringPrintf("%s://%s/%s?%s", chrome::kGuestScheme,
+ host.c_str(), params.persist_storage ? "persist" : "",
+ url_encoded_partition.c_str()));
+ SiteInstance* guest_site_instance = SiteInstance::CreateForURL(
+ web_contents()->GetBrowserContext(), guest_site);
+
guest_web_contents = WebContentsImpl::CreateGuest(
web_contents()->GetBrowserContext(),
- host,
+ guest_site_instance,
instance_id,
params);
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 7ba19db..ff95c38 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -4,6 +4,7 @@
#include "content/browser/storage_partition_impl.h"
+#include "base/utf_string_conversions.h"
#include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
@@ -28,7 +29,9 @@ namespace {
// Storage/ext/ABCDEF/def
// Storage/ext/ABCDEF/{hash(guest partition)}
//
-// The code in GetPartitionPath() constructs these path names.
+// The code in GetStoragePartitionPath() constructs these path names.
+//
+// TODO(nasko): Move extension related path code out of content.
const FilePath::CharType kStoragePartitionDirname[] =
FILE_PATH_LITERAL("Storage");
const FilePath::CharType kExtensionsDirname[] =
@@ -39,18 +42,20 @@ const FilePath::CharType kDefaultPartitionDirname[] =
} // namespace
// static
-FilePath StoragePartition::GetPartitionPath(const std::string& partition_id) {
- if (partition_id.empty()) {
- // The default profile just sits inside the top-level profile directory.
+FilePath StoragePartitionImpl::GetStoragePartitionPath(
+ const StoragePartitionConfig& config) {
+ if (config.partition_domain.empty())
return FilePath();
- }
- // TODO(ajwong): This should check that we create a valid path name.
- CHECK(IsStringASCII(partition_id));
- return FilePath(kStoragePartitionDirname)
- .Append(kExtensionsDirname)
- .AppendASCII(partition_id)
- .Append(kDefaultPartitionDirname);
+ CHECK(IsStringUTF8(config.partition_domain));
+
+ FilePath path = FilePath(kStoragePartitionDirname).Append(kExtensionsDirname)
+ .Append(FilePath::FromUTF8Unsafe(config.partition_domain));
+
+ if (!config.partition_name.empty())
+ return path.Append(FilePath::FromUTF8Unsafe(config.partition_name));
+
+ return path.Append(kDefaultPartitionDirname);
}
StoragePartitionImpl::StoragePartitionImpl(
@@ -88,7 +93,7 @@ StoragePartitionImpl::~StoragePartitionImpl() {
// need 3 pieces of info from it.
StoragePartitionImpl* StoragePartitionImpl::Create(
BrowserContext* context,
- const std::string& partition_id,
+ const StoragePartitionConfig& partition_config,
const FilePath& profile_path) {
// Ensure that these methods are called on the UI thread, except for
// unittests where a UI thread might not have been created.
@@ -96,7 +101,7 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
!BrowserThread::IsMessageLoopValid(BrowserThread::UI));
FilePath partition_path =
- profile_path.Append(GetPartitionPath(partition_id));
+ profile_path.Append(GetStoragePartitionPath(partition_config));
// All of the clients have to be created and registered with the
// QuotaManager prior to the QuotaManger being used. We do them
@@ -104,7 +109,7 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
// that utilizes the QuotaManager.
scoped_refptr<quota::QuotaManager> quota_manager =
new quota::QuotaManager(
- context->IsOffTheRecord(), partition_path,
+ partition_config.in_memory, partition_path,
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
context->GetSpecialStoragePolicy());
@@ -112,17 +117,17 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
// Each consumer is responsible for registering its QuotaClient during
// its construction.
scoped_refptr<fileapi::FileSystemContext> filesystem_context =
- CreateFileSystemContext(partition_path, context->IsOffTheRecord(),
+ CreateFileSystemContext(partition_path, partition_config.in_memory,
context->GetSpecialStoragePolicy(),
quota_manager->proxy());
scoped_refptr<webkit_database::DatabaseTracker> database_tracker =
new webkit_database::DatabaseTracker(
- partition_path, context->IsOffTheRecord(),
+ partition_path, partition_config.in_memory,
context->GetSpecialStoragePolicy(), quota_manager->proxy(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
- FilePath path = context->IsOffTheRecord() ? FilePath() : partition_path;
+ FilePath path = partition_config.in_memory ? FilePath() : partition_path;
scoped_refptr<DOMStorageContextImpl> dom_storage_context =
new DOMStorageContextImpl(path, context->GetSpecialStoragePolicy());
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h
index d3d6dba..9712837 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -7,6 +7,7 @@
#include "base/compiler_specific.h"
#include "base/file_path.h"
+#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/dom_storage/dom_storage_context_impl.h"
@@ -19,12 +20,6 @@ class StoragePartitionImpl : public StoragePartition {
public:
virtual ~StoragePartitionImpl();
- // TODO(ajwong): Break the direct dependency on |context|. We only
- // need 3 pieces of info from it.
- static StoragePartitionImpl* Create(BrowserContext* context,
- const std::string& partition_id,
- const FilePath& profile_path);
-
// StoragePartition interface.
virtual FilePath GetPath() OVERRIDE;
virtual net::URLRequestContextGetter* GetURLRequestContext() OVERRIDE;
@@ -37,8 +32,63 @@ class StoragePartitionImpl : public StoragePartition {
virtual IndexedDBContextImpl* GetIndexedDBContext() OVERRIDE;
private:
+ FRIEND_TEST_ALL_PREFIXES(StoragePartitionConfigTest, OperatorLess);
friend class StoragePartitionImplMap;
+ // Each StoragePartition is uniquely identified by which partition domain
+ // it belongs to (such as an app or the browser itself), the user supplied
+ // partition name and the bit indicating whether it should be persisted on
+ // disk or not. This structure contains those elements and is used as
+ // uniqueness key to lookup StoragePartition objects in the global map.
+ //
+ // TODO(nasko): It is equivalent, though not identical to the same structure
+ // that lives in chrome profiles. The difference is that this one has
+ // partition_domain and partition_name separate, while the latter one has
+ // the path produced by combining the two pieces together.
+ // The fix for http://crbug.com/159193 will remove the chrome version.
+ struct StoragePartitionConfig {
+ const std::string partition_domain;
+ const std::string partition_name;
+ const bool in_memory;
+
+ StoragePartitionConfig(const std::string& domain,
+ const std::string& partition,
+ const bool& in_memory_only)
+ : partition_domain(domain),
+ partition_name(partition),
+ in_memory(in_memory_only) {}
+ };
+
+ // Functor for operator <.
+ struct StoragePartitionConfigLess {
+ bool operator()(const StoragePartitionConfig& lhs,
+ const StoragePartitionConfig& rhs) const {
+ if (lhs.partition_domain != rhs.partition_domain)
+ return lhs.partition_domain < rhs.partition_domain;
+ else if (lhs.partition_name != rhs.partition_name)
+ return lhs.partition_name < rhs.partition_name;
+ else if (lhs.in_memory != rhs.in_memory)
+ return lhs.in_memory < rhs.in_memory;
+ else
+ return false;
+ }
+ };
+
+ // TODO(ajwong): Break the direct dependency on |context|. We only
+ // need 3 pieces of info from it.
+ static StoragePartitionImpl* Create(
+ BrowserContext* context,
+ const StoragePartitionConfig& partition_id,
+ const FilePath& profile_path);
+
+ // Returns the relative path from the profile's base directory, to the
+ // directory that holds all the state for storage contexts in
+ // |partition_config|. If any of the strings in |partition_config| contain
+ // embedded nuls, the values will be truncated and only the portion prior to
+ // the nul will be used.
+ static FilePath GetStoragePartitionPath(
+ const StoragePartitionConfig& partition_config);
+
StoragePartitionImpl(
const FilePath& partition_path,
quota::QuotaManager* quota_manager,
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 89f39e5..93f52d0 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -187,7 +187,8 @@ void InitializeURLRequestContext(
StoragePartitionImplMap::StoragePartitionImplMap(
BrowserContext* browser_context)
- : browser_context_(browser_context) {
+ : browser_context_(browser_context),
+ resource_context_initialized_(false) {
}
StoragePartitionImplMap::~StoragePartitionImplMap() {
@@ -196,51 +197,55 @@ StoragePartitionImplMap::~StoragePartitionImplMap() {
}
StoragePartitionImpl* StoragePartitionImplMap::Get(
- const std::string& partition_id) {
+ const std::string& partition_domain,
+ const std::string& partition_name,
+ bool in_memory) {
+ // TODO(ajwong): ResourceContexts no longer have any storage related state.
+ // We should move this into a place where it is called once per
+ // BrowserContext creation rather than piggybacking off the default context
+ // creation.
+ if (!resource_context_initialized_) {
+ resource_context_initialized_ = true;
+ InitializeResourceContext(browser_context_);
+ }
+
// Find the previously created partition if it's available.
- std::map<std::string, StoragePartitionImpl*>::const_iterator it =
- partitions_.find(partition_id);
+ StoragePartitionImpl::StoragePartitionConfig partition_config(
+ partition_domain, partition_name, in_memory);
+
+ PartitionMap::const_iterator it = partitions_.find(partition_config);
if (it != partitions_.end())
return it->second;
// There was no previous partition, so let's make a new one.
StoragePartitionImpl* partition =
- StoragePartitionImpl::Create(browser_context_, partition_id,
+ StoragePartitionImpl::Create(browser_context_, partition_config,
browser_context_->GetPath());
- partitions_[partition_id] = partition;
+ partitions_[partition_config] = partition;
// These calls must happen after StoragePartitionImpl::Create().
partition->SetURLRequestContext(
- partition_id.empty() ?
+ partition_domain.empty() ?
browser_context_->GetRequestContext() :
browser_context_->GetRequestContextForStoragePartition(
- partition->GetPath(), false));
+ partition->GetPath(), in_memory));
partition->SetMediaURLRequestContext(
- partition_id.empty() ?
+ partition_domain.empty() ?
browser_context_->GetMediaRequestContext() :
browser_context_->GetMediaRequestContextForStoragePartition(
- partition->GetPath(), false));
+ partition->GetPath(), in_memory));
PostCreateInitialization(partition);
- // TODO(ajwong): ResourceContexts no longer have any storage related state.
- // We should move this into a place where it is called once per
- // BrowserContext creation rather than piggybacking off the default context
- // creation.
- if (partition_id.empty()) {
- InitializeResourceContext(browser_context_);
- }
-
return partition;
}
void StoragePartitionImplMap::ForEach(
const BrowserContext::StoragePartitionCallback& callback) {
- for (std::map<std::string, StoragePartitionImpl*>::const_iterator it =
- partitions_.begin();
+ for (PartitionMap::const_iterator it = partitions_.begin();
it != partitions_.end();
++it) {
- callback.Run(it->first, it->second);
+ callback.Run(it->second);
}
}
diff --git a/content/browser/storage_partition_impl_map.h b/content/browser/storage_partition_impl_map.h
index 9e5c6d6..14b78ce 100644
--- a/content/browser/storage_partition_impl_map.h
+++ b/content/browser/storage_partition_impl_map.h
@@ -10,6 +10,7 @@
#include "base/callback_forward.h"
#include "base/supports_user_data.h"
+#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_context.h"
class FilePath;
@@ -17,7 +18,6 @@ class FilePath;
namespace content {
class BrowserContext;
-class StoragePartitionImpl;
// A std::string to StoragePartition map for use with SupportsUserData APIs.
class StoragePartitionImplMap : public base::SupportsUserData::Data {
@@ -27,11 +27,18 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data {
virtual ~StoragePartitionImplMap();
// This map retains ownership of the returned StoragePartition objects.
- StoragePartitionImpl* Get(const std::string& partition_id);
+ StoragePartitionImpl* Get(const std::string& partition_domain,
+ const std::string& partition_name,
+ bool in_memory);
void ForEach(const BrowserContext::StoragePartitionCallback& callback);
private:
+ typedef std::map<StoragePartitionImpl::StoragePartitionConfig,
+ StoragePartitionImpl*,
+ StoragePartitionImpl::StoragePartitionConfigLess>
+ PartitionMap;
+
// This must always be called *after* |partition| has been added to the
// partitions_.
//
@@ -41,7 +48,11 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data {
void PostCreateInitialization(StoragePartitionImpl* partition);
BrowserContext* browser_context_; // Not Owned.
- std::map<std::string, StoragePartitionImpl*> partitions_;
+ PartitionMap partitions_;
+
+ // Set to true when the ResourceContext for the associated |browser_context_|
+ // is initialized. Can never return to false.
+ bool resource_context_initialized_;
};
} // namespace content
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
new file mode 100644
index 0000000..bf74943
--- /dev/null
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -0,0 +1,58 @@
+// 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.
+
+#include "content/browser/storage_partition_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class StoragePartitionConfigTest : public testing::Test {
+};
+
+// Test that the Less comparison function is implemented properly to uniquely
+// identify storage partitions used as keys in a std::map.
+TEST_F(StoragePartitionConfigTest, OperatorLess) {
+ StoragePartitionImpl::StoragePartitionConfig c1("", "", false);
+ StoragePartitionImpl::StoragePartitionConfig c2("", "", false);
+ StoragePartitionImpl::StoragePartitionConfig c3("", "", true);
+ StoragePartitionImpl::StoragePartitionConfig c4("a", "", true);
+ StoragePartitionImpl::StoragePartitionConfig c5("b", "", true);
+ StoragePartitionImpl::StoragePartitionConfig c6("", "abc", false);
+ StoragePartitionImpl::StoragePartitionConfig c7("", "abc", true);
+ StoragePartitionImpl::StoragePartitionConfig c8("a", "abc", false);
+ StoragePartitionImpl::StoragePartitionConfig c9("a", "abc", true);
+
+ StoragePartitionImpl::StoragePartitionConfigLess less;
+
+ // Let's ensure basic comparison works.
+ EXPECT_TRUE(less(c1, c3));
+ EXPECT_TRUE(less(c1, c4));
+ EXPECT_TRUE(less(c3, c4));
+ EXPECT_TRUE(less(c4, c5));
+ EXPECT_TRUE(less(c4, c8));
+ EXPECT_TRUE(less(c6, c4));
+ EXPECT_TRUE(less(c6, c7));
+ EXPECT_TRUE(less(c8, c9));
+
+ // Now, ensure antisymmetry for each pair we've tested.
+ EXPECT_FALSE(less(c3, c1));
+ EXPECT_FALSE(less(c4, c1));
+ EXPECT_FALSE(less(c4, c3));
+ EXPECT_FALSE(less(c5, c4));
+ EXPECT_FALSE(less(c8, c4));
+ EXPECT_FALSE(less(c4, c6));
+ EXPECT_FALSE(less(c7, c6));
+ EXPECT_FALSE(less(c9, c8));
+
+ // Check for irreflexivity.
+ EXPECT_FALSE(less(c1, c1));
+
+ // Check for transitivity.
+ EXPECT_TRUE(less(c1, c4));
+
+ // Let's enforce that two identical elements obey strict weak ordering.
+ EXPECT_TRUE(!less(c1, c2) && !less(c2, c1));
+}
+
+} // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 693c08b..3408d0a 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -396,19 +396,12 @@ WebContentsImpl* WebContentsImpl::CreateWithOpener(
WebContentsImpl* WebContentsImpl::CreateGuest(
BrowserContext* browser_context,
- const std::string& host_url,
+ SiteInstance* site_instance,
int guest_instance_id,
const BrowserPluginHostMsg_CreateGuest_Params& params) {
- // The SiteInstance of a given guest is based on the fact that it's a guest
- // in addition to which platform application the guest belongs to, rather
- // than the URL that the guest is being navigated to.
- GURL guest_site(
- base::StringPrintf("%s://%s", chrome::kGuestScheme, host_url.c_str()));
- SiteInstance* guest_site_instance =
- SiteInstance::CreateForURL(browser_context, guest_site);
WebContentsImpl* new_contents = WebContentsImpl::Create(
browser_context,
- guest_site_instance,
+ site_instance,
MSG_ROUTING_NONE,
NULL); // base WebContents
WebContentsImpl* new_contents_impl =
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d2fc73d..1c23b92 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -95,7 +95,7 @@ class CONTENT_EXPORT WebContentsImpl
// Creates a WebContents to be used as a browser plugin guest.
static WebContentsImpl* CreateGuest(
BrowserContext* browser_context,
- const std::string& host,
+ content::SiteInstance* site_instance,
int guest_instance_id,
const BrowserPluginHostMsg_CreateGuest_Params& params);
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc
index 9db4872..3c64243c 100644
--- a/content/common/url_schemes.cc
+++ b/content/common/url_schemes.cc
@@ -39,6 +39,7 @@ void RegisterContentSchemes(bool lock_standard_schemes) {
url_util::AddStandardScheme(chrome::kChromeDevToolsScheme);
url_util::AddStandardScheme(chrome::kChromeUIScheme);
url_util::AddStandardScheme(chrome::kMetadataScheme);
+ url_util::AddStandardScheme(chrome::kGuestScheme);
std::for_each(additional_standard_schemes.begin(),
additional_standard_schemes.end(),
AddStandardSchemeHelper);
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 8b208f0..c87b945 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -321,6 +321,7 @@
'browser/speech/google_streaming_remote_engine_unittest.cc',
'browser/speech/speech_recognizer_unittest.cc',
'browser/ssl/ssl_host_state_unittest.cc',
+ 'browser/storage_partition_impl_unittest.cc',
'browser/system_message_window_win_unittest.cc',
'browser/trace_subscriber_stdio_unittest.cc',
'browser/web_contents/navigation_controller_impl_unittest.cc',
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index 79640f0..addc4d7 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -39,8 +39,7 @@ class CONTENT_EXPORT BrowserContext : public base::SupportsUserData {
public:
// Used in ForEachStoragePartition(). The first argument is the partition id.
// The second argument is the StoragePartition object for that partition id.
- typedef base::Callback<void(const std::string&, StoragePartition*)>
- StoragePartitionCallback;
+ typedef base::Callback<void(StoragePartition*)> StoragePartitionCallback;
static DownloadManager* GetDownloadManager(BrowserContext* browser_context);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index cb562f9..65374cf 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -160,12 +160,6 @@ net::URLRequestContext* ContentBrowserClient::OverrideRequestContextForURL(
return NULL;
}
-std::string ContentBrowserClient::GetStoragePartitionIdForChildProcess(
- BrowserContext* browser_context,
- int child_process_id) {
- return std::string();
-}
-
std::string ContentBrowserClient::GetStoragePartitionIdForSite(
BrowserContext* browser_context,
const GURL& site) {
@@ -180,6 +174,17 @@ bool ContentBrowserClient::IsValidStoragePartitionId(
return partition_id.empty();
}
+void ContentBrowserClient::GetStoragePartitionConfigForSite(
+ BrowserContext* browser_context,
+ const GURL& site,
+ std::string* partition_domain,
+ std::string* partition_name,
+ bool* in_memory) {
+ partition_domain->clear();
+ partition_name->clear();
+ *in_memory = false;
+}
+
MediaObserver* ContentBrowserClient::GetMediaObserver() {
return NULL;
}
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index e1e3e76..3d6199f 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -263,23 +263,8 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual net::URLRequestContext* OverrideRequestContextForURL(
const GURL& url, ResourceContext* context);
- // Allow the embedder to specify storage parititon id associated with a child
- // process.
- //
- // Child processes that have different storage partition identifiers will
- // behave as if they belong to different web browsers and not be able to
- // access each other's cookies, local storage, etc. IDs must only fit the
- // pattern [a-z0-9]* (lowercase letters or digits).
- //
- // Returns the empty string for the regular storage partition.
- virtual std::string GetStoragePartitionIdForChildProcess(
- content::BrowserContext* browser_context,
- int child_process_id);
-
- // Same as GetStoragePartitionIdForChildProcess(), but uses a site instead.
- //
- // TODO(ajwong): Replace all uses of GetStoragePartitionIdForChildProcess()
- // with this one.
+ // Allow the embedder to specify a string version of the storage partition
+ // config with a site.
virtual std::string GetStoragePartitionIdForSite(
content::BrowserContext* browser_context,
const GURL& site);
@@ -290,6 +275,21 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual bool IsValidStoragePartitionId(BrowserContext* browser_context,
const std::string& partition_id);
+ // Allows the embedder to provide a storage parititon configuration for a
+ // site. A storage partition configuration includes a domain of the embedder's
+ // choice, an optional name within that domain, and whether the partition is
+ // in-memory only. The |partition_domain| is [a-z]* UTF-8 string, specifying
+ // the domain in which partitions live (similar to namespace). Within a
+ // domain, partitions can be uniquely identified by the combination of
+ // |partition_name| and |in_memory| values. When a partition is not to be
+ // persisted, the |in_memory| value must be set to true.
+ virtual void GetStoragePartitionConfigForSite(
+ content::BrowserContext* browser_context,
+ const GURL& site,
+ std::string* partition_domain,
+ std::string* partition_name,
+ bool* in_memory);
+
// Create and return a new quota permission context.
virtual QuotaPermissionContext* CreateQuotaPermissionContext();
diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h
index 6743a42..76ea98b 100644
--- a/content/public/browser/storage_partition.h
+++ b/content/public/browser/storage_partition.h
@@ -54,14 +54,6 @@ class StoragePartition {
virtual DOMStorageContext* GetDOMStorageContext() = 0;
virtual IndexedDBContext* GetIndexedDBContext() = 0;
- // Returns the relative path from the profile's base directory, to the
- // directory that holds all the state for storage contexts in |partition_id|.
- //
- // TODO(ajwong): Remove this function from the public API once
- // URLRequestContextGetter's creation is moved into StoragePartition.
- static CONTENT_EXPORT FilePath GetPartitionPath(
- const std::string& partition_id);
-
protected:
virtual ~StoragePartition() {}
};