File: idb_value_wrapping.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (271 lines) | stat: -rw-r--r-- 11,046 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// Copyright 2017 The Chromium Authors
// 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_INDEXEDDB_IDB_VALUE_WRAPPING_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_VALUE_WRAPPING_H_

#include <memory>
#include <utility>

#include "base/containers/span.h"
#include "base/dcheck_is_on.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/public/platform/web_blob_info.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
#include "v8/include/v8.h"

namespace blink {

class BlobDataHandle;
class ExceptionState;
class IDBValue;
class ScriptState;
class ScriptValue;
class SerializedScriptValue;

// Logic for serializing V8 values for storage in IndexedDB.
//
// An IDBValueWrapper instance drives the serialization of a single V8 value to
// IndexedDB. An instance's lifecycle goes through the following stages:
// 1) Cloning - Right after an instance is constructed, its internal
//    representation is optimized for structured cloning via the Clone() method.
//    This may be necessary when extracting the primary key and/or index keys
//    for the serialized value.
// 2) Wrapping - DoneCloning() transitions the instance to an internal
//    representation optimized IPC and disk storage. See below for details.
// 3) Reading results - After any desired wrapping is performed, the Take*()
//    methods yield the serialized value components passed to the backing store.
//    To avoid unnecessary copies, the Take*() methods move out parts of the
//    internal representation, so each Take*() method can be called at most
//    once.
//
// Example usage:
//     IDBValueWrapper wrapper;
//     wrapper.Clone(...);  // Structured clone used to extract keys.
//     wrapper.DoneCloning();
//     std::unique_ptr<IDBValue> value = std::move(wrapper).Build();
//
// V8 values are first serialized via SerializedScriptValue (SSV), which is
// essentially a byte array plus an array of attached Blobs. The SSV output's
// byte array is then further compressed via Snappy. If the compressed array is
// not too large, it will be stored directly in IndexedDB's backing store,
// together with references to the attached Blobs.
//
// Values that are still "large" after compression are converted into a Blob
// (additional to those already attached). Specifically, the byte array in the
// SSV output is replaced with a "wrapped value" marker, and stored inside a
// Blob that is tacked to the end of the SSV's Blob array. IndexedDB's backing
// store receives the "wrapped value" marker and the references to the Blobs,
// while the large byte array in the SSV output is handled by the Blob storage
// system.
//
// In summary:
// "normal" v8::Value -> SSV + Snappy -> IDBValue (stores SSV output) -> LevelDB
// "large" v8::Value -> SSV + Snappy -> IDBValue (stores SSV output) ->
//     Blob (stores SSV output) + IDBValue (stores Blob reference) -> LevelDB
//
// Full picture that accounts for Blob attachments:
// "normal" v8::Value -> SSV (byte array, Blob attachments) -> Snappy ->
//     IDBValue (bytes: compressed SSV byte array, blobs: SSV Blob attachments)
//     -> LevelDB
// "large" v8::Value -> SSV (byte array, Blob attachments) -> Snappy ->
//     IDBValue (bytes: "wrapped value" marker,
//               blobs: SSV Blob attachments +
//                      [wrapper Blob(compressed SSV byte array)] ->
//     LevelDB
class MODULES_EXPORT IDBValueWrapper {
  DISALLOW_NEW();

 public:
  // Wrapper for an IndexedDB value.
  //
  // The serialization process can throw an exception. The caller is responsible
  // for checking exception_state.
  //
  // The wrapper's internal representation is optimized for cloning the
  // serialized value. DoneCloning() must be called to transition to an internal
  // representation optimized for writing.
  IDBValueWrapper(
      v8::Isolate*,
      v8::Local<v8::Value>,
      SerializedScriptValue::SerializeOptions::WasmSerializationPolicy,
      ExceptionState&);
  ~IDBValueWrapper();

  // Creates a clone of the serialized value.
  //
  // This method is used to fulfill the IndexedDB specification requirement that
  // a value's key and index keys are extracted from a structured clone of the
  // value, which avoids the issue of side-effects in custom getters.
  //
  // This method cannot be called after DoneCloning().
  void Clone(ScriptState*, ScriptValue* clone);

  // Optimizes the serialized value's internal representation for writing to
  // disk.
  //
  // This must be called before Take*() methods can be called. After this method
  // is called, Clone() cannot be called anymore.
  void DoneCloning();

  // Transfers ownership of the serialized byte array, blob infos, and FSA
  // tokens to the returned `IDBValue`.
  //
  // This method must be called at most once, and must be called after
  // WrapIfBiggerThan().
  std::unique_ptr<IDBValue> Build() &&;

  size_t DataLengthBeforeWrapInBytes() { return original_data_length_; }

  // MIME type used for Blobs that wrap IDBValues.
  static constexpr const char* kWrapMimeType =
      "application/vnd.blink-idb-value-wrapper";

  // Used to serialize the wrapped value. Exposed for testing.
  static void WriteVarInt(unsigned value, Vector<char>& output);

  void set_wrapping_threshold_for_test(unsigned threshold) {
    wrapping_threshold_override_ = threshold;
  }

  void set_compression_threshold_for_test(size_t threshold) {
    compression_threshold_override_ = threshold;
  }

