| /* |
| * Copyright (C) 2009 Google Inc. All Rights Reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/modules/storage/storage_namespace.h" |
| |
| #include <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_security_origin.h" |
| #include "third_party/blink/public/web/web_view_client.h" |
| #include "third_party/blink/renderer/modules/storage/cached_storage_area.h" |
| #include "third_party/blink/renderer/modules/storage/inspector_dom_storage_agent.h" |
| #include "third_party/blink/renderer/modules/storage/storage_area.h" |
| #include "third_party/blink/renderer/modules/storage/storage_controller.h" |
| #include "third_party/blink/renderer/platform/weborigin/security_origin.h" |
| |
| namespace blink { |
| |
| const char StorageNamespace::kSupplementName[] = "SessionStorageNamespace"; |
| |
| StorageNamespace::StorageNamespace(StorageController* controller) |
| : controller_(controller) {} |
| StorageNamespace::StorageNamespace(StorageController* controller, |
| const String& namespace_id) |
| : controller_(controller), namespace_id_(namespace_id) {} |
| |
| // static |
| void StorageNamespace::ProvideSessionStorageNamespaceTo(Page& page, |
| WebViewClient* client) { |
| if (client) { |
| if (client->GetSessionStorageNamespaceId().empty()) |
| return; |
| auto* ss_namespace = |
| StorageController::GetInstance()->CreateSessionStorageNamespace( |
| String(client->GetSessionStorageNamespaceId().data(), |
| client->GetSessionStorageNamespaceId().size())); |
| if (!ss_namespace) |
| return; |
| ProvideTo(page, ss_namespace); |
| } |
| } |
| |
| scoped_refptr<CachedStorageArea> StorageNamespace::GetCachedArea( |
| const SecurityOrigin* origin_ptr) { |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class CacheMetrics { |
| kMiss = 0, // Area not in cache. |
| kHit = 1, // Area with refcount = 0 loaded from cache. |
| kUnused = 2, // Cache was not used. Area had refcount > 0. |
| kMaxValue = kUnused, |
| }; |
| |
| CacheMetrics metric = CacheMetrics::kMiss; |
| scoped_refptr<CachedStorageArea> result; |
| auto cache_it = cached_areas_.find(origin_ptr); |
| if (cache_it != cached_areas_.end()) { |
| metric = cache_it->value->HasOneRef() ? CacheMetrics::kHit |
| : CacheMetrics::kUnused; |
| result = cache_it->value; |
| } |
| if (IsSessionStorage()) |
| LOCAL_HISTOGRAM_ENUMERATION("SessionStorage.RendererAreaCacheHit", metric); |
| else |
| UMA_HISTOGRAM_ENUMERATION("LocalStorage.RendererAreaCacheHit", metric); |
| |
| if (result) |
| return result; |
| |
| scoped_refptr<const SecurityOrigin> origin(origin_ptr); |
| |
| controller_->ClearAreasIfNeeded(); |
| result = base::MakeRefCounted<CachedStorageArea>( |
| IsSessionStorage() ? CachedStorageArea::AreaType::kSessionStorage |
| : CachedStorageArea::AreaType::kLocalStorage, |
| origin, controller_->TaskRunner(), this); |
| cached_areas_.insert(std::move(origin), result); |
| return result; |
| } |
| |
| void StorageNamespace::CloneTo(const String& target) { |
| DCHECK(IsSessionStorage()) << "Cannot clone a local storage namespace."; |
| EnsureConnected(); |
| |
| // Spec requires that all mutations on storage areas *before* cloning are |
| // visible in the clone and that no mutations on the original storage areas |
| // *after* cloning, are visible in the clone. Consider the following scenario |
| // in the comments below: |
| // |
| // 1. Area A calls Put("x", 42) |
| // 2. Area B calls Put("y", 13) |
| // 3. Area A & B's StorageNamespace gets CloneTo()'d to a new namespace |
| // 4. Area A calls Put("x", 43) in the original namespace |
| // |
| // First, we synchronize StorageNamespace against every cached StorageArea. |
| // This ensures that all StorageArea operations (e.g. Put, Delete) up to this |
| // point will have executed before the StorageNamespace implementation is able |
| // to receive or process the following |Clone()| call. Given the above |
| // example, this would mean that A.x=42 and B.y=13 definitely WILL be present |
| // in the cloned namespace. |
| for (auto& entry : cached_areas_) { |
| namespace_.PauseReceiverUntilFlushCompletes( |
| entry.value->RemoteArea().FlushAsync()); |
| } |
| |
| namespace_->Clone(target); |
| |
| // Finally, we synchronize every StorageArea against StorageNamespace. This |
| // ensures that any future calls on each StorageArea cannot be received and |
| // processed until after the above |Clone()| call executes. Given the example |
| // above, this would mean that A.x=43 definitely WILL NOT be present in the |
| // cloned namespace; only the original namespace will be updated, and A.x will |
| // still hold a value of 42 in the new clone. |
| for (auto& entry : cached_areas_) { |
| entry.value->RemoteArea().PauseReceiverUntilFlushCompletes( |
| namespace_.FlushAsync()); |
| } |
| } |
| |
| size_t StorageNamespace::TotalCacheSize() const { |
| size_t total = 0; |
| for (const auto& it : cached_areas_) |
| total += it.value->quota_used(); |
| return total; |
| } |
| |
| void StorageNamespace::CleanUpUnusedAreas() { |
| Vector<const SecurityOrigin*, 16> to_remove; |
| for (const auto& area : cached_areas_) { |
| if (area.value->HasOneRef()) |
| to_remove.push_back(area.key.get()); |
| } |
| cached_areas_.RemoveAll(to_remove); |
| } |
| |
| void StorageNamespace::AddInspectorStorageAgent( |
| InspectorDOMStorageAgent* agent) { |
| inspector_agents_.insert(agent); |
| } |
| void StorageNamespace::RemoveInspectorStorageAgent( |
| InspectorDOMStorageAgent* agent) { |
| inspector_agents_.erase(agent); |
| } |
| |
| void StorageNamespace::Trace(Visitor* visitor) const { |
| visitor->Trace(inspector_agents_); |
| visitor->Trace(namespace_); |
| Supplement<Page>::Trace(visitor); |
| } |
| |
| void StorageNamespace::DidDispatchStorageEvent(const SecurityOrigin* origin, |
| const String& key, |
| const String& old_value, |
| const String& new_value) { |
| for (InspectorDOMStorageAgent* agent : inspector_agents_) { |
| agent->DidDispatchDOMStorageEvent( |
| key, old_value, new_value, |
| IsSessionStorage() ? StorageArea::StorageType::kSessionStorage |
| : StorageArea::StorageType::kLocalStorage, |
| origin); |
| } |
| } |
| |
| void StorageNamespace::BindStorageArea( |
| const scoped_refptr<const SecurityOrigin>& origin, |
| mojo::PendingReceiver<mojom::blink::StorageArea> receiver) { |
| if (IsSessionStorage()) { |
| controller_->dom_storage()->BindSessionStorageArea(origin, namespace_id_, |
| std::move(receiver)); |
| } else { |
| controller_->dom_storage()->OpenLocalStorage(origin, std::move(receiver)); |
| } |
| } |
| |
| void StorageNamespace::ResetStorageAreaAndNamespaceConnections() { |
| for (const auto& area : cached_areas_) |
| area.value->ResetConnection(); |
| namespace_.reset(); |
| } |
| |
| void StorageNamespace::EnsureConnected() { |
| DCHECK(IsSessionStorage()); |
| if (namespace_.is_bound()) |
| return; |
| controller_->dom_storage()->BindSessionStorageNamespace( |
| namespace_id_, |
| namespace_.BindNewPipeAndPassReceiver(controller_->TaskRunner())); |
| } |
| |
| } // namespace blink |