blob: 2ffbe551b85c0a92938ae22d73eafc4fb0e9115a [file] [log] [blame]
/*
* Copyright (C) 2011 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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/bindings/modules/v8/v8_binding_for_modules.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer_view.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_string_list.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_file.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_uint8_array.h"
#include "third_party/blink/renderer/bindings/modules/v8/to_v8_for_modules.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_cursor.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_cursor_with_value.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_database.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_index.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_key_range.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_object_store.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key_path.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key_range.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
#include "third_party/blink/renderer/platform/shared_buffer.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
static v8::Local<v8::Value> DeserializeIDBValueData(v8::Isolate*,
const IDBValue*);
static v8::Local<v8::Value> DeserializeIDBValueArray(
v8::Isolate*,
v8::Local<v8::Object> creation_context,
const Vector<std::unique_ptr<IDBValue>>&);
v8::Local<v8::Value> ToV8(const IDBKeyPath& value,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
switch (value.GetType()) {
case IDBKeyPath::kNullType:
return v8::Null(isolate);
case IDBKeyPath::kStringType:
return V8String(isolate, value.GetString());
case IDBKeyPath::kArrayType:
return ToV8(value.Array(), creation_context, isolate);
}
NOTREACHED();
return v8::Undefined(isolate);
}
v8::Local<v8::Value> ToV8(const IDBKey* key,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
if (!key) {
// The IndexedDB spec requires that absent keys appear as attribute
// values as undefined, rather than the more typical (for DOM) null.
// This appears on the |upper| and |lower| attributes of IDBKeyRange.
// Spec: http://www.w3.org/TR/IndexedDB/#idl-def-IDBKeyRange
return v8::Local<v8::Value>();
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
switch (key->GetType()) {
case mojom::IDBKeyType::Invalid:
case mojom::IDBKeyType::Min:
NOTREACHED();
return v8::Local<v8::Value>();
case mojom::IDBKeyType::Null:
return v8::Null(isolate);
case mojom::IDBKeyType::Number:
return v8::Number::New(isolate, key->Number());
case mojom::IDBKeyType::String:
return V8String(isolate, key->GetString());
case mojom::IDBKeyType::Binary:
// https://w3c.github.io/IndexedDB/#convert-a-value-to-a-key
return ToV8(DOMArrayBuffer::Create(key->Binary()), creation_context,
isolate);
case mojom::IDBKeyType::Date:
return v8::Date::New(context, key->Date()).ToLocalChecked();
case mojom::IDBKeyType::Array: {
v8::Local<v8::Array> array = v8::Array::New(isolate, key->Array().size());
for (wtf_size_t i = 0; i < key->Array().size(); ++i) {
v8::Local<v8::Value> value =
ToV8(key->Array()[i].get(), creation_context, isolate);
if (value.IsEmpty())
value = v8::Undefined(isolate);
bool created_property;
if (!array->CreateDataProperty(context, i, value)
.To(&created_property) ||
!created_property)
return v8::Local<v8::Value>();
}
return array;
}
}
NOTREACHED();
return v8::Local<v8::Value>();
}
// IDBAny is a variant type used to hold the values produced by the |result|
// attribute of IDBRequest and (as a convenience) the |source| attribute of
// IDBRequest and IDBCursor.
// TODO(jsbell): Replace the use of IDBAny for |source| attributes (which are
// ScriptWrappable types) using unions per IDL.
v8::Local<v8::Value> ToV8(const IDBAny* impl,
v8::Local<v8::Object> creation_context,
v8::Isolate* isolate) {
if (!impl)
return v8::Null(isolate);
switch (impl->GetType()) {
case IDBAny::kUndefinedType:
return v8::Undefined(isolate);
case IDBAny::kNullType:
return v8::Null(isolate);
case IDBAny::kDOMStringListType:
return ToV8(impl->DomStringList(), creation_context, isolate);
case IDBAny::kIDBCursorType:
return ToV8(impl->IdbCursor(), creation_context, isolate);
case IDBAny::kIDBCursorWithValueType:
return ToV8(impl->IdbCursorWithValue(), creation_context, isolate);
case IDBAny::kIDBDatabaseType:
return ToV8(impl->IdbDatabase(), creation_context, isolate);
case IDBAny::kIDBValueType:
return DeserializeIDBValue(isolate, creation_context, impl->Value());
case IDBAny::kIDBValueArrayType:
return DeserializeIDBValueArray(isolate, creation_context,
impl->Values());
case IDBAny::kIntegerType:
return v8::Number::New(isolate, impl->Integer());
case IDBAny::kKeyType:
return ToV8(impl->Key(), creation_context, isolate);
}
NOTREACHED();
return v8::Undefined(isolate);
}
#if defined(NDEBUG)
static const size_t kMaximumDepth = 2000;
#else
// Stack frames in debug builds are generally much larger than in release
// builds. Use a lower recursion depth to avoid stack overflows (see e.g.
// http://crbug.com/729334).
static const size_t kMaximumDepth = 1000;
#endif
static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
Vector<v8::Local<v8::Array>>& stack,
ExceptionState& exception_state) {
if (value->IsNumber() && !std::isnan(value.As<v8::Number>()->Value()))
return IDBKey::CreateNumber(value.As<v8::Number>()->Value());
if (value->IsString())
return IDBKey::CreateString(ToCoreString(value.As<v8::String>()));
if (value->IsDate() && !std::isnan(value.As<v8::Date>()->ValueOf()))
return IDBKey::CreateDate(value.As<v8::Date>()->ValueOf());
// https://w3c.github.io/IndexedDB/#convert-a-key-to-a-value
if (value->IsArrayBuffer()) {
DOMArrayBuffer* buffer = V8ArrayBuffer::ToImpl(value.As<v8::Object>());
if (buffer->IsNeutered()) {
exception_state.ThrowTypeError("The ArrayBuffer is neutered.");
return nullptr;
}
const char* start = static_cast<const char*>(buffer->Data());
size_t length = buffer->ByteLength();
return IDBKey::CreateBinary(SharedBuffer::Create(start, length));
}
if (value->IsArrayBufferView()) {
DOMArrayBufferView* view =
V8ArrayBufferView::ToImpl(value.As<v8::Object>());
if (view->buffer()->IsNeutered()) {
exception_state.ThrowTypeError("The viewed ArrayBuffer is neutered.");
return nullptr;
}
const char* start = static_cast<const char*>(view->BaseAddress());
size_t length = view->byteLength();
return IDBKey::CreateBinary(SharedBuffer::Create(start, length));
}
if (value->IsArray()) {
v8::Local<v8::Array> array = value.As<v8::Array>();
if (stack.Contains(array))
return nullptr;
if (stack.size() >= kMaximumDepth)
return nullptr;
stack.push_back(array);
IDBKey::KeyArray subkeys;
uint32_t length = array->Length();
v8::TryCatch block(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
for (uint32_t i = 0; i < length; ++i) {
bool has_own_property;
if (!array->HasOwnProperty(context, i).To(&has_own_property)) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
if (!has_own_property)
return nullptr;
v8::Local<v8::Value> item;
if (!array->Get(context, i).ToLocal(&item)) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
std::unique_ptr<IDBKey> subkey =
CreateIDBKeyFromValue(isolate, item, stack, exception_state);
if (!subkey)
subkeys.push_back(IDBKey::CreateInvalid());
else
subkeys.push_back(std::move(subkey));
}
stack.pop_back();
return IDBKey::CreateArray(std::move(subkeys));
}
return nullptr;
}
static std::unique_ptr<IDBKey> CreateIDBKeyFromValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
Vector<v8::Local<v8::Array>> stack;
std::unique_ptr<IDBKey> key =
CreateIDBKeyFromValue(isolate, value, stack, exception_state);
if (!key)
key = IDBKey::CreateInvalid();
return key;
}
// Indexed DB key paths should apply to explicitly copied properties (that
// will be "own" properties when deserialized) as well as the following.
// http://www.w3.org/TR/IndexedDB/#key-path-construct
static bool IsImplicitProperty(v8::Isolate* isolate,
v8::Local<v8::Value> value,
const String& name) {
if (value->IsString() && name == "length")
return true;
if (value->IsArray() && name == "length")
return true;
if (V8Blob::HasInstance(value, isolate))
return name == "size" || name == "type";
if (V8File::HasInstance(value, isolate))
return name == "name" || name == "lastModified" ||
name == "lastModifiedDate";
return false;
}
// Assumes a valid key path.
static Vector<String> ParseKeyPath(const String& key_path) {
Vector<String> elements;
IDBKeyPathParseError error;
IDBParseKeyPath(key_path, elements, error);
DCHECK_EQ(error, kIDBKeyPathParseErrorNone);
return elements;
}
static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
v8::Isolate* isolate,
v8::Local<v8::Value> v8_value,
const String& key_path,
ExceptionState& exception_state) {
Vector<String> key_path_elements = ParseKeyPath(key_path);
DCHECK(isolate->InContext());
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::TryCatch block(isolate);
for (wtf_size_t i = 0; i < key_path_elements.size(); ++i) {
const String& element = key_path_elements[i];
// Special cases from https://w3c.github.io/IndexedDB/#key-path-construct
// These access special or non-own properties directly, to avoid side
// effects.
if (v8_value->IsString() && element == "length") {
int32_t length = v8_value.As<v8::String>()->Length();
v8_value = v8::Number::New(isolate, length);
continue;
}
if (v8_value->IsArray() && element == "length") {
int32_t length = v8_value.As<v8::Array>()->Length();
v8_value = v8::Number::New(isolate, length);
continue;
}
if (!v8_value->IsObject())
return nullptr;
v8::Local<v8::Object> object = v8_value.As<v8::Object>();
if (V8Blob::HasInstance(object, isolate)) {
if (element == "size") {
v8_value = v8::Number::New(isolate, V8Blob::ToImpl(object)->size());
continue;
}
if (element == "type") {
v8_value = V8String(isolate, V8Blob::ToImpl(object)->type());
continue;
}
// Fall through.
}
if (V8File::HasInstance(object, isolate)) {
if (element == "name") {
v8_value = V8String(isolate, V8File::ToImpl(object)->name());
continue;
}
if (element == "lastModified") {
v8_value =
v8::Number::New(isolate, V8File::ToImpl(object)->lastModified());
continue;
}
if (element == "lastModifiedDate") {
v8_value =
v8::Date::New(context, V8File::ToImpl(object)->lastModifiedDate())
.ToLocalChecked();
continue;
}
// Fall through.
}
v8::Local<v8::String> key = V8String(isolate, element);
bool has_own_property;
if (!object->HasOwnProperty(context, key).To(&has_own_property)) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
if (!has_own_property)
return nullptr;
if (!object->Get(context, key).ToLocal(&v8_value)) {
exception_state.RethrowV8Exception(block.Exception());
return nullptr;
}
}
return CreateIDBKeyFromValue(isolate, v8_value, exception_state);
}
static std::unique_ptr<IDBKey> CreateIDBKeyFromValueAndKeyPath(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
const IDBKeyPath& key_path,
ExceptionState& exception_state) {
DCHECK(!key_path.IsNull());
v8::HandleScope handle_scope(isolate);
if (key_path.GetType() == IDBKeyPath::kArrayType) {
IDBKey::KeyArray result;
const Vector<String>& array = key_path.Array();
for (wtf_size_t i = 0; i < array.size(); ++i) {
result.emplace_back(CreateIDBKeyFromValueAndKeyPath(
isolate, value, array[i], exception_state));
if (!result.back())
return nullptr;
}
return IDBKey::CreateArray(std::move(result));
}
DCHECK_EQ(key_path.GetType(), IDBKeyPath::kStringType);
return CreateIDBKeyFromValueAndKeyPath(isolate, value, key_path.GetString(),
exception_state);
}
// Deserialize just the value data & blobInfo from the given IDBValue.
//
// Primary key injection is performed in deserializeIDBValue() below.
static v8::Local<v8::Value> DeserializeIDBValueData(v8::Isolate* isolate,
const IDBValue* value) {
DCHECK(isolate->InContext());
if (!value || value->IsNull())
return v8::Null(isolate);
scoped_refptr<SerializedScriptValue> serialized_value =
value->CreateSerializedValue();
SerializedScriptValue::DeserializeOptions options;
options.blob_info = &value->BlobInfo();
options.read_wasm_from_stream = true;
// deserialize() returns null when serialization fails. This is sub-optimal
// because IndexedDB values can be null, so an application cannot distinguish
// between a de-serialization failure and a legitimately stored null value.
//
// TODO(crbug.com/703704): Ideally, SerializedScriptValue should return an
// empty handle on serialization errors, which should be handled by higher
// layers. For example, IndexedDB could throw an exception, abort the
// transaction, or close the database connection.
return serialized_value->Deserialize(isolate, options);
}
// Deserialize the entire IDBValue.
//
// On top of deserializeIDBValueData(), this handles the special case of having
// to inject a key into the de-serialized value. See injectV8KeyIntoV8Value()
// for details.
v8::Local<v8::Value> DeserializeIDBValue(v8::Isolate* isolate,
v8::Local<v8::Object> creation_context,
const IDBValue* value) {
DCHECK(isolate->InContext());
if (!value || value->IsNull())
return v8::Null(isolate);
v8::Local<v8::Value> v8_value = DeserializeIDBValueData(isolate, value);
if (value->PrimaryKey()) {
v8::Local<v8::Value> key =
ToV8(value->PrimaryKey(), creation_context, isolate);
if (key.IsEmpty())
return v8::Local<v8::Value>();
InjectV8KeyIntoV8Value(isolate, key, v8_value, value->KeyPath());
// TODO(crbug.com/703704): Throw an error here or at a higher layer if
// injectV8KeyIntoV8Value() returns false, which means that the serialized
// value got corrupted while on disk.
}
return v8_value;
}
static v8::Local<v8::Value> DeserializeIDBValueArray(
v8::Isolate* isolate,
v8::Local<v8::Object> creation_context,
const Vector<std::unique_ptr<IDBValue>>& values) {
DCHECK(isolate->InContext());
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> array = v8::Array::New(isolate, values.size());
for (wtf_size_t i = 0; i < values.size(); ++i) {
v8::Local<v8::Value> v8_value =
DeserializeIDBValue(isolate, creation_context, values[i].get());
if (v8_value.IsEmpty())
v8_value = v8::Undefined(isolate);
bool created_property;
if (!array->CreateDataProperty(context, i, v8_value)
.To(&created_property) ||
!created_property)
return v8::Local<v8::Value>();
}
return array;
}
// Injects a primary key into a deserialized V8 value.
//
// In general, the value stored in IndexedDB is the serialized version of a
// value passed to the API. However, the specification has a special case of
// object stores that specify a key path and have a key generator. In this case,
// the conceptual description in the spec states that the key produced by the
// key generator is injected into the value before it is written to IndexedDB.
//
// We cannot implement the spec's conceptual description. We need to assign
// primary keys in the browser process, to ensure that multiple renderer
// processes talking to the same database receive sequential keys. At the same
// time, we want the value serialization code to live in the renderer process,
// because this type of code is a likely victim to security exploits.
//
// We handle this special case by serializing and writing values without the
// corresponding keys. At read time, we obtain the keys and the values
// separately, and we inject the keys into values.
bool InjectV8KeyIntoV8Value(v8::Isolate* isolate,
v8::Local<v8::Value> key,
v8::Local<v8::Value> value,
const IDBKeyPath& key_path) {
IDB_TRACE("injectIDBV8KeyIntoV8Value");
DCHECK(isolate->InContext());
DCHECK_EQ(key_path.GetType(), IDBKeyPath::kStringType);
Vector<String> key_path_elements = ParseKeyPath(key_path.GetString());
// The conbination of a key generator and an empty key path is forbidden by
// spec.
if (!key_path_elements.size()) {
NOTREACHED();
return false;
}
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
// For an object o = {} which should have keypath 'a.b.c' and key k, this
// populates o to be {a:{b:{}}}. This is only applied to deserialized
// values, so we can assume that there are no getters/setters on the
// object itself (though there might be on the prototype chain).
//
// Previous versions of this code assumed that the deserialized value meets
// the constraints checked by the serialization validation code. For example,
// given a keypath of a.b.c, the code assumed that the de-serialized value
// cannot possibly be {a:{b:42}}. This is not a safe assumption.
//
// IndexedDB's backing store (LevelDB) does use CRC32C to protect against disk
// errors. However, this does not prevent corruption caused by bugs in the
// higher level code writing invalid values. The following cases are
// interesting here.
//
// (1) Deserialization failures, which are currently handled by returning
// null. Disk errors aside, deserialization errors can also occur when a user
// switches channels and receives an older build which does not support the
// serialization format used by the previous (more recent) build that the user
// had.
//
// (2) Bugs that write a value which is incompatible with the primary key
// injection required by the object store. The simplest example is writing
// numbers or booleans to an object store with an auto-incrementing primary
// keys.
for (wtf_size_t i = 0; i < key_path_elements.size() - 1; ++i) {
if (!value->IsObject())
return false;
const String& key_path_element = key_path_elements[i];
DCHECK(!IsImplicitProperty(isolate, value, key_path_element));
v8::Local<v8::Object> object = value.As<v8::Object>();
v8::Local<v8::String> property = V8String(isolate, key_path_element);
bool has_own_property;
if (!object->HasOwnProperty(context, property).To(&has_own_property))
return false;
if (has_own_property) {
if (!object->Get(context, property).ToLocal(&value))
return false;
} else {
value = v8::Object::New(isolate);
bool created_property;
if (!object->CreateDataProperty(context, property, value)
.To(&created_property) ||
!created_property)
return false;
}
}
// Implicit properties don't need to be set. The caller is not required to
// be aware of this, so this is an expected no-op. The caller can verify
// that the value is correct via assertPrimaryKeyValidOrInjectable.
if (IsImplicitProperty(isolate, value, key_path_elements.back()))
return true;
// If the key path does not point to an implicit property, the value must be
// an object. Previous code versions DCHECKed this, which is unsafe in the
// event of database corruption or version skew in the serialization format.
if (!value->IsObject())
return false;
v8::Local<v8::Object> object = value.As<v8::Object>();
v8::Local<v8::String> property = V8String(isolate, key_path_elements.back());
bool created_property;
if (!object->CreateDataProperty(context, property, key).To(&created_property))
return false;
return created_property;
}
// Verify that an value can have an generated key inserted at the location
// specified by the key path (by injectV8KeyIntoV8Value) when the object is
// later deserialized.
bool CanInjectIDBKeyIntoScriptValue(v8::Isolate* isolate,
const ScriptValue& script_value,
const IDBKeyPath& key_path) {
IDB_TRACE("canInjectIDBKeyIntoScriptValue");
DCHECK_EQ(key_path.GetType(), IDBKeyPath::kStringType);
Vector<String> key_path_elements = ParseKeyPath(key_path.GetString());
if (!key_path_elements.size())
return false;
v8::Local<v8::Value> current(script_value.V8Value());
if (!current->IsObject())
return false;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
for (wtf_size_t i = 0; i < key_path_elements.size(); ++i) {
const String& key_path_element = key_path_elements[i];
// Can't overwrite properties like array or string length.
if (IsImplicitProperty(isolate, current, key_path_element))
return false;
// Can't set properties on non-objects.
if (!current->IsObject())
return false;
v8::Local<v8::Object> object = current.As<v8::Object>();
v8::Local<v8::String> property = V8String(isolate, key_path_element);
// If the value lacks an "own" property, it can be added - either as
// an intermediate object or as the final value.
bool has_own_property;
if (!object->HasOwnProperty(context, property).To(&has_own_property))
return false;
if (!has_own_property)
return true;
// Otherwise, get it and keep traversing.
if (!object->Get(context, property).ToLocal(&current))
return false;
}
return true;
}
ScriptValue DeserializeScriptValue(ScriptState* script_state,
SerializedScriptValue* serialized_value,
const Vector<WebBlobInfo>* blob_info,
bool read_wasm_from_stream) {
v8::Isolate* isolate = script_state->GetIsolate();
v8::HandleScope handle_scope(isolate);
if (!serialized_value)
return ScriptValue::CreateNull(script_state);
SerializedScriptValue::DeserializeOptions options;
options.blob_info = blob_info;
options.read_wasm_from_stream = read_wasm_from_stream;
return ScriptValue(script_state,
serialized_value->Deserialize(isolate, options));
}
SQLValue NativeValueTraits<SQLValue>::NativeValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
if (value.IsEmpty() || value->IsNull())
return SQLValue();
if (value->IsNumber())
return SQLValue(value.As<v8::Number>()->Value());
V8StringResource<> string_value(value);
if (!string_value.Prepare(exception_state))
return SQLValue();
return SQLValue(string_value);
}
std::unique_ptr<IDBKey> NativeValueTraits<std::unique_ptr<IDBKey>>::NativeValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return CreateIDBKeyFromValue(isolate, value, exception_state);
}
std::unique_ptr<IDBKey> NativeValueTraits<std::unique_ptr<IDBKey>>::NativeValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state,
const IDBKeyPath& key_path) {
IDB_TRACE("createIDBKeyFromValueAndKeyPath");
return CreateIDBKeyFromValueAndKeyPath(isolate, value, key_path,
exception_state);
}
IDBKeyRange* NativeValueTraits<IDBKeyRange*>::NativeValue(
v8::Isolate* isolate,
v8::Local<v8::Value> value,
ExceptionState& exception_state) {
return V8IDBKeyRange::ToImplWithTypeCheck(isolate, value);
}
#if DCHECK_IS_ON()
// This assertion is used when a value has been retrieved from an object store
// with implicit keys (i.e. a key path). It verifies that either the value
// contains an implicit key matching the primary key (so it was correctly
// extracted when stored) or that the key can be inserted as an own property.
void AssertPrimaryKeyValidOrInjectable(ScriptState* script_state,
const IDBValue* value) {
ScriptState::Scope scope(script_state);
v8::Isolate* isolate = script_state->GetIsolate();
ScriptValue key_value = ScriptValue::From(script_state, value->PrimaryKey());
ScriptValue script_value(script_state,
DeserializeIDBValueData(isolate, value));
DummyExceptionStateForTesting exception_state;
std::unique_ptr<IDBKey> expected_key = CreateIDBKeyFromValueAndKeyPath(
isolate, script_value.V8Value(), value->KeyPath(), exception_state);
DCHECK(!exception_state.HadException());
if (expected_key && expected_key->IsEqual(value->PrimaryKey()))
return;
bool injected = InjectV8KeyIntoV8Value(
isolate, key_value.V8Value(), script_value.V8Value(), value->KeyPath());
DCHECK(injected);
}
#endif // DCHECK_IS_ON()
} // namespace blink