| // Copyright 2018 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. |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ |
| |
| #include "base/trace_event/memory_dump_provider.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "third_party/blink/public/mojom/dom_storage/storage_area.mojom-blink.h" |
| #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h" |
| #include "third_party/blink/renderer/modules/modules_export.h" |
| #include "third_party/blink/renderer/modules/storage/storage_area_map.h" |
| #include "third_party/blink/renderer/modules/storage/storage_namespace.h" |
| #include "third_party/blink/renderer/platform/heap/heap_allocator.h" |
| #include "third_party/blink/renderer/platform/heap/persistent.h" |
| #include "third_party/blink/renderer/platform/weborigin/security_origin.h" |
| #include "third_party/blink/renderer/platform/wtf/deque.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_map.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| |
| namespace blink { |
| |
| // An in-process implementation of LocalStorage using a LevelDB Mojo service. |
| // Maintains a complete cache of the origin's Map of key/value pairs for fast |
| // access. The cache is primed on first access and changes are written to the |
| // backend through the level db interface pointer. Mutations originating in |
| // other processes are applied to the cache via mojom::LevelDBObserver |
| // callbacks. |
| // There is one CachedStorageArea for potentially many LocalStorageArea |
| // objects. |
| class MODULES_EXPORT CachedStorageArea |
| : public mojom::blink::StorageAreaObserver, |
| public RefCounted<CachedStorageArea>, |
| public base::trace_event::MemoryDumpProvider { |
| public: |
| // Instances of this class are used to identify the "source" of any changes |
| // made to this storage area, as well as to dispatch any incoming change |
| // events. Change events are not sent back to the source that caused the |
| // change. The source passed to the various methods that modify storage |
| // should have been registered first by calling RegisterSource. |
| class Source : public GarbageCollectedMixin { |
| public: |
| virtual ~Source() = default; |
| virtual KURL GetPageUrl() const = 0; |
| // Return 'true' to continue receiving events, and 'false' to stop. |
| virtual bool EnqueueStorageEvent(const String& key, |
| const String& old_value, |
| const String& new_value, |
| const String& url) = 0; |
| virtual blink::WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser( |
| const char* name, |
| WebScopedVirtualTimePauser::VirtualTaskDuration duration) = 0; |
| }; |
| |
| enum class AreaType { |
| kSessionStorage, |
| kLocalStorage, |
| }; |
| |
| CachedStorageArea(AreaType type, |
| scoped_refptr<const SecurityOrigin> origin, |
| scoped_refptr<base::SingleThreadTaskRunner> ipc_runner, |
| StorageNamespace* storage_namespace); |
| |
| // These correspond to blink::Storage. |
| unsigned GetLength(); |
| String GetKey(unsigned index); |
| String GetItem(const String& key); |
| bool SetItem(const String& key, const String& value, Source* source); |
| void RemoveItem(const String& key, Source* source); |
| void Clear(Source* source); |
| |
| // Allow this object to keep track of the Source instances corresponding to |
| // it, which is needed for mutation event notifications. |
| // Returns the (unique) id allocated for this source for testing purposes. |
| String RegisterSource(Source* source); |
| |
| size_t quota_used() const { return map_ ? map_->quota_used() : 0; } |
| size_t memory_used() const { return map_ ? map_->memory_used() : 0; } |
| |
| // Only public to allow tests to parametrize on this type. |
| enum class FormatOption { |
| kLocalStorageDetectFormat, |
| kSessionStorageForceUTF16, |
| kSessionStorageForceUTF8 |
| }; |
| |
| mojo::Remote<mojom::blink::StorageArea>& RemoteArea() { return remote_area_; } |
| |
| // Invoked by the owning StorageNamespace the renderer is told to reset its |
| // DOM Storage connections (e.g. to recover from a backend crash). Normally |
| // a new StorageArea pipe is bound through the owning StorageNamespace, but |
| // |new_area| may be provided by test code instead. |
| void ResetConnection( |
| mojo::PendingRemote<mojom::blink::StorageArea> new_area = {}); |
| |
| void SetRemoteAreaForTesting( |
| mojo::PendingRemote<mojom::blink::StorageArea> area) { |
| remote_area_.Bind(std::move(area)); |
| } |
| |
| private: |
| friend class RefCounted<CachedStorageArea>; |
| ~CachedStorageArea() override; |
| |
| friend class CachedStorageAreaTest; |
| friend class CachedStorageAreaStringFormatTest; |
| friend class MockStorageArea; |
| |
| // Simple structure used to return information about each locally-initiated |
| // mutation on the StorageArea until the mutation is acknowledged by a |
| // corresponding StorageAreaObserver event. See |pending_mutations_by_source_| |
| // and |pending_mutations_by_key_| below. |
| struct PendingMutation { |
| String key; |
| String new_value; |
| String old_value; |
| }; |
| |
| void BindStorageArea( |
| mojo::PendingRemote<mojom::blink::StorageArea> new_area = {}); |
| |
| // mojom::blink::StorageAreaObserver: |
| void KeyChanged(const Vector<uint8_t>& key, |
| const Vector<uint8_t>& new_value, |
| const base::Optional<Vector<uint8_t>>& old_value, |
| const String& source) override; |
| void KeyChangeFailed(const Vector<uint8_t>& key, |
| const String& source) override; |
| void KeyDeleted(const Vector<uint8_t>& key, |
| const base::Optional<Vector<uint8_t>>& old_value, |
| const String& source) override; |
| void AllDeleted(bool was_nonempty, const String& source) override; |
| void ShouldSendOldValueOnMutations(bool value) override; |
| |
| // base::trace_event::MemoryDumpProvider: |
| bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
| base::trace_event::ProcessMemoryDump* pmd) override; |
| |
| // Enqueues state regarding a locally-initiated storage mutation which will |
| // eventually be acknowledged by a StorageAreaObserver event targeting |
| // |receiver_|. |
| void EnqueuePendingMutation(const String& key, |
| const String& new_value, |
| const String& old_value, |
| const String& source); |
| |
| // Dequeues and returns the oldest PendingMutation from the queue for |source| |
| // if one exists. If there is no pending mutation queued for |source| this |
| // returns null. |
| std::unique_ptr<PendingMutation> PopPendingMutation(const String& source); |
| |
| void MaybeApplyNonLocalMutationForKey(const String& key, |
| const String& new_value); |
| |
| // Synchronously fetches the areas data if it hasn't been fetched already. |
| void EnsureLoaded(); |
| |
| bool IsSessionStorage() const; |
| FormatOption GetKeyFormat() const; |
| FormatOption GetValueFormat() const; |
| |
| void EnqueueStorageEvent(const String& key, |
| const String& old_value, |
| const String& new_value, |
| const String& url, |
| const String& storage_area_id); |
| |
| static String Uint8VectorToString(const Vector<uint8_t>& input, |
| FormatOption format_option); |
| static Vector<uint8_t> StringToUint8Vector(const String& input, |
| FormatOption format_option); |
| |
| const AreaType type_; |
| const scoped_refptr<const SecurityOrigin> origin_; |
| const WeakPersistent<StorageNamespace> storage_namespace_; |
| const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| |
| std::unique_ptr<StorageAreaMap> map_; |
| |
| // Queues of local mutations which are pending browser acknowledgement via |
| // StorageAreaObserver events. This map is keyed by local source ID and owns |
| // the PendingMutation objects. |
| // |
| // Only used for Local Storage. Session Storage operations are confined to |
| // local side-effects and are not acknowledged with StorageAreaObsever events. |
| using OwnedPendingMutationQueue = Deque<std::unique_ptr<PendingMutation>>; |
| HashMap<String, OwnedPendingMutationQueue> pending_mutations_by_source_; |
| |
| // Queues of local mutations indexed per key. Every queued value references a |
| // PendingMutation owned by |pending_mutations_by_source_| above. |
| // |
| // These per-key queues exists for two main reasons: |
| // |
| // * As long as a key's queue is non-empty, non-local mutations observed for |
| // the key are ignored. |
| // * Any time a non-local |AllDeleted| (i.e. script |clear()|) is observed, |
| // the most recent mutation for each key is re-applied locally to the |
| // cleared area, as this improves locally script-observable consistency. |
| // |
| // Only used for Local Storage. Session Storage operations are confined to |
| // local side-effects and are not acknowledged with StorageAreaObsever events. |
| HashMap<String, Deque<PendingMutation*>> pending_mutations_by_key_; |
| |
| // See ShouldSendOldValueOnMutations(). |
| bool should_send_old_value_on_mutations_ = true; |
| |
| // Connection to the backing implementation of this StorageArea. This is |
| // always bound. |
| mojo::Remote<mojom::blink::StorageArea> remote_area_; |
| |
| // Receives StorageAreaObserver events from the StorageArea implementation and |
| // dispatches them to this CachedStorageArea. |
| mojo::Receiver<mojom::blink::StorageAreaObserver> receiver_{this}; |
| |
| Persistent<HeapHashMap<WeakMember<Source>, String>> areas_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CachedStorageArea); |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_CACHED_STORAGE_AREA_H_ |