| // 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 CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ |
| #define CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ |
| |
| #include <stdint.h> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/memory/ref_counted.h" |
| #include "components/services/leveldb/public/interfaces/leveldb.mojom.h" |
| #include "content/common/content_export.h" |
| #include "url/origin.h" |
| |
| namespace content { |
| |
| // Holds the metadata information for a session storage database. This includes |
| // logic for parsing and saving database content. |
| class CONTENT_EXPORT SessionStorageMetadata { |
| public: |
| // Version 0 represents the old SessionStorageDatabase where we never stored a |
| // version. This class stores '0' as the version in this case. Version 1 |
| // removes 'namespaces-' dummy entry, and 'map-_-' refcount entries that are |
| // present in version 0. |
| static constexpr const int64_t kMinSessionStorageSchemaVersion = 0; |
| static constexpr const int64_t kLatestSessionStorageSchemaVersion = 1; |
| |
| static constexpr const int64_t kInvalidDatabaseVersion = -1; |
| static constexpr const int64_t kInvalidMapId = -1; |
| |
| static constexpr const uint8_t kDatabaseVersionBytes[] = {'v', 'e', 'r', 's', |
| 'i', 'o', 'n'}; |
| |
| static constexpr const uint8_t kNamespacePrefixBytes[] = { |
| 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '-'}; |
| |
| // This is "next-map-id" (without the quotes). |
| static constexpr const uint8_t kNextMapIdKeyBytes[] = { |
| 'n', 'e', 'x', 't', '-', 'm', 'a', 'p', '-', 'i', 'd'}; |
| |
| // Represents a map which can be shared by multiple areas. |
| // The |DeleteNamespace| and |DeleteArea| methods can destroy any MapData |
| // objects who are no longer referenced by another namespace. |
| // Maps (and thus MapData objects) can only be shared for the same origin. |
| class CONTENT_EXPORT MapData : public base::RefCounted<MapData> { |
| public: |
| explicit MapData(int64_t map_number, url::Origin origin); |
| |
| const url::Origin& origin() const { return origin_; } |
| |
| // The number of namespaces that reference this map. |
| int ReferenceCount() const { return reference_count_; } |
| |
| // The key prefix for the map data (e.g. "map-2-"). |
| const std::vector<uint8_t>& KeyPrefix() const { return key_prefix_; } |
| |
| // The number of the map as bytes (e.g. "2"). |
| const std::vector<uint8_t>& MapNumberAsBytes() const { |
| return number_as_bytes_; |
| } |
| |
| private: |
| friend class base::RefCounted<MapData>; |
| friend class SessionStorageMetadata; |
| ~MapData(); |
| |
| void IncReferenceCount() { ++reference_count_; } |
| void DecReferenceCount() { --reference_count_; } |
| |
| // The map number as bytes (e.g. "2"). These bytes are the string |
| // representation of the map number. |
| std::vector<uint8_t> number_as_bytes_; |
| std::vector<uint8_t> key_prefix_; |
| url::Origin origin_; |
| int reference_count_ = 0; |
| }; |
| |
| using NamespaceOriginMap = |
| std::map<std::string, std::map<url::Origin, scoped_refptr<MapData>>>; |
| using NamespaceEntry = NamespaceOriginMap::iterator; |
| |
| SessionStorageMetadata(); |
| ~SessionStorageMetadata(); |
| |
| // For a new database, this saves the database version, clears the metadata, |
| // and returns the operations to save to disk. |
| std::vector<leveldb::mojom::BatchedOperationPtr> SetupNewDatabase(); |
| |
| // This parses the database version from the bytes that were stored on |
| // disk, or if there was no version saved then passes a base::nullopt. This |
| // call is not necessary on new databases. The |upgrade_operations| are |
| // populated with any operations needed to upgrade the databases versioning |
| // metadata. Note this is different than the namespaces metadata, which will |
| // be upgraded in ParseNamespaces. |
| // Returns if the parsing is correct and we support the version read. |
| bool ParseDatabaseVersion( |
| base::Optional<std::vector<uint8_t>> value, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* upgrade_operations); |
| |
| // Parses all namespaces and maps, and stores all metadata locally. This |
| // invalidates all NamespaceEntry and MapData objects. If there is a parsing |
| // error, the namespaces will be cleared.If the version given to |
| // |ParseDatabaseVersion| is an older version, any namespace metadata upgrades |
| // will be populated in |upgrade_operations|. This call is not necessary on |
| // new databases. |
| bool ParseNamespaces( |
| std::vector<leveldb::mojom::KeyValuePtr> values, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* upgrade_operations); |
| |
| // Parses the next map id from the given bytes. If that fails, then it uses |
| // the next available id from parsing the namespaces. This call is not |
| // necessary on new databases. |
| void ParseNextMapId(const std::vector<uint8_t>& map_id); |
| |
| // Creates new map data for the given namespace-origin area. If the area |
| // entry exists, then it will decrement the refcount of the old map. The |
| // |save_operations| save the new or modified area entry, as well as saving |
| // the next available map id. |
| // Note: It is invalid to call this method for an area that has a map with |
| // only one reference. |
| scoped_refptr<MapData> RegisterNewMap( |
| NamespaceEntry namespace_entry, |
| const url::Origin& origin, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* save_operations); |
| |
| // Registers an origin-map in the |destination_namespace| from every |
| // origin-map in the |source_namespace|. The |destination_namespace| must have |
| // no origin-maps. All maps in the destination namespace are the same maps as |
| // the source namespace. All database operations to save the namespace origin |
| // metadata are put in |save_operations|. |
| void RegisterShallowClonedNamespace( |
| NamespaceEntry source_namespace, |
| NamespaceEntry destination_namespace, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* save_operations); |
| |
| // Deletes the given namespace any any maps that no longer have any |
| // references. This will invalidate all NamespaceEntry objects for the |
| // |namespace_id|, and can invalidate any MapData objects whose reference |
| // count hits zero. |
| void DeleteNamespace( |
| const std::string& namespace_id, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* delete_operations); |
| |
| // This removes the metadata entry for this namespace-origin area. If the map |
| // at this entry isn't reference by any other area (refcount hits 0), then |
| // this will delete that map on disk and invalidate that MapData. |
| void DeleteArea( |
| const std::string& namespace_id, |
| const url::Origin& origin, |
| std::vector<leveldb::mojom::BatchedOperationPtr>* delete_operations); |
| |
| NamespaceEntry GetOrCreateNamespaceEntry(const std::string& namespace_id); |
| |
| const NamespaceOriginMap& namespace_origin_map() const { |
| return namespace_origin_map_; |
| } |
| |
| int64_t NextMapId() const { return next_map_id_; } |
| |
| private: |
| static std::vector<uint8_t> LatestDatabaseVersionAsVector(); |
| |
| static std::vector<uint8_t> GetNamespacePrefix( |
| const std::string& namespace_id); |
| static std::vector<uint8_t> GetAreaKey(const std::string& namespace_id, |
| const url::Origin& origin); |
| static std::vector<uint8_t> GetMapPrefix(int64_t map_number); |
| static std::vector<uint8_t> GetMapPrefix( |
| const std::vector<uint8_t>& map_number_as_bytes); |
| |
| int64_t initial_database_version_from_disk_ = kInvalidDatabaseVersion; |
| int64_t next_map_id_ = kInvalidMapId; |
| int64_t next_map_id_from_namespaces_ = 0; |
| |
| NamespaceOriginMap namespace_origin_map_; |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_DOM_STORAGE_SESSION_STORAGE_METADATA_H_ |