diff options
16 files changed, 175 insertions, 10 deletions
diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc index 08ff3f2..95a2486 100644 --- a/chrome/browser/extensions/extension_special_storage_policy.cc +++ b/chrome/browser/extensions/extension_special_storage_policy.cc @@ -5,11 +5,15 @@ #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "base/logging.h" +#include "chrome/browser/content_settings/host_content_settings_map.h" +#include "chrome/common/content_settings.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" -ExtensionSpecialStoragePolicy::ExtensionSpecialStoragePolicy() {} +ExtensionSpecialStoragePolicy::ExtensionSpecialStoragePolicy( + HostContentSettingsMap* host_content_settings_map) + : host_content_settings_map_(host_content_settings_map) {} ExtensionSpecialStoragePolicy::~ExtensionSpecialStoragePolicy() {} @@ -25,6 +29,14 @@ bool ExtensionSpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) { return unlimited_extensions_.Contains(origin); } +bool ExtensionSpecialStoragePolicy::IsStorageSessionOnly(const GURL& origin) { + if (host_content_settings_map_ == NULL) + return false; + ContentSetting content_setting = host_content_settings_map_-> + GetCookieContentSetting(origin, origin, true); + return (content_setting == CONTENT_SETTING_SESSION_ONLY); +} + bool ExtensionSpecialStoragePolicy::IsFileHandler( const std::string& extension_id) { base::AutoLock locker(lock_); diff --git a/chrome/browser/extensions/extension_special_storage_policy.h b/chrome/browser/extensions/extension_special_storage_policy.h index b31407c..8ce9d30 100644 --- a/chrome/browser/extensions/extension_special_storage_policy.h +++ b/chrome/browser/extensions/extension_special_storage_policy.h @@ -14,18 +14,21 @@ #include "webkit/quota/special_storage_policy.h" class Extension; +class HostContentSettingsMap; // Special rights are granted to 'extensions' and 'applications'. The // storage subsystems and the browsing data remover query this interface // to determine which origins have these rights. class ExtensionSpecialStoragePolicy : public quota::SpecialStoragePolicy { public: - ExtensionSpecialStoragePolicy(); + explicit ExtensionSpecialStoragePolicy( + HostContentSettingsMap* host_content_settings_map); // SpecialStoragePolicy methods used by storage subsystems and the browsing // data remover. These methods are safe to call on any thread. virtual bool IsStorageProtected(const GURL& origin); virtual bool IsStorageUnlimited(const GURL& origin); + virtual bool IsStorageSessionOnly(const GURL& origin); virtual bool IsFileHandler(const std::string& extension_id); // Methods used by the ExtensionService to populate this class. @@ -61,6 +64,7 @@ class ExtensionSpecialStoragePolicy : public quota::SpecialStoragePolicy { SpecialCollection protected_apps_; SpecialCollection unlimited_extensions_; SpecialCollection file_handler_extensions_; + scoped_refptr<HostContentSettingsMap> host_content_settings_map_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SPECIAL_STORAGE_POLICY_H_ diff --git a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc index cbaf6ed..bd7f274 100644 --- a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc +++ b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc @@ -118,7 +118,7 @@ TEST_F(ExtensionSpecialStoragePolicyTest, EmptyPolicy) { const GURL kExtensionUrl("chrome-extension://bar"); scoped_refptr<ExtensionSpecialStoragePolicy> policy( - new ExtensionSpecialStoragePolicy); + new ExtensionSpecialStoragePolicy(NULL)); ASSERT_FALSE(policy->IsStorageUnlimited(kHttpUrl)); ASSERT_FALSE(policy->IsStorageUnlimited(kHttpUrl)); // test cached result @@ -133,7 +133,7 @@ TEST_F(ExtensionSpecialStoragePolicyTest, EmptyPolicy) { TEST_F(ExtensionSpecialStoragePolicyTest, AppWithProtectedStorage) { scoped_refptr<Extension> extension(CreateProtectedApp()); scoped_refptr<ExtensionSpecialStoragePolicy> policy( - new ExtensionSpecialStoragePolicy); + new ExtensionSpecialStoragePolicy(NULL)); policy->GrantRightsForExtension(extension); EXPECT_FALSE(policy->IsStorageUnlimited(extension->url())); EXPECT_FALSE(policy->IsStorageUnlimited(GURL("http://explicit/"))); @@ -152,7 +152,7 @@ TEST_F(ExtensionSpecialStoragePolicyTest, AppWithProtectedStorage) { TEST_F(ExtensionSpecialStoragePolicyTest, AppWithUnlimitedStorage) { scoped_refptr<Extension> extension(CreateUnlimitedApp()); scoped_refptr<ExtensionSpecialStoragePolicy> policy( - new ExtensionSpecialStoragePolicy); + new ExtensionSpecialStoragePolicy(NULL)); policy->GrantRightsForExtension(extension); EXPECT_TRUE(policy->IsStorageProtected(GURL("http://explicit/"))); EXPECT_TRUE(policy->IsStorageProtected(GURL("http://explicit:6000/"))); @@ -181,7 +181,7 @@ TEST_F(ExtensionSpecialStoragePolicyTest, OverlappingApps) { scoped_refptr<Extension> protected_app(CreateProtectedApp()); scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp()); scoped_refptr<ExtensionSpecialStoragePolicy> policy( - new ExtensionSpecialStoragePolicy); + new ExtensionSpecialStoragePolicy(NULL)); policy->GrantRightsForExtension(protected_app); policy->GrantRightsForExtension(unlimited_app); diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.cc b/chrome/browser/extensions/mock_extension_special_storage_policy.cc index dddf49f..7cf86cd 100644 --- a/chrome/browser/extensions/mock_extension_special_storage_policy.cc +++ b/chrome/browser/extensions/mock_extension_special_storage_policy.cc @@ -4,7 +4,9 @@ #include "chrome/browser/extensions/mock_extension_special_storage_policy.h" -MockExtensionSpecialStoragePolicy::MockExtensionSpecialStoragePolicy() {} +MockExtensionSpecialStoragePolicy::MockExtensionSpecialStoragePolicy() + : ExtensionSpecialStoragePolicy(NULL) {} + MockExtensionSpecialStoragePolicy::~MockExtensionSpecialStoragePolicy() {} bool MockExtensionSpecialStoragePolicy::IsStorageProtected(const GURL& origin) { @@ -15,6 +17,11 @@ bool MockExtensionSpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) { return unlimited_.find(origin) != unlimited_.end(); } +bool MockExtensionSpecialStoragePolicy::IsStorageSessionOnly( + const GURL& origin) { + return session_only_.find(origin) != session_only_.end(); +} + bool MockExtensionSpecialStoragePolicy::IsFileHandler( const std::string& extension_id) { return file_handlers_.find(extension_id) != file_handlers_.end(); diff --git a/chrome/browser/extensions/mock_extension_special_storage_policy.h b/chrome/browser/extensions/mock_extension_special_storage_policy.h index 0c481f1..b4ce548 100644 --- a/chrome/browser/extensions/mock_extension_special_storage_policy.h +++ b/chrome/browser/extensions/mock_extension_special_storage_policy.h @@ -22,6 +22,7 @@ class MockExtensionSpecialStoragePolicy : public ExtensionSpecialStoragePolicy { virtual bool IsStorageProtected(const GURL& origin); virtual bool IsStorageUnlimited(const GURL& origin); + virtual bool IsStorageSessionOnly(const GURL& origin); virtual bool IsFileHandler(const std::string& extension_id); void AddProtected(const GURL& origin) { @@ -32,6 +33,10 @@ class MockExtensionSpecialStoragePolicy : public ExtensionSpecialStoragePolicy { unlimited_.insert(origin); } + void AddSessionOnly(const GURL& origin) { + session_only_.insert(origin); + } + void AddFileHandler(const std::string& id) { file_handlers_.insert(id); } @@ -39,12 +44,14 @@ class MockExtensionSpecialStoragePolicy : public ExtensionSpecialStoragePolicy { void Reset() { protected_.clear(); unlimited_.clear(); + session_only_.clear(); file_handlers_.clear(); } private: std::set<GURL> protected_; std::set<GURL> unlimited_; + std::set<GURL> session_only_; std::set<std::string> file_handlers_; DISALLOW_COPY_AND_ASSIGN(MockExtensionSpecialStoragePolicy); diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 7790c47..f838d08 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -663,6 +663,9 @@ ProfileImpl::~ProfileImpl() { true)); } + if (webkit_context_.get()) + webkit_context_->DeleteSessionOnlyData(); + StopCreateSessionServiceTimer(); // Remove pref observers @@ -827,8 +830,10 @@ ExtensionEventRouter* ProfileImpl::GetExtensionEventRouter() { ExtensionSpecialStoragePolicy* ProfileImpl::GetExtensionSpecialStoragePolicy() { - if (!extension_special_storage_policy_.get()) - extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(); + if (!extension_special_storage_policy_.get()) { + extension_special_storage_policy_ = + new ExtensionSpecialStoragePolicy(GetHostContentSettingsMap()); + } return extension_special_storage_policy_.get(); } diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 5aaf31f..181bda7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2038,6 +2038,7 @@ '../content/browser/geolocation/win7_location_api_unittest_win.cc', '../content/browser/geolocation/win7_location_provider_unittest_win.cc', '../content/browser/gpu/gpu_blacklist_unittest.cc', + '../content/browser/in_process_webkit/dom_storage_unittest.cc', '../content/browser/in_process_webkit/indexed_db_quota_client_unittest.cc', '../content/browser/in_process_webkit/webkit_context_unittest.cc', '../content/browser/in_process_webkit/webkit_thread_unittest.cc', diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc index 1a7b131..2f3ecfa 100644 --- a/chrome/test/base/testing_profile.cc +++ b/chrome/test/base/testing_profile.cc @@ -440,7 +440,7 @@ void TestingProfile::SetExtensionSpecialStoragePolicy( ExtensionSpecialStoragePolicy* TestingProfile::GetExtensionSpecialStoragePolicy() { if (!extension_special_storage_policy_.get()) - extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(); + extension_special_storage_policy_ = new ExtensionSpecialStoragePolicy(NULL); return extension_special_storage_policy_.get(); } diff --git a/content/browser/in_process_webkit/dom_storage_context.cc b/content/browser/in_process_webkit/dom_storage_context.cc index 3daa66e..d86b089 100644 --- a/content/browser/in_process_webkit/dom_storage_context.cc +++ b/content/browser/in_process_webkit/dom_storage_context.cc @@ -198,6 +198,29 @@ void DOMStorageContext::DeleteDataModifiedSince(const base::Time& cutoff) { } } +void DOMStorageContext::DeleteSessionOnlyData() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); + + // Make sure that we don't delete a database that's currently being accessed + // by unloading all of the databases temporarily. + PurgeMemory(); + + file_util::FileEnumerator file_enumerator( + data_path_.Append(kLocalStorageDirectory), false, + file_util::FileEnumerator::FILES); + for (FilePath path = file_enumerator.Next(); !path.value().empty(); + path = file_enumerator.Next()) { + GURL origin(WebSecurityOrigin::createFromDatabaseIdentifier( + webkit_glue::FilePathToWebString(path.BaseName())).toString()); + if (!special_storage_policy_->IsStorageSessionOnly(origin)) + continue; + if (special_storage_policy_->IsStorageProtected(origin)) + continue; + + file_util::Delete(path, false); + } +} + void DOMStorageContext::DeleteLocalStorageFile(const FilePath& file_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); diff --git a/content/browser/in_process_webkit/dom_storage_context.h b/content/browser/in_process_webkit/dom_storage_context.h index 4b5e639..a23dc83 100644 --- a/content/browser/in_process_webkit/dom_storage_context.h +++ b/content/browser/in_process_webkit/dom_storage_context.h @@ -10,6 +10,7 @@ #include <set> #include "base/file_path.h" +#include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/string16.h" #include "base/time.h" @@ -80,6 +81,11 @@ class DOMStorageContext { // are not deleted by this method. void DeleteDataModifiedSince(const base::Time& cutoff); + // Delete any local storage files which are allowed to be stored only until + // the end of the session. Protected origins, per the SpecialStoragePolicy, + // are not deleted by this method. + void DeleteSessionOnlyData(); + // Deletes a single local storage file. void DeleteLocalStorageFile(const FilePath& file_path); @@ -108,6 +114,9 @@ class DOMStorageContext { #endif private: + + FRIEND_TEST_ALL_PREFIXES(DOMStorageTest, SessionOnly); + // Get the local storage instance. The object is owned by this class. DOMStorageNamespace* CreateLocalStorage(); diff --git a/content/browser/in_process_webkit/dom_storage_unittest.cc b/content/browser/in_process_webkit/dom_storage_unittest.cc new file mode 100644 index 0000000..14f5a30 --- /dev/null +++ b/content/browser/in_process_webkit/dom_storage_unittest.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2011 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 "base/file_path.h" +#include "base/file_util.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_browser_process_test.h" +#include "chrome/test/base/testing_profile.h" +#include "content/browser/in_process_webkit/webkit_context.h" +#include "webkit/quota/mock_special_storage_policy.h" + +class DOMStorageTest : public TestingBrowserProcessTest { + public: + DOMStorageTest() + : message_loop_(MessageLoop::TYPE_IO), + webkit_thread_(BrowserThread::WEBKIT, &message_loop_) { + } + protected: + MessageLoop message_loop_; + + private: + BrowserThread webkit_thread_; +}; + +TEST_F(DOMStorageTest, SessionOnly) { + GURL session_only_origin("http://www.sessiononly.com"); + scoped_refptr<quota::MockSpecialStoragePolicy> special_storage_policy = + new quota::MockSpecialStoragePolicy; + special_storage_policy->AddSessionOnly(session_only_origin); + + TestingProfile profile; + + // Create databases for permanent and session-only origins. + FilePath domstorage_dir = profile.GetPath().Append( + DOMStorageContext::kLocalStorageDirectory); + FilePath::StringType session_only_database( + FILE_PATH_LITERAL("http_www.sessiononly.com_0")); + FilePath::StringType permanent_database( + FILE_PATH_LITERAL("http_www.permanent.com_0")); + session_only_database.append(DOMStorageContext::kLocalStorageExtension); + permanent_database.append(DOMStorageContext::kLocalStorageExtension); + FilePath session_only_database_path = + domstorage_dir.Append(session_only_database); + FilePath permanent_database_path = + domstorage_dir.Append(permanent_database); + + ASSERT_TRUE(file_util::CreateDirectory(domstorage_dir)); + + ASSERT_EQ(1, file_util::WriteFile(session_only_database_path, ".", 1)); + ASSERT_EQ(1, file_util::WriteFile(permanent_database_path, ".", 1)); + + // Inject MockSpecialStoragePolicy into DOMStorageContext. + profile.GetWebKitContext()->dom_storage_context()->special_storage_policy_ = + special_storage_policy; + + // Tell the WebKitContext explicitly do clean up the session-only + // data. TestingProfile (unlike ProfileImpl) doesn't do it automatically when + // destructed. The deletion is done immediately since we're on the WEBKIT + // thread (created by the test). + profile.GetWebKitContext()->DeleteSessionOnlyData(); + + // Expected result: the database file for the permanent storage remains but + // the database for the session only storage was deleted. + EXPECT_FALSE(file_util::PathExists(session_only_database_path)); + EXPECT_TRUE(file_util::PathExists(permanent_database_path)); +} diff --git a/content/browser/in_process_webkit/webkit_context.cc b/content/browser/in_process_webkit/webkit_context.cc index 6359916..8fd9d11 100644 --- a/content/browser/in_process_webkit/webkit_context.cc +++ b/content/browser/in_process_webkit/webkit_context.cc @@ -66,6 +66,16 @@ void WebKitContext::DeleteDataModifiedSince(const base::Time& cutoff) { dom_storage_context_->DeleteDataModifiedSince(cutoff); } +void WebKitContext::DeleteSessionOnlyData() { + if (!BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)) { + BrowserThread::PostTask( + BrowserThread::WEBKIT, FROM_HERE, + NewRunnableMethod(this, &WebKitContext::DeleteSessionOnlyData)); + return; + } + + dom_storage_context_->DeleteSessionOnlyData(); +} void WebKitContext::DeleteSessionStorageNamespace( int64 session_storage_namespace_id) { diff --git a/content/browser/in_process_webkit/webkit_context.h b/content/browser/in_process_webkit/webkit_context.h index 6992d77..8d404ce 100644 --- a/content/browser/in_process_webkit/webkit_context.h +++ b/content/browser/in_process_webkit/webkit_context.h @@ -69,6 +69,10 @@ class WebKitContext : public base::RefCountedThreadSafe<WebKitContext> { // last modified on or after the following time. void DeleteDataModifiedSince(const base::Time& cutoff); + // Tell all children (where applicable) to delete any objects that are allowed + // to be stored only until the end of the session. + void DeleteSessionOnlyData(); + // Delete the session storage namespace associated with this id. Can be // called from any thread. void DeleteSessionStorageNamespace(int64 session_storage_namespace_id); diff --git a/webkit/quota/mock_special_storage_policy.cc b/webkit/quota/mock_special_storage_policy.cc index 8b75073..5787be9 100644 --- a/webkit/quota/mock_special_storage_policy.cc +++ b/webkit/quota/mock_special_storage_policy.cc @@ -21,6 +21,10 @@ bool MockSpecialStoragePolicy::IsStorageUnlimited(const GURL& origin) { return unlimited_.find(origin) != unlimited_.end(); } +bool MockSpecialStoragePolicy::IsStorageSessionOnly(const GURL& origin) { + return session_only_.find(origin) != session_only_.end(); +} + bool MockSpecialStoragePolicy::IsFileHandler(const std::string& extension_id) { return file_handlers_.find(extension_id) != file_handlers_.end(); } diff --git a/webkit/quota/mock_special_storage_policy.h b/webkit/quota/mock_special_storage_policy.h index b04d16d..7e54a6e 100644 --- a/webkit/quota/mock_special_storage_policy.h +++ b/webkit/quota/mock_special_storage_policy.h @@ -19,6 +19,7 @@ class MockSpecialStoragePolicy : public quota::SpecialStoragePolicy { virtual bool IsStorageProtected(const GURL& origin); virtual bool IsStorageUnlimited(const GURL& origin); + virtual bool IsStorageSessionOnly(const GURL& origin); virtual bool IsFileHandler(const std::string& extension_id); void AddProtected(const GURL& origin) { @@ -29,6 +30,10 @@ class MockSpecialStoragePolicy : public quota::SpecialStoragePolicy { unlimited_.insert(origin); } + void AddSessionOnly(const GURL& origin) { + session_only_.insert(origin); + } + void AddFileHandler(const std::string& id) { file_handlers_.insert(id); } @@ -40,7 +45,9 @@ class MockSpecialStoragePolicy : public quota::SpecialStoragePolicy { void Reset() { protected_.clear(); unlimited_.clear(); + session_only_.clear(); file_handlers_.clear(); + all_unlimited_ = false; } void NotifyChanged() { @@ -50,6 +57,7 @@ class MockSpecialStoragePolicy : public quota::SpecialStoragePolicy { private: std::set<GURL> protected_; std::set<GURL> unlimited_; + std::set<GURL> session_only_; std::set<std::string> file_handlers_; bool all_unlimited_; diff --git a/webkit/quota/special_storage_policy.h b/webkit/quota/special_storage_policy.h index cc1af6b..ed8989c 100644 --- a/webkit/quota/special_storage_policy.h +++ b/webkit/quota/special_storage_policy.h @@ -42,6 +42,10 @@ class SpecialStoragePolicy // file handler. virtual bool IsFileHandler(const std::string& extension_id) = 0; + // Some origins are only allowed to store session-only data which is deleted + // when the session ends. + virtual bool IsStorageSessionOnly(const GURL& origin) = 0; + // Adds/removes an observer, the policy does not take // ownership of the observer. Should only be called on the IO thread. void AddObserver(Observer* observer); |