diff options
author | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-11 23:30:25 +0000 |
---|---|---|
committer | tburkard@chromium.org <tburkard@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-11 23:30:25 +0000 |
commit | 458061b6c2e1d299689264922b7a274c3963677b (patch) | |
tree | 6f9e1f9274140cd1e2c298c5c027d36b66ab3cbe /content/browser/dom_storage | |
parent | 85181aab0f6d85c4f79b82872a2f6eb4bbd63aea (diff) | |
download | chromium_src-458061b6c2e1d299689264922b7a274c3963677b.zip chromium_src-458061b6c2e1d299689264922b7a274c3963677b.tar.gz chromium_src-458061b6c2e1d299689264922b7a274c3963677b.tar.bz2 |
Allow aliasing and merging of session storage namespaces.
R=jam@chromium.org, marja@chromium.org, michaeln@chromium.org
Review URL: https://codereview.chromium.org/62923003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234317 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/dom_storage')
13 files changed, 496 insertions, 46 deletions
diff --git a/content/browser/dom_storage/dom_storage_context_impl.cc b/content/browser/dom_storage/dom_storage_context_impl.cc index 98eab63..44c4242 100644 --- a/content/browser/dom_storage/dom_storage_context_impl.cc +++ b/content/browser/dom_storage/dom_storage_context_impl.cc @@ -238,6 +238,16 @@ void DOMStorageContextImpl::NotifyAreaCleared( OnDOMStorageAreaCleared(area, page_url)); } +void DOMStorageContextImpl::NotifyAliasSessionMerged( + int64 namespace_id, + DOMStorageNamespace* old_alias_master_namespace) { + FOR_EACH_OBSERVER( + EventObserver, event_observers_, + OnDOMSessionStorageReset(namespace_id)); + if (old_alias_master_namespace) + MaybeShutdownSessionNamespace(old_alias_master_namespace); +} + std::string DOMStorageContextImpl::AllocatePersistentSessionId() { std::string guid = base::GenerateGUID(); std::replace(guid.begin(), guid.end(), '-', '_'); @@ -262,11 +272,36 @@ void DOMStorageContextImpl::DeleteSessionNamespace( int64 namespace_id, bool should_persist_data) { DCHECK_NE(kLocalStorageNamespaceId, namespace_id); StorageNamespaceMap::const_iterator it = namespaces_.find(namespace_id); - if (it == namespaces_.end()) + if (it == namespaces_.end() || + it->second->ready_for_deletion_pending_aliases()) { + return; + } + it->second->set_ready_for_deletion_pending_aliases(true); + DOMStorageNamespace* alias_master = it->second->alias_master_namespace(); + if (alias_master) { + DCHECK(it->second->num_aliases() == 0); + DCHECK(alias_master->alias_master_namespace() == NULL); + if (should_persist_data) + alias_master->set_must_persist_at_shutdown(true); + if (it->second->DecrementMasterAliasCount()) + MaybeShutdownSessionNamespace(alias_master); + namespaces_.erase(namespace_id); + } else { + if (should_persist_data) + it->second->set_must_persist_at_shutdown(true); + MaybeShutdownSessionNamespace(it->second); + } +} + +void DOMStorageContextImpl::MaybeShutdownSessionNamespace( + DOMStorageNamespace* ns) { + if (ns->num_aliases() > 0 || !ns->ready_for_deletion_pending_aliases()) return; - std::string persistent_namespace_id = it->second->persistent_namespace_id(); + DCHECK_EQ(ns->num_aliases(), 0); + DCHECK(ns->alias_master_namespace() == NULL); + std::string persistent_namespace_id = ns->persistent_namespace_id(); if (session_storage_database_.get()) { - if (!should_persist_data) { + if (!ns->must_persist_at_shutdown()) { task_runner_->PostShutdownBlockingTask( FROM_HERE, DOMStorageTaskRunner::COMMIT_SEQUENCE, @@ -276,7 +311,7 @@ void DOMStorageContextImpl::DeleteSessionNamespace( persistent_namespace_id)); } else { // Ensure that the data gets committed before we shut down. - it->second->Shutdown(); + ns->Shutdown(); if (!scavenging_started_) { // Protect the persistent namespace ID from scavenging. protected_persistent_session_ids_.insert(persistent_namespace_id); @@ -284,7 +319,7 @@ void DOMStorageContextImpl::DeleteSessionNamespace( } } persistent_namespace_id_to_namespace_id_.erase(persistent_namespace_id); - namespaces_.erase(namespace_id); + namespaces_.erase(ns->namespace_id()); } void DOMStorageContextImpl::CloneSessionNamespace( @@ -301,6 +336,21 @@ void DOMStorageContextImpl::CloneSessionNamespace( CreateSessionNamespace(new_id, new_persistent_id); } +void DOMStorageContextImpl::CreateAliasSessionNamespace( + int64 existing_id, int64 new_id, + const std::string& persistent_id) { + if (is_shutdown_) + return; + DCHECK_NE(kLocalStorageNamespaceId, existing_id); + DCHECK_NE(kLocalStorageNamespaceId, new_id); + StorageNamespaceMap::iterator found = namespaces_.find(existing_id); + if (found != namespaces_.end()) { + namespaces_[new_id] = found->second->CreateAlias(new_id); + } else { + CreateSessionNamespace(new_id, persistent_id); + } +} + void DOMStorageContextImpl::ClearSessionOnlyOrigins() { if (!localstorage_directory_.empty()) { std::vector<LocalStorageUsageInfo> infos; @@ -438,8 +488,9 @@ void DOMStorageContextImpl::RemoveTransactionLogProcessId(int64 namespace_id, } SessionStorageNamespace::MergeResult -DOMStorageContextImpl::CanMergeSessionStorage( - int64 namespace1_id, int process_id, int64 namespace2_id) { +DOMStorageContextImpl::MergeSessionStorage( + int64 namespace1_id, bool actually_merge, int process_id, + int64 namespace2_id) { DCHECK_NE(kLocalStorageNamespaceId, namespace1_id); DCHECK_NE(kLocalStorageNamespaceId, namespace2_id); StorageNamespaceMap::const_iterator it = namespaces_.find(namespace1_id); @@ -450,7 +501,7 @@ DOMStorageContextImpl::CanMergeSessionStorage( if (it == namespaces_.end()) return SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_FOUND; DOMStorageNamespace* ns2 = it->second; - return ns1->CanMerge(process_id, ns2); + return ns1->Merge(actually_merge, process_id, ns2, this); } } // namespace content diff --git a/content/browser/dom_storage/dom_storage_context_impl.h b/content/browser/dom_storage/dom_storage_context_impl.h index c56024b..a9400fb 100644 --- a/content/browser/dom_storage/dom_storage_context_impl.h +++ b/content/browser/dom_storage/dom_storage_context_impl.h @@ -16,6 +16,7 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "base/time/time.h" +#include "content/browser/dom_storage/dom_storage_namespace.h" #include "content/common/content_export.h" #include "content/public/browser/session_storage_namespace.h" #include "url/gurl.h" @@ -33,7 +34,6 @@ class SpecialStoragePolicy; namespace content { class DOMStorageArea; -class DOMStorageNamespace; class DOMStorageSession; class DOMStorageTaskRunner; class SessionStorageDatabase; @@ -81,6 +81,9 @@ class CONTENT_EXPORT DOMStorageContextImpl virtual void OnDOMStorageAreaCleared( const DOMStorageArea* area, const GURL& page_url) = 0; + // Indicates that cached values of the DOM Storage provided must be + // cleared and retrieved again. + virtual void OnDOMSessionStorageReset(int64 namespace_id) = 0; protected: virtual ~EventObserver() {} @@ -134,6 +137,7 @@ class CONTENT_EXPORT DOMStorageContextImpl // Methods to add, remove, and notify EventObservers. void AddEventObserver(EventObserver* observer); void RemoveEventObserver(EventObserver* observer); + void NotifyItemSet( const DOMStorageArea* area, const base::string16& key, @@ -148,6 +152,9 @@ class CONTENT_EXPORT DOMStorageContextImpl void NotifyAreaCleared( const DOMStorageArea* area, const GURL& page_url); + void NotifyAliasSessionMerged( + int64 namespace_id, + DOMStorageNamespace* old_alias_master_namespace); // May be called on any thread. int64 AllocateSessionId() { @@ -162,6 +169,8 @@ class CONTENT_EXPORT DOMStorageContextImpl void DeleteSessionNamespace(int64 namespace_id, bool should_persist_data); void CloneSessionNamespace(int64 existing_id, int64 new_id, const std::string& new_persistent_id); + void CreateAliasSessionNamespace(int64 existing_id, int64 new_id, + const std::string& persistent_id); // Starts backing sessionStorage on disk. This function must be called right // after DOMStorageContextImpl is created, before it's used. @@ -175,8 +184,9 @@ class CONTENT_EXPORT DOMStorageContextImpl void AddTransactionLogProcessId(int64 namespace_id, int process_id); void RemoveTransactionLogProcessId(int64 namespace_id, int process_id); - SessionStorageNamespace::MergeResult CanMergeSessionStorage( - int64 namespace1_id, int process_id, int64 namespace2_id); + SessionStorageNamespace::MergeResult MergeSessionStorage( + int64 namespace1_id, bool actually_merge, int process_id, + int64 namespace2_id); private: friend class DOMStorageContextImplTest; @@ -185,10 +195,12 @@ class CONTENT_EXPORT DOMStorageContextImpl typedef std::map<int64, scoped_refptr<DOMStorageNamespace> > StorageNamespaceMap; - ~DOMStorageContextImpl(); + virtual ~DOMStorageContextImpl(); void ClearSessionOnlyOrigins(); + void MaybeShutdownSessionNamespace(DOMStorageNamespace* ns); + // For scavenging unused sessionStorages. void FindUnusedNamespaces(); void FindUnusedNamespacesInCommitSequence( diff --git a/content/browser/dom_storage/dom_storage_context_impl_unittest.cc b/content/browser/dom_storage/dom_storage_context_impl_unittest.cc index 27aaefb2..ba8d8e9 100644 --- a/content/browser/dom_storage/dom_storage_context_impl_unittest.cc +++ b/content/browser/dom_storage/dom_storage_context_impl_unittest.cc @@ -15,6 +15,7 @@ #include "content/browser/dom_storage/dom_storage_namespace.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/public/browser/local_storage_usage_info.h" +#include "content/public/browser/session_storage_namespace.h" #include "content/public/browser/session_storage_usage_info.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/browser/quota/mock_special_storage_policy.h" @@ -260,4 +261,115 @@ TEST_F(DOMStorageContextImplTest, DeleteSessionStorage) { base::MessageLoop::current()->RunUntilIdle(); } +TEST_F(DOMStorageContextImplTest, SessionStorageAlias) { + const int kFirstSessionStorageNamespaceId = 1; + const std::string kPersistentId = "persistent"; + context_->CreateSessionNamespace(kFirstSessionStorageNamespaceId, + kPersistentId); + DOMStorageNamespace* dom_namespace1 = + context_->GetStorageNamespace(kFirstSessionStorageNamespaceId); + ASSERT_TRUE(dom_namespace1); + DOMStorageArea* area1 = dom_namespace1->OpenStorageArea(kOrigin); + base::NullableString16 old_value; + area1->SetItem(kKey, kValue, &old_value); + EXPECT_TRUE(old_value.is_null()); + base::NullableString16 read_value = area1->GetItem(kKey); + EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue); + + // Create an alias. + const int kAliasSessionStorageNamespaceId = 2; + context_->CreateAliasSessionNamespace(kFirstSessionStorageNamespaceId, + kAliasSessionStorageNamespaceId, + kPersistentId); + DOMStorageNamespace* dom_namespace2 = + context_->GetStorageNamespace(kAliasSessionStorageNamespaceId); + ASSERT_TRUE(dom_namespace2); + ASSERT_TRUE(dom_namespace2->alias_master_namespace() == dom_namespace1); + + // Verify that read values are identical. + DOMStorageArea* area2 = dom_namespace2->OpenStorageArea(kOrigin); + read_value = area2->GetItem(kKey); + EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue); + + // Verify that writes are reflected in both namespaces. + const base::string16 kValue2(ASCIIToUTF16("value2")); + area2->SetItem(kKey, kValue2, &old_value); + read_value = area1->GetItem(kKey); + EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue2); + dom_namespace1->CloseStorageArea(area1); + dom_namespace2->CloseStorageArea(area2); + + // When creating an alias of an alias, ensure that the master relationship + // is collapsed. + const int kAlias2SessionStorageNamespaceId = 3; + context_->CreateAliasSessionNamespace(kAliasSessionStorageNamespaceId, + kAlias2SessionStorageNamespaceId, + kPersistentId); + DOMStorageNamespace* dom_namespace3 = + context_->GetStorageNamespace(kAlias2SessionStorageNamespaceId); + ASSERT_TRUE(dom_namespace3); + ASSERT_TRUE(dom_namespace3->alias_master_namespace() == dom_namespace1); +} + +TEST_F(DOMStorageContextImplTest, SessionStorageMerge) { + // Create a target namespace that we will merge into. + const int kTargetSessionStorageNamespaceId = 1; + const std::string kTargetPersistentId = "persistent"; + context_->CreateSessionNamespace(kTargetSessionStorageNamespaceId, + kTargetPersistentId); + DOMStorageNamespace* target_ns = + context_->GetStorageNamespace(kTargetSessionStorageNamespaceId); + ASSERT_TRUE(target_ns); + DOMStorageArea* target_ns_area = target_ns->OpenStorageArea(kOrigin); + base::NullableString16 old_value; + const base::string16 kKey2(ASCIIToUTF16("key2")); + const base::string16 kKey2Value(ASCIIToUTF16("key2value")); + target_ns_area->SetItem(kKey, kValue, &old_value); + target_ns_area->SetItem(kKey2, kKey2Value, &old_value); + + // Create a source namespace & its alias. + const int kSourceSessionStorageNamespaceId = 2; + const int kAliasSessionStorageNamespaceId = 3; + const std::string kSourcePersistentId = "persistent_source"; + context_->CreateSessionNamespace(kSourceSessionStorageNamespaceId, + kSourcePersistentId); + context_->CreateAliasSessionNamespace(kSourceSessionStorageNamespaceId, + kAliasSessionStorageNamespaceId, + kSourcePersistentId); + DOMStorageNamespace* alias_ns = + context_->GetStorageNamespace(kAliasSessionStorageNamespaceId); + ASSERT_TRUE(alias_ns); + + // Create a transaction log that can't be merged. + const int kPid1 = 10; + ASSERT_FALSE(alias_ns->IsLoggingRenderer(kPid1)); + alias_ns->AddTransactionLogProcessId(kPid1); + ASSERT_TRUE(alias_ns->IsLoggingRenderer(kPid1)); + const base::string16 kValue2(ASCIIToUTF16("value2")); + DOMStorageNamespace::TransactionRecord txn; + txn.origin = kOrigin; + txn.key = kKey; + txn.value = base::NullableString16(kValue2, false); + txn.transaction_type = DOMStorageNamespace::TRANSACTION_READ; + alias_ns->AddTransaction(kPid1, txn); + ASSERT_TRUE(alias_ns->Merge(false, kPid1, target_ns, NULL) == + SessionStorageNamespace::MERGE_RESULT_NOT_MERGEABLE); + + // Create a transaction log that can be merged. + const int kPid2 = 20; + alias_ns->AddTransactionLogProcessId(kPid2); + txn.transaction_type = DOMStorageNamespace::TRANSACTION_WRITE; + alias_ns->AddTransaction(kPid2, txn); + ASSERT_TRUE(alias_ns->Merge(true, kPid2, target_ns, NULL) == + SessionStorageNamespace::MERGE_RESULT_MERGEABLE); + + // Verify that the merge was successful. + ASSERT_TRUE(alias_ns->alias_master_namespace() == target_ns); + base::NullableString16 read_value = target_ns_area->GetItem(kKey); + EXPECT_TRUE(!read_value.is_null() && read_value.string() == kValue2); + DOMStorageArea* alias_ns_area = alias_ns->OpenStorageArea(kOrigin); + read_value = alias_ns_area->GetItem(kKey2); + EXPECT_TRUE(!read_value.is_null() && read_value.string() == kKey2Value); +} + } // namespace content diff --git a/content/browser/dom_storage/dom_storage_host.cc b/content/browser/dom_storage/dom_storage_host.cc index de57be2..556f76d 100644 --- a/content/browser/dom_storage/dom_storage_host.cc +++ b/content/browser/dom_storage/dom_storage_host.cc @@ -107,7 +107,7 @@ bool DOMStorageHost::SetAreaItem( context_->NotifyItemSet(area, key, value, *old_value, page_url); MaybeLogTransaction(connection_id, DOMStorageNamespace::TRANSACTION_WRITE, - area->origin(), key, + area->origin(), page_url, key, base::NullableString16(value, false)); return true; } @@ -120,7 +120,7 @@ void DOMStorageHost::LogGetAreaItem( return; MaybeLogTransaction(connection_id, DOMStorageNamespace::TRANSACTION_READ, - area->origin(), key, value); + area->origin(), GURL(), key, value); } bool DOMStorageHost::RemoveAreaItem( @@ -134,7 +134,7 @@ bool DOMStorageHost::RemoveAreaItem( context_->NotifyItemRemoved(area, key, *old_value, page_url); MaybeLogTransaction(connection_id, DOMStorageNamespace::TRANSACTION_REMOVE, - area->origin(), key, base::NullableString16()); + area->origin(), page_url, key, base::NullableString16()); return true; } @@ -147,23 +147,38 @@ bool DOMStorageHost::ClearArea(int connection_id, const GURL& page_url) { context_->NotifyAreaCleared(area, page_url); MaybeLogTransaction(connection_id, DOMStorageNamespace::TRANSACTION_CLEAR, - area->origin(), base::string16(), + area->origin(), page_url, base::string16(), base::NullableString16()); return true; } bool DOMStorageHost::HasAreaOpen( - int namespace_id, const GURL& origin) const { + int64 namespace_id, const GURL& origin, int64* alias_namespace_id) const { AreaMap::const_iterator it = connections_.begin(); for (; it != connections_.end(); ++it) { - if (namespace_id == it->second.namespace_->namespace_id() && + if (namespace_id == it->second.area_->namespace_id() && origin == it->second.area_->origin()) { + *alias_namespace_id = it->second.namespace_->namespace_id(); return true; } } return false; } +bool DOMStorageHost::ResetOpenAreasForNamespace(int64 namespace_id) { + bool result = false; + AreaMap::iterator it = connections_.begin(); + for (; it != connections_.end(); ++it) { + if (namespace_id == it->second.namespace_->namespace_id()) { + GURL origin = it->second.area_->origin(); + it->second.namespace_->CloseStorageArea(it->second.area_.get()); + it->second.area_ = it->second.namespace_->OpenStorageArea(origin); + result = true; + } + } + return result; +} + DOMStorageArea* DOMStorageHost::GetOpenArea(int connection_id) { AreaMap::iterator found = connections_.find(connection_id); if (found == connections_.end()) @@ -182,6 +197,7 @@ void DOMStorageHost::MaybeLogTransaction( int connection_id, DOMStorageNamespace::LogType transaction_type, const GURL& origin, + const GURL& page_url, const base::string16& key, const base::NullableString16& value) { DOMStorageNamespace* ns = GetNamespace(connection_id); @@ -191,6 +207,7 @@ void DOMStorageHost::MaybeLogTransaction( DOMStorageNamespace::TransactionRecord transaction; transaction.transaction_type = transaction_type; transaction.origin = origin; + transaction.page_url = page_url; transaction.key = key; transaction.value = value; ns->AddTransaction(render_process_id_, transaction); diff --git a/content/browser/dom_storage/dom_storage_host.h b/content/browser/dom_storage/dom_storage_host.h index f10bb29..f4ca784 100644 --- a/content/browser/dom_storage/dom_storage_host.h +++ b/content/browser/dom_storage/dom_storage_host.h @@ -51,7 +51,11 @@ class CONTENT_EXPORT DOMStorageHost { const GURL& page_url, base::string16* old_value); bool ClearArea(int connection_id, const GURL& page_url); - bool HasAreaOpen(int namespace_id, const GURL& origin) const; + bool HasAreaOpen(int64 namespace_id, const GURL& origin, + int64* alias_namespace_id) const; + // Resets all open areas for the namespace provided. Returns true + // iff there were any areas to reset. + bool ResetOpenAreasForNamespace(int64 namespace_id); private: // Struct to hold references needed for areas that are open @@ -70,6 +74,7 @@ class CONTENT_EXPORT DOMStorageHost { int connection_id, DOMStorageNamespace::LogType transaction_type, const GURL& origin, + const GURL& page_url, const base::string16& key, const base::NullableString16& value); diff --git a/content/browser/dom_storage/dom_storage_message_filter.cc b/content/browser/dom_storage/dom_storage_message_filter.cc index ce5a8c1..3ab972c 100644 --- a/content/browser/dom_storage/dom_storage_message_filter.cc +++ b/content/browser/dom_storage/dom_storage_message_filter.cc @@ -193,6 +193,11 @@ void DOMStorageMessageFilter::OnDOMStorageAreaCleared( base::NullableString16()); } +void DOMStorageMessageFilter::OnDOMSessionStorageReset(int64 namespace_id) { + if (host_->ResetOpenAreasForNamespace(namespace_id)) + Send(new DOMStorageMsg_ResetCachedValues(namespace_id)); +} + void DOMStorageMessageFilter::SendDOMStorageEvent( const DOMStorageArea* area, const GURL& page_url, @@ -202,8 +207,10 @@ void DOMStorageMessageFilter::SendDOMStorageEvent( DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); // Only send mutation events to processes which have the area open. bool originated_in_process = connection_dispatching_message_for_ != 0; - if (originated_in_process || - host_->HasAreaOpen(area->namespace_id(), area->origin())) { + int64 alias_namespace_id = area->namespace_id(); + if (host_->HasAreaOpen(area->namespace_id(), area->origin(), + &alias_namespace_id) || + originated_in_process) { DOMStorageMsg_Event_Params params; params.origin = area->origin(); params.page_url = page_url; @@ -211,7 +218,7 @@ void DOMStorageMessageFilter::SendDOMStorageEvent( params.key = key; params.new_value = new_value; params.old_value = old_value; - params.namespace_id = area->namespace_id(); + params.namespace_id = alias_namespace_id; Send(new DOMStorageMsg_Event(params)); } } diff --git a/content/browser/dom_storage/dom_storage_message_filter.h b/content/browser/dom_storage/dom_storage_message_filter.h index 7856d91..a2670ef 100644 --- a/content/browser/dom_storage/dom_storage_message_filter.h +++ b/content/browser/dom_storage/dom_storage_message_filter.h @@ -78,6 +78,7 @@ class DOMStorageMessageFilter virtual void OnDOMStorageAreaCleared( const DOMStorageArea* area, const GURL& page_url) OVERRIDE; + virtual void OnDOMSessionStorageReset(int64 namespace_id) OVERRIDE; void SendDOMStorageEvent( const DOMStorageArea* area, diff --git a/content/browser/dom_storage/dom_storage_namespace.cc b/content/browser/dom_storage/dom_storage_namespace.cc index 56cdb69..5672930 100644 --- a/content/browser/dom_storage/dom_storage_namespace.cc +++ b/content/browser/dom_storage/dom_storage_namespace.cc @@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/stl_util.h" #include "content/browser/dom_storage/dom_storage_area.h" +#include "content/browser/dom_storage/dom_storage_context_impl.h" #include "content/browser/dom_storage/dom_storage_task_runner.h" #include "content/browser/dom_storage/session_storage_database.h" #include "content/common/child_process_host_impl.h" @@ -31,7 +32,11 @@ DOMStorageNamespace::DOMStorageNamespace( DOMStorageTaskRunner* task_runner) : namespace_id_(kLocalStorageNamespaceId), directory_(directory), - task_runner_(task_runner) { + task_runner_(task_runner), + num_aliases_(0), + master_alias_count_decremented_(false), + ready_for_deletion_pending_aliases_(false), + must_persist_at_shutdown_(false) { } DOMStorageNamespace::DOMStorageNamespace( @@ -42,15 +47,22 @@ DOMStorageNamespace::DOMStorageNamespace( : namespace_id_(namespace_id), persistent_namespace_id_(persistent_namespace_id), task_runner_(task_runner), - session_storage_database_(session_storage_database) { + session_storage_database_(session_storage_database), + num_aliases_(0), + master_alias_count_decremented_(false), + ready_for_deletion_pending_aliases_(false), + must_persist_at_shutdown_(false) { DCHECK_NE(kLocalStorageNamespaceId, namespace_id); } DOMStorageNamespace::~DOMStorageNamespace() { STLDeleteValues(&transactions_); + DecrementMasterAliasCount(); } DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) { + if (alias_master_namespace_) + return alias_master_namespace_->OpenStorageArea(origin); if (AreaHolder* holder = GetAreaHolder(origin)) { ++(holder->open_count_); return holder->area_.get(); @@ -69,6 +81,11 @@ DOMStorageArea* DOMStorageNamespace::OpenStorageArea(const GURL& origin) { void DOMStorageNamespace::CloseStorageArea(DOMStorageArea* area) { AreaHolder* holder = GetAreaHolder(area->origin()); + if (alias_master_namespace_) { + DCHECK(!holder); + alias_master_namespace_->CloseStorageArea(area); + return; + } DCHECK(holder); DCHECK_EQ(holder->area_.get(), area); --(holder->open_count_); @@ -77,6 +94,8 @@ void DOMStorageNamespace::CloseStorageArea(DOMStorageArea* area) { } DOMStorageArea* DOMStorageNamespace::GetOpenStorageArea(const GURL& origin) { + if (alias_master_namespace_) + return alias_master_namespace_->GetOpenStorageArea(origin); AreaHolder* holder = GetAreaHolder(origin); if (holder && holder->open_count_) return holder->area_.get(); @@ -86,6 +105,10 @@ DOMStorageArea* DOMStorageNamespace::GetOpenStorageArea(const GURL& origin) { DOMStorageNamespace* DOMStorageNamespace::Clone( int64 clone_namespace_id, const std::string& clone_persistent_namespace_id) { + if (alias_master_namespace_) { + return alias_master_namespace_->Clone(clone_namespace_id, + clone_persistent_namespace_id); + } DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); DCHECK_NE(kLocalStorageNamespaceId, clone_namespace_id); DOMStorageNamespace* clone = new DOMStorageNamespace( @@ -110,8 +133,34 @@ DOMStorageNamespace* DOMStorageNamespace::Clone( return clone; } +DOMStorageNamespace* DOMStorageNamespace::CreateAlias( + int64 alias_namespace_id) { + // Creates an alias of the current DOMStorageNamespace. + // The alias will have a reference to this namespace (called the master), + // and all operations will be redirected to the master (in particular, + // the alias will never open any areas of its own, but always redirect + // to the master). Accordingly, an alias will also never undergo the shutdown + // procedure which initiates persisting to disk, since there is never any data + // of its own to persist to disk. DOMStorageContextImpl is the place where + // shutdowns are initiated, but only for non-alias DOMStorageNamespaces. + DCHECK_NE(kLocalStorageNamespaceId, namespace_id_); + DCHECK_NE(kLocalStorageNamespaceId, alias_namespace_id); + DOMStorageNamespace* alias = new DOMStorageNamespace( + alias_namespace_id, persistent_namespace_id_, + session_storage_database_.get(), task_runner_.get()); + if (alias_master_namespace_ != NULL) { + DCHECK(alias_master_namespace_->alias_master_namespace_ == NULL); + alias->alias_master_namespace_ = alias_master_namespace_; + } else { + alias->alias_master_namespace_ = this; + } + alias->alias_master_namespace_->num_aliases_++; + return alias; +} + void DOMStorageNamespace::DeleteLocalStorageOrigin(const GURL& origin) { DCHECK(!session_storage_database_.get()); + DCHECK(!alias_master_namespace_.get()); AreaHolder* holder = GetAreaHolder(origin); if (holder) { holder->area_->DeleteOrigin(); @@ -125,12 +174,20 @@ void DOMStorageNamespace::DeleteLocalStorageOrigin(const GURL& origin) { } void DOMStorageNamespace::DeleteSessionStorageOrigin(const GURL& origin) { + if (alias_master_namespace_) { + alias_master_namespace_->DeleteSessionStorageOrigin(origin); + return; + } DOMStorageArea* area = OpenStorageArea(origin); area->FastClear(); CloseStorageArea(area); } void DOMStorageNamespace::PurgeMemory(PurgeOption option) { + if (alias_master_namespace_) { + alias_master_namespace_->PurgeMemory(option); + return; + } if (directory_.empty()) return; // We can't purge w/o backing on disk. AreaMap::iterator it = areas_.begin(); @@ -166,6 +223,8 @@ void DOMStorageNamespace::Shutdown() { } unsigned int DOMStorageNamespace::CountInMemoryAreas() const { + if (alias_master_namespace_) + return alias_master_namespace_->CountInMemoryAreas(); unsigned int area_count = 0; for (AreaMap::const_iterator it = areas_.begin(); it != areas_.end(); ++it) { if (it->second.area_->IsLoadedInMemory()) @@ -196,16 +255,23 @@ void DOMStorageNamespace::RemoveTransactionLogProcessId(int process_id) { transactions_.erase(process_id); } -SessionStorageNamespace::MergeResult DOMStorageNamespace::CanMerge( +SessionStorageNamespace::MergeResult DOMStorageNamespace::Merge( + bool actually_merge, int process_id, - DOMStorageNamespace* other) { + DOMStorageNamespace* other, + DOMStorageContextImpl* context) { + if (!alias_master_namespace()) + return SessionStorageNamespace::MERGE_RESULT_NAMESPACE_NOT_ALIAS; if (transactions_.count(process_id) < 1) return SessionStorageNamespace::MERGE_RESULT_NOT_LOGGING; TransactionData* data = transactions_[process_id]; if (data->max_log_size_exceeded) return SessionStorageNamespace::MERGE_RESULT_TOO_MANY_TRANSACTIONS; - if (data->log.size() < 1) + if (data->log.size() < 1) { + if (actually_merge) + SwitchToNewAliasMaster(other, context); return SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS; + } // skip_areas and skip_keys store areas and (area, key) pairs, respectively, // that have already been handled previously. Any further modifications to @@ -242,6 +308,42 @@ SessionStorageNamespace::MergeResult DOMStorageNamespace::CanMerge( } NOTREACHED(); } + if (!actually_merge) + return SessionStorageNamespace::MERGE_RESULT_MERGEABLE; + + // Actually perform the merge. + + for (unsigned int i = 0; i < data->log.size(); i++) { + TransactionRecord& transaction = data->log[i]; + if (transaction.transaction_type == TRANSACTION_READ) + continue; + DOMStorageArea* area = other->OpenStorageArea(transaction.origin); + if (transaction.transaction_type == TRANSACTION_CLEAR) { + area->Clear(); + if (context) + context->NotifyAreaCleared(area, transaction.page_url); + } + if (transaction.transaction_type == TRANSACTION_REMOVE) { + base::string16 old_value; + area->RemoveItem(transaction.key, &old_value); + if (context) { + context->NotifyItemRemoved(area, transaction.key, old_value, + transaction.page_url); + } + } + if (transaction.transaction_type == TRANSACTION_WRITE) { + base::NullableString16 old_value; + area->SetItem(transaction.key, base::string16(transaction.value.string()), + &old_value); + if (context) { + context->NotifyItemSet(area, transaction.key,transaction.value.string(), + old_value, transaction.page_url); + } + } + other->CloseStorageArea(area); + } + + SwitchToNewAliasMaster(other, context); return SessionStorageNamespace::MERGE_RESULT_MERGEABLE; } @@ -267,6 +369,41 @@ void DOMStorageNamespace::AddTransaction( } } +bool DOMStorageNamespace::DecrementMasterAliasCount() { + if (!alias_master_namespace_ || master_alias_count_decremented_) + return false; + DCHECK_GT(alias_master_namespace_->num_aliases_, 0); + alias_master_namespace_->num_aliases_--; + master_alias_count_decremented_ = true; + return (alias_master_namespace_->num_aliases_ == 0); +} + +void DOMStorageNamespace::SwitchToNewAliasMaster( + DOMStorageNamespace* new_master, + DOMStorageContextImpl* context) { + DCHECK(alias_master_namespace()); + scoped_refptr<DOMStorageNamespace> old_master = alias_master_namespace(); + if (new_master->alias_master_namespace()) + new_master = new_master->alias_master_namespace(); + DCHECK(!new_master->alias_master_namespace()); + DCHECK(old_master != this); + DCHECK(old_master != new_master); + DecrementMasterAliasCount(); + alias_master_namespace_ = new_master; + alias_master_namespace_->num_aliases_++; + master_alias_count_decremented_ = false; + // There are three things that we need to clean up: + // -- the old master may ready for shutdown, if its last alias has disappeared + // -- The dom_storage hosts need to close and reopen their areas, so that + // they point to the correct new areas. + // -- The renderers will need to reset their local caches. + // All three of these things are accomplished with the following call below. + // |context| will be NULL in unit tests, which is when this will + // not apply, of course. + if (context) + context->NotifyAliasSessionMerged(namespace_id(), old_master.get()); +} + DOMStorageNamespace::TransactionData::TransactionData() : max_log_size_exceeded(false) { } diff --git a/content/browser/dom_storage/dom_storage_namespace.h b/content/browser/dom_storage/dom_storage_namespace.h index 11a47bf..929669c 100644 --- a/content/browser/dom_storage/dom_storage_namespace.h +++ b/content/browser/dom_storage/dom_storage_namespace.h @@ -19,6 +19,7 @@ namespace content { class DOMStorageArea; +class DOMStorageContextImpl; class DOMStorageTaskRunner; class SessionStorageDatabase; @@ -69,6 +70,10 @@ class CONTENT_EXPORT DOMStorageNamespace DOMStorageNamespace* Clone(int64 clone_namespace_id, const std::string& clone_persistent_namespace_id); + // Creates an alias of |this| namespace. + // Should only be called for session storage namespaces. + DOMStorageNamespace* CreateAlias(int64 alias_namespace_id); + void DeleteLocalStorageOrigin(const GURL& origin); void DeleteSessionStorageOrigin(const GURL& origin); void PurgeMemory(PurgeOption purge); @@ -78,8 +83,24 @@ class CONTENT_EXPORT DOMStorageNamespace void AddTransactionLogProcessId(int process_id); void RemoveTransactionLogProcessId(int process_id); - SessionStorageNamespace::MergeResult CanMerge(int process_id, - DOMStorageNamespace* other); + SessionStorageNamespace::MergeResult Merge( + bool actually_merge, + int process_id, + DOMStorageNamespace* other, + DOMStorageContextImpl* context); + DOMStorageNamespace* alias_master_namespace() { + return alias_master_namespace_.get(); + } + int num_aliases() const { return num_aliases_; } + bool ready_for_deletion_pending_aliases() const { + return ready_for_deletion_pending_aliases_; } + void set_ready_for_deletion_pending_aliases(bool value) { + ready_for_deletion_pending_aliases_ = value; + } + bool must_persist_at_shutdown() const { return must_persist_at_shutdown_; } + void set_must_persist_at_shutdown(bool value) { + must_persist_at_shutdown_ = value; + } enum LogType { TRANSACTION_READ, @@ -88,9 +109,10 @@ class CONTENT_EXPORT DOMStorageNamespace TRANSACTION_CLEAR }; - struct TransactionRecord { + struct CONTENT_EXPORT TransactionRecord { LogType transaction_type; GURL origin; + GURL page_url; base::string16 key; base::NullableString16 value; TransactionRecord(); @@ -99,6 +121,9 @@ class CONTENT_EXPORT DOMStorageNamespace void AddTransaction(int process_id, const TransactionRecord& transaction); bool IsLoggingRenderer(int process_id); + // Decrements the count of aliases owned by the master, and returns true + // if the new count is 0. + bool DecrementMasterAliasCount(); private: friend class base::RefCountedThreadSafe<DOMStorageNamespace>; @@ -126,6 +151,10 @@ class CONTENT_EXPORT DOMStorageNamespace // Returns a pointer to the area holder in our map or NULL. AreaHolder* GetAreaHolder(const GURL& origin); + // Switches the current alias DOM storage namespace to a new alias master. + void SwitchToNewAliasMaster(DOMStorageNamespace* new_master, + DOMStorageContextImpl* context); + int64 namespace_id_; std::string persistent_namespace_id_; base::FilePath directory_; @@ -133,6 +162,19 @@ class CONTENT_EXPORT DOMStorageNamespace scoped_refptr<DOMStorageTaskRunner> task_runner_; scoped_refptr<SessionStorageDatabase> session_storage_database_; std::map<int, TransactionData*> transactions_; + int num_aliases_; + scoped_refptr<DOMStorageNamespace> alias_master_namespace_; + // Indicates whether we have already decremented |num_aliases_| for this + // namespace in its alias master. We may only decrement it once, and around + // deletion, this instance will stick around a bit longer until its refcount + // drops to 0. Therefore, we want to make sure we don't decrement the master's + // alias count a second time. + bool master_alias_count_decremented_; + // This indicates, for an alias master, that the master itself is ready + // for deletion, but there are aliases outstanding that we have to wait for + // before we can start cleaning up the master. + bool ready_for_deletion_pending_aliases_; + bool must_persist_at_shutdown_; }; } // namespace content diff --git a/content/browser/dom_storage/dom_storage_session.cc b/content/browser/dom_storage/dom_storage_session.cc index 398aade..c3b7a9e 100644 --- a/content/browser/dom_storage/dom_storage_session.cc +++ b/content/browser/dom_storage/dom_storage_session.cc @@ -17,19 +17,19 @@ namespace content { namespace { -void PostCanMergeTaskResult( +void PostMergeTaskResult( const SessionStorageNamespace::MergeResultCallback& callback, SessionStorageNamespace::MergeResult result) { callback.Run(result); } -void RunCanMergeTaskAndPostResult( +void RunMergeTaskAndPostResult( const base::Callback<SessionStorageNamespace::MergeResult(void)>& task, scoped_refptr<base::SingleThreadTaskRunner> result_loop, const SessionStorageNamespace::MergeResultCallback& callback) { SessionStorageNamespace::MergeResult result = task.Run(); result_loop->PostTask( - FROM_HERE, base::Bind(&PostCanMergeTaskResult, callback, result)); + FROM_HERE, base::Bind(&PostMergeTaskResult, callback, result)); } } // namespace @@ -57,6 +57,22 @@ DOMStorageSession::DOMStorageSession(DOMStorageContextImpl* context, context_, namespace_id_, persistent_namespace_id_)); } +DOMStorageSession::DOMStorageSession( + DOMStorageSession* master_dom_storage_session) + : context_(master_dom_storage_session->context_), + namespace_id_(master_dom_storage_session->context_->AllocateSessionId()), + persistent_namespace_id_( + master_dom_storage_session->persistent_namespace_id()), + should_persist_(false) { + context_->task_runner()->PostTask( + FROM_HERE, + base::Bind(&DOMStorageContextImpl::CreateAliasSessionNamespace, + context_, + master_dom_storage_session->namespace_id(), + namespace_id_, + persistent_namespace_id_)); +} + void DOMStorageSession::SetShouldPersist(bool should_persist) { should_persist_ = should_persist; } @@ -116,20 +132,40 @@ void DOMStorageSession::RemoveTransactionLogProcessId(int process_id) { context_, namespace_id_, process_id)); } -void DOMStorageSession::CanMerge( +void DOMStorageSession::Merge( + bool actually_merge, int process_id, DOMStorageSession* other, const SessionStorageNamespace::MergeResultCallback& callback) { scoped_refptr<base::SingleThreadTaskRunner> current_loop( base::ThreadTaskRunnerHandle::Get()); + SessionStorageNamespace::MergeResultCallback cb = + base::Bind(&DOMStorageSession::ProcessMergeResult, + this, + actually_merge, + callback, + other->persistent_namespace_id()); context_->task_runner()->PostTask( FROM_HERE, - base::Bind(&RunCanMergeTaskAndPostResult, - base::Bind(&DOMStorageContextImpl::CanMergeSessionStorage, - context_, namespace_id_, process_id, + base::Bind(&RunMergeTaskAndPostResult, + base::Bind(&DOMStorageContextImpl::MergeSessionStorage, + context_, namespace_id_, actually_merge, process_id, other->namespace_id_), current_loop, - callback)); + cb)); +} + +void DOMStorageSession::ProcessMergeResult( + bool actually_merge, + const SessionStorageNamespace::MergeResultCallback& callback, + const std::string& new_persistent_namespace_id, + SessionStorageNamespace::MergeResult result) { + if (actually_merge && + (result == SessionStorageNamespace::MERGE_RESULT_MERGEABLE || + result == SessionStorageNamespace::MERGE_RESULT_NO_TRANSACTIONS)) { + persistent_namespace_id_ = new_persistent_namespace_id; + } + callback.Run(result); } } // namespace content diff --git a/content/browser/dom_storage/dom_storage_session.h b/content/browser/dom_storage/dom_storage_session.h index 5cc180a..5e12621 100644 --- a/content/browser/dom_storage/dom_storage_session.h +++ b/content/browser/dom_storage/dom_storage_session.h @@ -31,6 +31,10 @@ class CONTENT_EXPORT DOMStorageSession DOMStorageSession(DOMStorageContextImpl* context, const std::string& persistent_namespace_id); + // Constructs a |DOMStorageSession| as an alias of + // |master_dom_storage_session|. Allocates a new non-persistent ID. + explicit DOMStorageSession(DOMStorageSession* master_dom_storage_session); + int64 namespace_id() const { return namespace_id_; } const std::string& persistent_namespace_id() const { return persistent_namespace_id_; @@ -47,9 +51,10 @@ class CONTENT_EXPORT DOMStorageSession void AddTransactionLogProcessId(int process_id); void RemoveTransactionLogProcessId(int process_id); - void CanMerge(int process_id, - DOMStorageSession* other, - const SessionStorageNamespace::MergeResultCallback& callback); + void Merge(bool actually_merge, + int process_id, + DOMStorageSession* other, + const SessionStorageNamespace::MergeResultCallback& callback); private: friend class base::RefCountedThreadSafe<DOMStorageSession>; @@ -59,6 +64,12 @@ class CONTENT_EXPORT DOMStorageSession const std::string& persistent_namespace_id); ~DOMStorageSession(); + void ProcessMergeResult( + bool actually_merge, + const SessionStorageNamespace::MergeResultCallback& callback, + const std::string& new_persistent_namespace_id, + SessionStorageNamespace::MergeResult result); + scoped_refptr<DOMStorageContextImpl> context_; int64 namespace_id_; std::string persistent_namespace_id_; diff --git a/content/browser/dom_storage/session_storage_namespace_impl.cc b/content/browser/dom_storage/session_storage_namespace_impl.cc index ea04c63..e01472a 100644 --- a/content/browser/dom_storage/session_storage_namespace_impl.cc +++ b/content/browser/dom_storage/session_storage_namespace_impl.cc @@ -25,6 +25,13 @@ SessionStorageNamespaceImpl::SessionStorageNamespaceImpl( : session_(new DOMStorageSession(context->context(), persistent_id)) { } +SessionStorageNamespaceImpl::SessionStorageNamespaceImpl( + SessionStorageNamespaceImpl* master_session_storage_namespace) + : session_(new DOMStorageSession( + master_session_storage_namespace->session_)) { +} + + int64 SessionStorageNamespaceImpl::id() const { return session_->namespace_id(); } @@ -67,13 +74,18 @@ void SessionStorageNamespaceImpl::RemoveTransactionLogProcessId( session_->RemoveTransactionLogProcessId(process_id); } -void SessionStorageNamespaceImpl::CanMerge( +void SessionStorageNamespaceImpl::Merge( + bool actually_merge, int process_id, SessionStorageNamespace* other, const MergeResultCallback& callback) { SessionStorageNamespaceImpl* other_impl = static_cast<SessionStorageNamespaceImpl*>(other); - session_->CanMerge(process_id, other_impl->session_, callback); + session_->Merge(actually_merge, process_id, other_impl->session_, callback); +} + +bool SessionStorageNamespaceImpl::IsAliasOf(SessionStorageNamespace* other) { + return persistent_id() == other->persistent_id(); } } // namespace content diff --git a/content/browser/dom_storage/session_storage_namespace_impl.h b/content/browser/dom_storage/session_storage_namespace_impl.h index dbc98c2..ff6bd36 100644 --- a/content/browser/dom_storage/session_storage_namespace_impl.h +++ b/content/browser/dom_storage/session_storage_namespace_impl.h @@ -34,6 +34,11 @@ class SessionStorageNamespaceImpl SessionStorageNamespaceImpl(DOMStorageContextWrapper* context, const std::string& persistent_id); + // Creates an alias of |master_session_storage_namespace|. This will allocate + // a new non-persistent ID. + explicit SessionStorageNamespaceImpl( + SessionStorageNamespaceImpl* master_session_storage_namespace); + // SessionStorageNamespace implementation. virtual int64 id() const OVERRIDE; virtual const std::string& persistent_id() const OVERRIDE; @@ -45,9 +50,11 @@ class SessionStorageNamespaceImpl virtual void AddTransactionLogProcessId(int process_id) OVERRIDE; virtual void RemoveTransactionLogProcessId(int process_id) OVERRIDE; - virtual void CanMerge(int process_id, - SessionStorageNamespace* other, - const MergeResultCallback& callback) OVERRIDE; + virtual void Merge(bool actually_merge, + int process_id, + SessionStorageNamespace* other, + const MergeResultCallback& callback) OVERRIDE; + virtual bool IsAliasOf(SessionStorageNamespace* other) OVERRIDE; private: explicit SessionStorageNamespaceImpl(DOMStorageSession* clone); |