summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/web_view_browsertest.cc225
-rw-r--r--chrome/test/data/extensions/platform_apps/web_view_isolation/main.js16
-rw-r--r--content/browser/browser_context.cc6
-rw-r--r--content/browser/storage_partition_impl.cc59
-rw-r--r--content/browser/storage_partition_impl.h62
-rw-r--r--content/browser/storage_partition_impl_map.cc90
-rw-r--r--content/browser/storage_partition_impl_map.h52
-rw-r--r--content/browser/storage_partition_impl_map_unittest.cc (renamed from content/browser/storage_partition_impl_unittest.cc)24
-rw-r--r--content/content_tests.gypi2
9 files changed, 398 insertions, 138 deletions
diff --git a/chrome/browser/extensions/web_view_browsertest.cc b/chrome/browser/extensions/web_view_browsertest.cc
index 8e68ece..6c6cbaa 100644
--- a/chrome/browser/extensions/web_view_browsertest.cc
+++ b/chrome/browser/extensions/web_view_browsertest.cc
@@ -32,12 +32,23 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
// the expected process allocation and storage partition assignment.
// The |navigate_to_url| parameter is used to navigate the main browser
// window.
+ //
+ // TODO(ajwong): This function is getting to be too large. Either refactor it
+ // so the test can specify a configuration of WebView tags that we will
+ // dynamically inject JS to generate, or move this test wholesale into
+ // something that RunPlatformAppTest() can execute purely in Javascript. This
+ // won't let us do a white-box examination of the StoragePartition equivalence
+ // directly, but we will be able to view the black box effects which is good
+ // enough. http://crbug.com/160361
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) {
+ content::WebContents** named_partition_contents2,
+ content::WebContents** persistent_partition_contents1,
+ content::WebContents** persistent_partition_contents2,
+ content::WebContents** persistent_partition_contents3) {
GURL::Replacements replace_host;
std::string host_str("localhost"); // Must stay in scope with replace_host.
replace_host.SetHostStr(host_str);
@@ -56,6 +67,15 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
GURL tag_url4 = test_server()->GetURL(
"files/extensions/platform_apps/web_view_isolation/storage2.html");
tag_url4 = tag_url4.ReplaceComponents(replace_host);
+ GURL tag_url5 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/storage1.html#p1");
+ tag_url5 = tag_url5.ReplaceComponents(replace_host);
+ GURL tag_url6 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/storage1.html#p2");
+ tag_url6 = tag_url6.ReplaceComponents(replace_host);
+ GURL tag_url7 = test_server()->GetURL(
+ "files/extensions/platform_apps/web_view_isolation/storage1.html#p3");
+ tag_url7 = tag_url7.ReplaceComponents(replace_host);
ui_test_utils::NavigateToURLWithDisposition(
browser(), navigate_to_url, CURRENT_TAB,
@@ -69,11 +89,20 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
tag_url3, content::NotificationService::AllSources());
ui_test_utils::UrlLoadObserver observer4(
tag_url4, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer5(
+ tag_url5, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer6(
+ tag_url6, content::NotificationService::AllSources());
+ ui_test_utils::UrlLoadObserver observer7(
+ tag_url7, content::NotificationService::AllSources());
LoadAndLaunchPlatformApp("web_view_isolation");
observer1.Wait();
observer2.Wait();
observer3.Wait();
observer4.Wait();
+ observer5.Wait();
+ observer6.Wait();
+ observer7.Wait();
content::Source<content::NavigationController> source1 = observer1.source();
EXPECT_TRUE(source1->GetWebContents()->GetRenderProcessHost()->IsGuest());
@@ -83,6 +112,12 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
EXPECT_TRUE(source3->GetWebContents()->GetRenderProcessHost()->IsGuest());
content::Source<content::NavigationController> source4 = observer4.source();
EXPECT_TRUE(source4->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source5 = observer5.source();
+ EXPECT_TRUE(source5->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source6 = observer6.source();
+ EXPECT_TRUE(source6->GetWebContents()->GetRenderProcessHost()->IsGuest());
+ content::Source<content::NavigationController> source7 = observer7.source();
+ EXPECT_TRUE(source7->GetWebContents()->GetRenderProcessHost()->IsGuest());
// Check that the first two tags use the same process and it is different
// than the process used by the other two.
@@ -119,10 +154,41 @@ class WebViewTest : public extensions::PlatformAppBrowserTest {
source3->GetWebContents()->GetRenderProcessHost()->
GetStoragePartition());
+ // Ensure the persistent storage partitions are different.
+ EXPECT_EQ(
+ source5->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source6->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+ EXPECT_NE(
+ source5->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source7->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+ EXPECT_NE(
+ source1->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source5->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+ EXPECT_NE(
+ source1->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition(),
+ source7->GetWebContents()->GetRenderProcessHost()->
+ GetStoragePartition());
+
*default_tag_contents1 = source1->GetWebContents();
*default_tag_contents2 = source2->GetWebContents();
*named_partition_contents1 = source3->GetWebContents();
*named_partition_contents2 = source4->GetWebContents();
+ if (persistent_partition_contents1) {
+ *persistent_partition_contents1 = source5->GetWebContents();
+ }
+ if (persistent_partition_contents2) {
+ *persistent_partition_contents2 = source6->GetWebContents();
+ }
+ if (persistent_partition_contents3) {
+ *persistent_partition_contents3 = source7->GetWebContents();
+ }
}
void ExecuteScriptWaitForTitle(content::WebContents* web_contents,
@@ -182,7 +248,7 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, CookieIsolation) {
NavigateAndOpenAppForIsolation(set_cookie_url, &cookie_contents1,
&cookie_contents2, &named_partition_contents1,
- &named_partition_contents2);
+ &named_partition_contents2, NULL, NULL, NULL);
EXPECT_TRUE(content::ExecuteJavaScript(
cookie_contents1->GetRenderViewHost(), std::wstring(), cookie_script1));
@@ -219,6 +285,157 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, CookieIsolation) {
EXPECT_EQ("", cookie_value);
}
+// This tests that in-memory storage partitions are reset on browser restart,
+// but persistent ones maintain state for cookies and HTML5 storage.
+IN_PROC_BROWSER_TEST_F(WebViewTest, PRE_StoragePersistence) {
+ ASSERT_TRUE(StartTestServer());
+ const std::wstring kExpire =
+ L"var expire = new Date(Date.now() + 24 * 60 * 60 * 1000);";
+ std::wstring cookie_script1(kExpire);
+ cookie_script1.append(
+ L"document.cookie = 'inmemory=true; path=/; expires=' + expire + ';';");
+ std::wstring cookie_script2(kExpire);
+ cookie_script2.append(
+ L"document.cookie = 'persist1=true; path=/; expires=' + expire + ';';");
+ std::wstring cookie_script3(kExpire);
+ cookie_script3.append(
+ L"document.cookie = 'persist2=true; path=/; expires=' + expire + ';';");
+
+ // We don't care where the main browser is on this test.
+ GURL blank_url("about:blank");
+
+ // 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;
+ content::WebContents* persistent_partition_contents1;
+ content::WebContents* persistent_partition_contents2;
+ content::WebContents* persistent_partition_contents3;
+ NavigateAndOpenAppForIsolation(blank_url, &cookie_contents1,
+ &cookie_contents2, &named_partition_contents1,
+ &named_partition_contents2,
+ &persistent_partition_contents1,
+ &persistent_partition_contents2,
+ &persistent_partition_contents3);
+
+ // Set the inmemory=true cookie for tags with inmemory partitions.
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ cookie_contents1->GetRenderViewHost(), std::wstring(),
+ cookie_script1));
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ named_partition_contents1->GetRenderViewHost(), std::wstring(),
+ cookie_script1));
+
+ // For the two different persistent storage partitions, set the
+ // two different cookies so we can check that they aren't comingled below.
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ persistent_partition_contents1->GetRenderViewHost(), std::wstring(),
+ cookie_script2));
+
+ EXPECT_TRUE(content::ExecuteJavaScript(
+ persistent_partition_contents3->GetRenderViewHost(), std::wstring(),
+ cookie_script3));
+
+ int cookie_size;
+ std::string cookie_value;
+
+ // Check that all in-memory partitions have a cookie set.
+ automation_util::GetCookies(GURL("http://localhost"),
+ cookie_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("inmemory=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ cookie_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("inmemory=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ named_partition_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("inmemory=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ named_partition_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("inmemory=true", cookie_value);
+
+ // Check that all persistent partitions kept their state.
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist1=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist1=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents3,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist2=true", cookie_value);
+}
+
+// This is the post-reset portion of the StoragePersistence test. See
+// PRE_StoragePersistence for main comment.
+IN_PROC_BROWSER_TEST_F(WebViewTest, StoragePersistence) {
+ ASSERT_TRUE(StartTestServer());
+
+ // We don't care where the main browser is on this test.
+ GURL blank_url("about:blank");
+
+ // 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;
+ content::WebContents* persistent_partition_contents1;
+ content::WebContents* persistent_partition_contents2;
+ content::WebContents* persistent_partition_contents3;
+ NavigateAndOpenAppForIsolation(blank_url, &cookie_contents1,
+ &cookie_contents2, &named_partition_contents1,
+ &named_partition_contents2,
+ &persistent_partition_contents1,
+ &persistent_partition_contents2,
+ &persistent_partition_contents3);
+
+ int cookie_size;
+ std::string cookie_value;
+
+ // Check that all in-memory partitions lost their state.
+ automation_util::GetCookies(GURL("http://localhost"),
+ cookie_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ cookie_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ named_partition_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ named_partition_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("", cookie_value);
+
+ // Check that all persistent partitions kept their state.
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents1,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist1=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents2,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist1=true", cookie_value);
+ automation_util::GetCookies(GURL("http://localhost"),
+ persistent_partition_contents3,
+ &cookie_size, &cookie_value);
+ EXPECT_EQ("persist2=true", cookie_value);
+}
+
// 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
@@ -240,7 +457,7 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, DOMStorageIsolation) {
NavigateAndOpenAppForIsolation(regular_url, &default_tag_contents1,
&default_tag_contents2, &storage_contents1,
- &storage_contents2);
+ &storage_contents2, NULL, NULL, NULL);
// Initialize the storage for the first of the two tags that share a storage
// partition.
@@ -321,7 +538,7 @@ IN_PROC_BROWSER_TEST_F(WebViewTest, IndexedDBIsolation) {
NavigateAndOpenAppForIsolation(regular_url, &default_tag_contents1,
&default_tag_contents2, &storage_contents1,
- &storage_contents2);
+ &storage_contents2, NULL, NULL, NULL);
// Initialize the storage for the first of the two tags that share a storage
// partition.
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 ad95ea9..6b44dff 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
@@ -11,15 +11,27 @@ chrome.test.getConfig(function(config) {
'/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 url5 = 'http://localhost:' + config.testServer.port +
+ '/files/extensions/platform_apps/web_view_isolation/storage1.html#p1';
+ var url6 = 'http://localhost:' + config.testServer.port +
+ '/files/extensions/platform_apps/web_view_isolation/storage1.html#p2';
+ var url7 = 'http://localhost:' + config.testServer.port +
+ '/files/extensions/platform_apps/web_view_isolation/storage1.html#p3';
var node = document.getElementById('web_view_container');
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 +
+ "<object id='webview3' partition='partition1' src=" + url3 +
" type='application/browser-plugin' width=500 height=550></object>" +
- "<object id='webview2' partition='partition1' src=" + url4 +
+ "<object id='webview4' partition='partition1' src=" + url4 +
+ " type='application/browser-plugin' width=500 height=550></object>" +
+ "<object id='webview5' partition='persist:1' src=" + url5 +
+ " type='application/browser-plugin' width=500 height=550></object>" +
+ "<object id='webview6' partition='persist:1' src=" + url6 +
+ " type='application/browser-plugin' width=500 height=550></object>" +
+ "<object id='webview7' partition='persist:2' src=" + url7 +
" type='application/browser-plugin' width=500 height=550></object>";
chrome.test.sendMessage('Launched');
});
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 99905f8..d8bc87e 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -53,12 +53,6 @@ StoragePartition* GetStoragePartitionFromConfig(
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);
}
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index ff95c38..f2d1383 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -14,50 +14,6 @@
namespace content {
-namespace {
-
-// These constants are used to create the directory structure under the profile
-// where renderers with a non-default storage partition keep their persistent
-// state. This will contain a set of directories that partially mirror the
-// directory structure of BrowserContext::GetPath().
-//
-// The kStoragePartitionDirname is contains an extensions directory which is
-// further partitioned by extension id, followed by another level of directories
-// for the "default" extension storage partition and one directory for each
-// persistent partition used by an extension's browser tags. Example:
-//
-// Storage/ext/ABCDEF/def
-// Storage/ext/ABCDEF/{hash(guest partition)}
-//
-// 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[] =
- FILE_PATH_LITERAL("ext");
-const FilePath::CharType kDefaultPartitionDirname[] =
- FILE_PATH_LITERAL("def");
-
-} // namespace
-
-// static
-FilePath StoragePartitionImpl::GetStoragePartitionPath(
- const StoragePartitionConfig& config) {
- if (config.partition_domain.empty())
- return FilePath();
-
- 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(
const FilePath& partition_path,
quota::QuotaManager* quota_manager,
@@ -93,23 +49,20 @@ StoragePartitionImpl::~StoragePartitionImpl() {
// need 3 pieces of info from it.
StoragePartitionImpl* StoragePartitionImpl::Create(
BrowserContext* context,
- const StoragePartitionConfig& partition_config,
- const FilePath& profile_path) {
+ bool in_memory,
+ const FilePath& partition_path) {
// Ensure that these methods are called on the UI thread, except for
// unittests where a UI thread might not have been created.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
!BrowserThread::IsMessageLoopValid(BrowserThread::UI));
- FilePath partition_path =
- 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
// all together here prior to handing out a reference to anything
// that utilizes the QuotaManager.
scoped_refptr<quota::QuotaManager> quota_manager =
new quota::QuotaManager(
- partition_config.in_memory, partition_path,
+ in_memory, partition_path,
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
context->GetSpecialStoragePolicy());
@@ -117,17 +70,17 @@ StoragePartitionImpl* StoragePartitionImpl::Create(
// Each consumer is responsible for registering its QuotaClient during
// its construction.
scoped_refptr<fileapi::FileSystemContext> filesystem_context =
- CreateFileSystemContext(partition_path, partition_config.in_memory,
+ CreateFileSystemContext(partition_path, in_memory,
context->GetSpecialStoragePolicy(),
quota_manager->proxy());
scoped_refptr<webkit_database::DatabaseTracker> database_tracker =
new webkit_database::DatabaseTracker(
- partition_path, partition_config.in_memory,
+ partition_path, in_memory,
context->GetSpecialStoragePolicy(), quota_manager->proxy(),
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
- FilePath path = partition_config.in_memory ? FilePath() : partition_path;
+ FilePath path = 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 9712837..c5af368 100644
--- a/content/browser/storage_partition_impl.h
+++ b/content/browser/storage_partition_impl.h
@@ -7,7 +7,6 @@
#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"
@@ -32,62 +31,17 @@ 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.
+ // The |partition_path| is the absolute path to the root of this
+ // StoragePartition's on-disk storage.
//
- // 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);
+ // If |in_memory| is true, the |partition_path| is (ab)used as a way of
+ // distinguishing different in-memory partitions, but nothing is persisted
+ // on to disk.
+ static StoragePartitionImpl* Create(BrowserContext* context,
+ bool in_memory,
+ const FilePath& profile_path);
StoragePartitionImpl(
const FilePath& partition_path,
diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc
index 93f52d0..eb7ae78 100644
--- a/content/browser/storage_partition_impl_map.cc
+++ b/content/browser/storage_partition_impl_map.cc
@@ -9,6 +9,7 @@
#include "base/file_path.h"
#include "base/stl_util.h"
#include "base/string_util.h"
+#include "base/string_number_conversions.h"
#include "content/browser/appcache/chrome_appcache_service.h"
#include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/browser/fileapi/chrome_blob_storage_context.h"
@@ -24,6 +25,7 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/url_constants.h"
+#include "crypto/sha2.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_context.h"
#include "webkit/appcache/view_appcache_internals_job.h"
@@ -183,8 +185,86 @@ void InitializeURLRequestContext(
// TODO(jam): Add the ProtocolHandlerRegistryIntercepter here!
}
+// These constants are used to create the directory structure under the profile
+// where renderers with a non-default storage partition keep their persistent
+// state. This will contain a set of directories that partially mirror the
+// directory structure of BrowserContext::GetPath().
+//
+// The kStoragePartitionDirname contains an extensions directory which is
+// further partitioned by extension id, followed by another level of directories
+// for the "default" extension storage partition and one directory for each
+// persistent partition used by a webview tag. Example:
+//
+// Storage/ext/ABCDEF/def
+// Storage/ext/ABCDEF/hash(partition name)
+//
+// 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[] =
+ FILE_PATH_LITERAL("ext");
+const FilePath::CharType kDefaultPartitionDirname[] =
+ FILE_PATH_LITERAL("def");
+
+// Because partition names are user specified, they can be arbitrarily long
+// which makes them unsuitable for paths names. We use a truncation of a
+// SHA256 hash to perform a deterministic shortening of the string. The
+// kPartitionNameHashBytes constant controls the length of the truncation.
+// We use 6 bytes, which gives us 99.999% reliability against collisions over
+// 1 million partition domains.
+//
+// Analysis:
+// We assume that all partition names within one partition domain are
+// controlled by the the same entity. Thus there is no chance for adverserial
+// attack and all we care about is accidental collision. To get 5 9s over
+// 1 million domains, we need the probability of a collision in any one domain
+// to be
+//
+// p < nroot(1000000, .99999) ~= 10^-11
+//
+// We use the following birthday attack approximation to calculate the max
+// number of unique names for this probability:
+//
+// n(p,H) = sqrt(2*H * ln(1/(1-p)))
+//
+// For a 6-byte hash, H = 2^(6*8). n(10^-11, H) ~= 75
+//
+// An average partition domain is likely to have less than 10 unique
+// partition names which is far lower than 75.
+//
+// Note, that for 4 9s of reliability, the limit is 237 partition names per
+// partition domain.
+const int kPartitionNameHashBytes = 6;
+
} // namespace
+// static
+FilePath StoragePartitionImplMap::GetStoragePartitionPath(
+ const std::string& partition_domain,
+ const std::string& partition_name) {
+ if (partition_domain.empty())
+ return FilePath();
+
+ CHECK(IsStringUTF8(partition_domain));
+
+ FilePath path = FilePath(kStoragePartitionDirname).Append(kExtensionsDirname)
+ .Append(FilePath::FromUTF8Unsafe(partition_domain));
+
+ if (!partition_name.empty()) {
+ // For analysis of why we can ignore collisions, see the comment above
+ // kPartitionNameHashBytes.
+ char buffer[kPartitionNameHashBytes];
+ crypto::SHA256HashString(partition_name, &buffer[0],
+ sizeof(buffer));
+ return path.AppendASCII(base::HexEncode(buffer, sizeof(buffer)));
+ }
+
+ return path.Append(kDefaultPartitionDirname);
+}
+
+
StoragePartitionImplMap::StoragePartitionImplMap(
BrowserContext* browser_context)
: browser_context_(browser_context),
@@ -210,17 +290,19 @@ StoragePartitionImpl* StoragePartitionImplMap::Get(
}
// Find the previously created partition if it's available.
- StoragePartitionImpl::StoragePartitionConfig partition_config(
+ 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.
+ FilePath partition_path =
+ browser_context_->GetPath().Append(
+ GetStoragePartitionPath(partition_domain, partition_name));
StoragePartitionImpl* partition =
- StoragePartitionImpl::Create(browser_context_, partition_config,
- browser_context_->GetPath());
+ StoragePartitionImpl::Create(browser_context_, in_memory,
+ partition_path);
partitions_[partition_config] = partition;
// These calls must happen after StoragePartitionImpl::Create().
diff --git a/content/browser/storage_partition_impl_map.h b/content/browser/storage_partition_impl_map.h
index 14b78ce..9da35c3 100644
--- a/content/browser/storage_partition_impl_map.h
+++ b/content/browser/storage_partition_impl_map.h
@@ -9,6 +9,7 @@
#include <string>
#include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
#include "base/supports_user_data.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/browser_context.h"
@@ -34,11 +35,58 @@ class StoragePartitionImplMap : public base::SupportsUserData::Data {
void ForEach(const BrowserContext::StoragePartitionCallback& callback);
private:
- typedef std::map<StoragePartitionImpl::StoragePartitionConfig,
+ FRIEND_TEST_ALL_PREFIXES(StoragePartitionConfigTest, OperatorLess);
+
+ // 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;
+ }
+ };
+
+ typedef std::map<StoragePartitionConfig,
StoragePartitionImpl*,
- StoragePartitionImpl::StoragePartitionConfigLess>
+ StoragePartitionConfigLess>
PartitionMap;
+ // Returns the relative path from the profile's base directory, to the
+ // directory that holds all the state for storage contexts in the given
+ // |partition_domain| and |partition_name|.
+ static FilePath GetStoragePartitionPath(const std::string& partition_domain,
+ const std::string& partition_name);
+
// This must always be called *after* |partition| has been added to the
// partitions_.
//
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_map_unittest.cc
index bf74943..1c44fcc 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_map_unittest.cc
@@ -2,7 +2,7 @@
// 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 "content/browser/storage_partition_impl_map.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -13,17 +13,17 @@ 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;
+ StoragePartitionImplMap::StoragePartitionConfig c1("", "", false);
+ StoragePartitionImplMap::StoragePartitionConfig c2("", "", false);
+ StoragePartitionImplMap::StoragePartitionConfig c3("", "", true);
+ StoragePartitionImplMap::StoragePartitionConfig c4("a", "", true);
+ StoragePartitionImplMap::StoragePartitionConfig c5("b", "", true);
+ StoragePartitionImplMap::StoragePartitionConfig c6("", "abc", false);
+ StoragePartitionImplMap::StoragePartitionConfig c7("", "abc", true);
+ StoragePartitionImplMap::StoragePartitionConfig c8("a", "abc", false);
+ StoragePartitionImplMap::StoragePartitionConfig c9("a", "abc", true);
+
+ StoragePartitionImplMap::StoragePartitionConfigLess less;
// Let's ensure basic comparison works.
EXPECT_TRUE(less(c1, c3));
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 93b60b2..42c4ef1 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -321,7 +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/storage_partition_impl_map_unittest.cc',
'browser/system_message_window_win_unittest.cc',
'browser/trace_subscriber_stdio_unittest.cc',
'browser/web_contents/navigation_controller_impl_unittest.cc',