 private:
  // Evaluates if the specified uncompressed length merits a compression
  // attempt.
  bool ShouldCompress(size_t uncompressed_length) const;
  // Tries to compress `wire_bytes_` via Snappy, storing the output in
  // `wire_data_buffer_`. If the compression effect is small, the compression
  // will be discarded and an uncompressed value will be stored in
  // `wire_data_buffer_` (mainly to avoid an extra memory allocation when later
  // reading the value).
  void MaybeCompress();

  // Stores `wire_bytes_` in a Blob if it is over the size threshold.
  void MaybeStoreInBlob();

  // V8 value serialization state.
  scoped_refptr<SerializedScriptValue> serialized_value_;
  Vector<WebBlobInfo> blob_info_;

  // Buffer for wire data that is not stored in SerializedScriptValue.
  //
  // This buffer ends up storing metadata generated by wrapping operations.
  Vector<char> wire_data_buffer_;

  // Points into SerializedScriptValue's data buffer, or into wire_data_buffer_.
  // TODO(367764863) Rewrite to base::raw_span.
  RAW_PTR_EXCLUSION base::span<const uint8_t> wire_data_;

  size_t original_data_length_ = 0;

  std::optional<unsigned> wrapping_threshold_override_;
  std::optional<size_t> compression_threshold_override_;

#if DCHECK_IS_ON()
  // Accounting for lifecycle stages.
  bool had_exception_ = false;
  bool done_cloning_ = false;
  bool owns_wire_bytes_ = true;
#endif  // DCHECK_IS_ON()
};

// State and logic for unwrapping large IndexedDB values from Blobs.
//
// See IDBValueWrapper for an explanation of the wrapping concept.
//
// Once created, an IDBValueUnwrapper instance can be used to unwrap multiple
// Blobs. For each Blob to be unwrapped, the caller should first call Parse().
// If the method succeeds, the IDBValueUnwrapper will store the parse state,
// which can be obtained using WrapperBlobSize() and WrapperBlobHandle().
class MODULES_EXPORT IDBValueUnwrapper {
  STACK_ALLOCATED();

 public:
  IDBValueUnwrapper();

  // True if the IDBValue's data was wrapped in a Blob.
  static bool IsWrapped(IDBValue*);

  // True if at least one of the IDBValues' data was wrapped in a Blob.
  static bool IsWrapped(const Vector<std::unique_ptr<IDBValue>>&);

  // Unwraps an IDBValue that has wrapped Blob data, placing the result in
  // `wrapped_value`.
  static void Unwrap(Vector<char>&& wrapper_blob_content,
                     IDBValue& wrapped_value);

  // Decompresses the value in `buffer` and stores in one of the two provided
  // buffers (exactly one must be provided). Returns true on success.
  static bool Decompress(
      base::span<const uint8_t> buffer,
      Vector<char>* out_buffer,
      SerializedScriptValue::DataBufferPtr* out_buffer_in_place);

  // Parses the wrapper Blob information from a wrapped IDBValue.
  //
  // Returns true for success, and false for failure. Failure can mean that the
  // given value was not a wrapped IDBValue, or that the value bytes were
  // corrupted.
  bool Parse(IDBValue*);

  // Returns the size of the Blob obtained by the last Unwrap() call.
  //
  // Should only be called after a successful result from Unwrap().
  inline unsigned WrapperBlobSize() const { return blob_size_; }

  // Returns a handle to the Blob obtained by the last Unwrap() call.
  //
  // Should only be called exactly once after a successful result from Unwrap().
  scoped_refptr<BlobDataHandle> WrapperBlobHandle();

 private:
  friend class IDBValueUnwrapperReadTestHelper;

  // Used to deserialize the wrapped value.
  bool ReadVarInt(unsigned&);
  bool ReadBytes(Vector<uint8_t>&);

  // Resets the parsing state.
  bool Reset();

  // Deserialization cursor in the `data_` of the IDBValue being unwrapped.
  base::span<const uint8_t> parse_span_;

  // The size of the Blob holding the data for the last unwrapped IDBValue.
  unsigned blob_size_;

  // Handle to the Blob holding the data for the last unwrapped IDBValue.
  scoped_refptr<BlobDataHandle> blob_handle_;
};

// This flag controls behavior that decompresses
// `IDBValue::data_` directly into a buffer that's passed by ownership to
// `SerializedScriptValue`.
//
//  * For values that are not compressed, this flag has no effect: `data_` is
//  always copied on conversion to a script value.
//  * For values that are compressed,
//    * Normally `data_` will be decompressed the first time it's serialized,
//    overwriting `data_`, and *copied* into `SerializedScriptValue` the first
//    time and every subsequent time one is created.
//    * When this flag is enabled, `data_` will be decompressed *directly into*
//    a buffer that's passed off to `SerializedScriptValue`, which avoids a copy
//    and the memory overhead that entails. However this will happen every time
//    the value is deserialized, so if that happens more than once, the
//    decompression routine must run more than once.
MODULES_EXPORT BASE_DECLARE_FEATURE(kIdbDecompressValuesInPlace);

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_VALUE_WRAPPING_H_