OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef IDBValueWrapping_h |
| 6 #define IDBValueWrapping_h |
| 7 |
| 8 #include "bindings/core/v8/ExceptionState.h" |
| 9 #include "bindings/core/v8/serialization/SerializedScriptValue.h" |
| 10 #include "modules/ModulesExport.h" |
| 11 #include "platform/SharedBuffer.h" |
| 12 #include "platform/wtf/Allocator.h" |
| 13 #include "platform/wtf/RefPtr.h" |
| 14 #include "platform/wtf/Vector.h" |
| 15 #include "public/platform/WebBlobInfo.h" |
| 16 #include "v8/include/v8.h" |
| 17 |
| 18 namespace blink { |
| 19 |
| 20 class Blob; |
| 21 class BlobDataHandle; |
| 22 class ExceptionState; |
| 23 class IDBValue; |
| 24 class ScriptState; |
| 25 class ScriptValue; |
| 26 class SerializedScriptValue; |
| 27 class SharedBuffer; |
| 28 |
| 29 // Logic for serializing V8 values for storage in IndexedDB. |
| 30 // |
| 31 // V8 values are stored on disk using the format implemented in |
| 32 // SerializedScriptValue (SSV), which is essentialy a byte array plus an array |
| 33 // of attached Blobs. For "normal" (not too large) V8 values, the SSV output's |
| 34 // byte array is stored directly in IndexedDB's backing store, together with |
| 35 // references to the attached Blobs. |
| 36 // |
| 37 // "Large" V8 values are wrapped in Blobs, in order to avoid operating the |
| 38 // backing store in a sub-optimal region. Specifically, the byte array in the |
| 39 // SSV output is replaced with a "wrapped value" marker, and stored inside a |
| 40 // Blob that is tacked to the end of the SSV's Blob array. IndexedDB's backing |
| 41 // store receives the "wrapped value" marker and the references to the Blobs, |
| 42 // while the large byte array in the SSV output is handled by the Blob storage |
| 43 // system. |
| 44 // |
| 45 // In summary: |
| 46 // "normal" v8::Value -> SSV -> IDBValue (stores SSV output) -> LevelDB |
| 47 // "large" v8::Value -> SSV -> IDBValue (stores SSV output) -> |
| 48 // Blob (stores SSV output) + IDBValue (stores Blob reference) -> LevelDB |
| 49 // |
| 50 // Full picture that accounts for Blob attachments: |
| 51 // "normal" v8::Value -> SSV (byte array, Blob attachments) -> |
| 52 // IDBValue (bytes: SSV byte array, blobs: SSV Blob attachments) -> LevelDB |
| 53 // "large" v8::Value -> SSV (byte array, Blob attachments) -> |
| 54 // IDBValue (bytes: "wrapped value" marker, |
| 55 // blobs: SSV Blob attachments + [wrapper Blob(SSV byte array)] -> |
| 56 // LevelDB |
| 57 class MODULES_EXPORT IDBValueWrapper { |
| 58 STACK_ALLOCATED(); |
| 59 |
| 60 public: |
| 61 // Wrapper for an IndexedDB value. |
| 62 // |
| 63 // The serialization process can throw an exception. The caller is responsible |
| 64 // for checking exception_state. |
| 65 IDBValueWrapper( |
| 66 v8::Isolate*, |
| 67 v8::Local<v8::Value>, |
| 68 SerializedScriptValue::SerializeOptions::WasmSerializationPolicy, |
| 69 ExceptionState&); |
| 70 |
| 71 // Creates a clone of the serialized value. |
| 72 // |
| 73 // This method is used to fulfill the IndexedDB specification requirement that |
| 74 // a value's key and index keys are extracted from a structured clone of the |
| 75 // value, which avoids the issue of side-effects in custom getters. |
| 76 // |
| 77 // This method cannot be called after WrapIfBiggerThan(). |
| 78 void Clone(ScriptState*, ScriptValue* clone); |
| 79 |
| 80 // Conditionally wraps the serialized value's byte array into a Blob. |
| 81 // |
| 82 // The byte array is wrapped if its size exceeds max_bytes. In production, the |
| 83 // max_bytes threshold is currently always kWrapThreshold. |
| 84 // |
| 85 // This method must be called before ExtractWireBytes() and cannot be called |
| 86 // after ExtractWireBytes(). |
| 87 bool WrapIfBiggerThan(unsigned max_bytes); |
| 88 |
| 89 // Obtains the BlobDataHandles from the serialized value's Blob array. |
| 90 // |
| 91 // This method must be called at most once, and must be called after |
| 92 // WrapIfBiggerThan(). |
| 93 void ExtractBlobDataHandles( |
| 94 Vector<RefPtr<BlobDataHandle>>* blob_data_handles); |
| 95 |
| 96 // Obtains the byte array for the serialized value. |
| 97 // |
| 98 // This method must be called at most once, and must be called after |
| 99 // WrapIfBiggerThan(). |
| 100 RefPtr<SharedBuffer> ExtractWireBytes(); |
| 101 |
| 102 // Obtains WebBlobInfos for the serialized value's Blob array. |
| 103 // |
| 104 // This method must be called at most once, and must be called after |
| 105 // WrapIfBiggerThan(). |
| 106 inline Vector<WebBlobInfo>& WrappedBlobInfo() { |
| 107 #if DCHECK_IS_ON() |
| 108 DCHECK(!had_exception_) |
| 109 << "WrapBlobInfo() called on wrapper with serialization exception"; |
| 110 #endif // DCHECK_IS_ON() |
| 111 return blob_info_; |
| 112 } |
| 113 |
| 114 // Default threshold for WrapIfBiggerThan(). |
| 115 // |
| 116 // This should be tuned to achieve a compromise between short-term IndexedDB |
| 117 // throughput and long-term I/O load and memory usage. LevelDB, the underlying |
| 118 // storage for IndexedDB, was not designed with large values in mind. At the |
| 119 // very least, large values will slow down compaction, causing occasional I/O |
| 120 // spikes. |
| 121 static constexpr unsigned kWrapThreshold = 64 * 1024; |
| 122 |
| 123 // MIME type used for Blobs that wrap IDBValues. |
| 124 static constexpr const char* kWrapMimeType = |
| 125 "application/vnd.blink-idb-value-wrapper"; |
| 126 |
| 127 private: |
| 128 // Used to serialize the wrapped value. |
| 129 static void WriteVarint(unsigned value, Vector<char>& output); |
| 130 |
| 131 RefPtr<SerializedScriptValue> serialized_value_; |
| 132 RefPtr<BlobDataHandle> wrapper_handle_; |
| 133 Vector<WebBlobInfo> blob_info_; |
| 134 Vector<char> wire_bytes_; |
| 135 #if DCHECK_IS_ON() |
| 136 bool had_exception_ = false; |
| 137 bool wrap_called_ = false; |
| 138 #endif // DCHECK_IS_ON() |
| 139 }; |
| 140 |
| 141 // State and logic for unwrapping large IndexedDB values from Blobs. |
| 142 // |
| 143 // See IDBValueWrapper for an explanation of the wrapping concept. |
| 144 // |
| 145 // Once created, an IDBValueUnwrapper instance can be used to unwrap multiple |
| 146 // Blobs. For each Blob to be unwrapped, the caller should first call Parse(). |
| 147 // If the method succeeds, the IDBValueUnwrapper will store the parse state, |
| 148 // which can be obtained using WrapperBlobSize() and WrapperBlobHandle(). |
| 149 class MODULES_EXPORT IDBValueUnwrapper { |
| 150 STACK_ALLOCATED(); |
| 151 |
| 152 public: |
| 153 IDBValueUnwrapper(); |
| 154 |
| 155 // True if the IDBValue's data was wrapped in a Blob. |
| 156 static bool IsWrapped(IDBValue*); |
| 157 |
| 158 // True if at least one of the IDBValues' data was wrapped in a Blob. |
| 159 static bool IsWrapped(const Vector<RefPtr<IDBValue>>&); |
| 160 |
| 161 // Pieces together an unwrapped IDBValue from a wrapped value and Blob data. |
| 162 static RefPtr<IDBValue> Unwrap(IDBValue* wrapped_value, |
| 163 RefPtr<SharedBuffer>&& wrapper_blob_content); |
| 164 |
| 165 // Parses the wrapper Blob information from a wrapped IDBValue. |
| 166 // |
| 167 // Returns true for success, and false for failure. Failure can mean that the |
| 168 // given value was not a wrapped IDBValue, or that the value bytes were |
| 169 // corrupted. |
| 170 bool Parse(IDBValue*); |
| 171 |
| 172 // Returns the size of the Blob obtained by the last Unwrap() call. |
| 173 // |
| 174 // Should only be called after a successful result from Unwrap(). |
| 175 inline unsigned WrapperBlobSize() const { |
| 176 DCHECK(end_); |
| 177 return blob_size_; |
| 178 } |
| 179 |
| 180 // Returns a handle to the Blob obtained by the last Unwrap() call. |
| 181 // |
| 182 // Should only be called exactly once after a successful result from Unwrap(). |
| 183 RefPtr<BlobDataHandle> WrapperBlobHandle(); |
| 184 |
| 185 private: |
| 186 // Used to deserialize the wrapped value. |
| 187 bool ReadVarint(unsigned& value); |
| 188 |
| 189 // Resets the parsing state. |
| 190 inline bool Reset() { |
| 191 #if DCHECK_IS_ON() |
| 192 blob_handle_.Clear(); |
| 193 current_ = nullptr; |
| 194 end_ = nullptr; |
| 195 #endif // DCHECK_IS_ON() |
| 196 return false; |
| 197 } |
| 198 |
| 199 // Deserialization cursor in the SharedBuffer of the IDBValue being unwrapped. |
| 200 const uint8_t* current_; |
| 201 |
| 202 // Smallest invalid position_ value. |
| 203 const uint8_t* end_; |
| 204 |
| 205 // The size of the Blob holding the data for the last unwrapped IDBValue. |
| 206 unsigned blob_size_; |
| 207 |
| 208 // Handle to the Blob holding the data for the last unwrapped IDBValue. |
| 209 RefPtr<BlobDataHandle> blob_handle_; |
| 210 }; |
| 211 |
| 212 } // namespace blink |
| 213 |
| 214 #endif // IDBValueWrapping_h |
OLD | NEW |