2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "bindings/v8/IDBBindingUtilities.h"
29 #include "RuntimeEnabledFeatures.h"
30 #include "V8DOMStringList.h"
31 #include "V8IDBCursor.h"
32 #include "V8IDBCursorWithValue.h"
33 #include "V8IDBDatabase.h"
34 #include "V8IDBIndex.h"
35 #include "V8IDBKeyRange.h"
36 #include "V8IDBObjectStore.h"
37 #include "V8IDBRequest.h"
38 #include "V8IDBTransaction.h"
39 #include "bindings/v8/DOMRequestState.h"
40 #include "bindings/v8/SerializedScriptValue.h"
41 #include "bindings/v8/V8Binding.h"
42 #include "bindings/v8/custom/V8ArrayBufferViewCustom.h"
43 #include "bindings/v8/custom/V8Uint8ArrayCustom.h"
44 #include "modules/indexeddb/IDBKey.h"
45 #include "modules/indexeddb/IDBKeyPath.h"
46 #include "modules/indexeddb/IDBKeyRange.h"
47 #include "modules/indexeddb/IDBTracing.h"
48 #include "platform/SharedBuffer.h"
49 #include "wtf/ArrayBufferView.h"
50 #include "wtf/MathExtras.h"
51 #include "wtf/Uint8Array.h"
52 #include "wtf/Vector.h"
56 static v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer*, v8::Isolate*);
58 static v8::Handle<v8::Value> toV8(const IDBKeyPath& value, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
60 switch (value.type()) {
61 case IDBKeyPath::NullType:
62 return v8::Null(isolate);
63 case IDBKeyPath::StringType:
64 return v8String(isolate, value.string());
65 case IDBKeyPath::ArrayType:
66 RefPtr<DOMStringList> keyPaths = DOMStringList::create();
67 for (Vector<String>::const_iterator it = value.array().begin(); it != value.array().end(); ++it)
68 keyPaths->append(*it);
69 return toV8(keyPaths.release(), creationContext, isolate);
72 return v8::Undefined(isolate);
75 static v8::Handle<v8::Value> toV8(const IDBKey* key, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
78 // This should be undefined, not null.
79 // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange
83 switch (key->type()) {
84 case IDBKey::InvalidType:
88 case IDBKey::NumberType:
89 return v8::Number::New(isolate, key->number());
90 case IDBKey::StringType:
91 return v8String(isolate, key->string());
92 case IDBKey::BinaryType:
93 return toV8(Uint8Array::create(reinterpret_cast<const unsigned char*>(key->binary()->data()), key->binary()->size()), creationContext, isolate);
94 case IDBKey::DateType:
95 return v8::Date::New(isolate, key->date());
96 case IDBKey::ArrayType:
98 v8::Local<v8::Array> array = v8::Array::New(isolate, key->array().size());
99 for (size_t i = 0; i < key->array().size(); ++i)
100 array->Set(i, toV8(key->array()[i].get(), creationContext, isolate));
105 ASSERT_NOT_REACHED();
106 return v8Undefined();
109 static v8::Handle<v8::Value> toV8(const IDBAny* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
112 return v8::Null(isolate);
114 switch (impl->type()) {
115 case IDBAny::UndefinedType:
116 return v8::Undefined(isolate);
117 case IDBAny::NullType:
118 return v8::Null(isolate);
119 case IDBAny::DOMStringListType:
120 return toV8(impl->domStringList(), creationContext, isolate);
121 case IDBAny::IDBCursorType: {
122 // Ensure request wrapper is kept alive at least as long as the cursor wrapper,
123 // so that event listeners are retained.
124 v8::Handle<v8::Value> cursor = toV8(impl->idbCursor(), creationContext, isolate);
125 v8::Handle<v8::Value> request = toV8(impl->idbCursor()->request(), creationContext, isolate);
126 setHiddenValue(isolate, cursor->ToObject(), "idbCursorRequest", request);
129 case IDBAny::IDBCursorWithValueType: {
130 // Ensure request wrapper is kept alive at least as long as the cursor wrapper,
131 // so that event listeners are retained.
132 v8::Handle<v8::Value> cursor = toV8(impl->idbCursorWithValue(), creationContext, isolate);
133 v8::Handle<v8::Value> request = toV8(impl->idbCursorWithValue()->request(), creationContext, isolate);
134 setHiddenValue(isolate, cursor->ToObject(), "idbCursorRequest", request);
137 case IDBAny::IDBDatabaseType:
138 return toV8(impl->idbDatabase(), creationContext, isolate);
139 case IDBAny::IDBIndexType:
140 return toV8(impl->idbIndex(), creationContext, isolate);
141 case IDBAny::IDBObjectStoreType:
142 return toV8(impl->idbObjectStore(), creationContext, isolate);
143 case IDBAny::IDBTransactionType:
144 return toV8(impl->idbTransaction(), creationContext, isolate);
145 case IDBAny::BufferType:
146 return deserializeIDBValueBuffer(impl->buffer(), isolate);
147 case IDBAny::StringType:
148 return v8String(isolate, impl->string());
149 case IDBAny::IntegerType:
150 return v8::Number::New(isolate, impl->integer());
151 case IDBAny::KeyType:
152 return toV8(impl->key(), creationContext, isolate);
153 case IDBAny::KeyPathType:
154 return toV8(impl->keyPath(), creationContext, isolate);
155 case IDBAny::BufferKeyAndKeyPathType: {
156 v8::Handle<v8::Value> value = deserializeIDBValueBuffer(impl->buffer(), isolate);
157 v8::Handle<v8::Value> key = toV8(impl->key(), creationContext, isolate);
158 bool injected = injectV8KeyIntoV8Value(key, value, impl->keyPath(), isolate);
159 ASSERT_UNUSED(injected, injected);
164 ASSERT_NOT_REACHED();
165 return v8::Undefined(isolate);
168 static const size_t maximumDepth = 2000;
170 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack, v8::Isolate* isolate, bool allowExperimentalTypes = false)
172 if (value->IsNumber() && !std::isnan(value->NumberValue()))
173 return IDBKey::createNumber(value->NumberValue());
174 if (value->IsString())
175 return IDBKey::createString(toCoreString(value.As<v8::String>()));
176 if (value->IsDate() && !std::isnan(value->NumberValue()))
177 return IDBKey::createDate(value->NumberValue());
178 if (value->IsUint8Array() && (allowExperimentalTypes || RuntimeEnabledFeatures::indexedDBExperimentalEnabled())) {
179 // Per discussion in https://www.w3.org/Bugs/Public/show_bug.cgi?id=23332 the
180 // input type is constrained to Uint8Array to match the output type.
181 ArrayBufferView* view = WebCore::V8ArrayBufferView::toNative(value->ToObject());
182 const char* start = static_cast<const char*>(view->baseAddress());
183 size_t length = view->byteLength();
184 return IDBKey::createBinary(SharedBuffer::create(start, length));
186 if (value->IsArray()) {
187 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
189 if (stack.contains(array))
191 if (stack.size() >= maximumDepth)
195 IDBKey::KeyArray subkeys;
196 uint32_t length = array->Length();
197 for (uint32_t i = 0; i < length; ++i) {
198 v8::Local<v8::Value> item = array->Get(v8::Int32::New(isolate, i));
199 RefPtr<IDBKey> subkey = createIDBKeyFromValue(item, stack, isolate, allowExperimentalTypes);
201 subkeys.append(IDBKey::createInvalid());
203 subkeys.append(subkey);
207 return IDBKey::createArray(subkeys);
212 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, v8::Isolate* isolate, bool allowExperimentalTypes = false)
214 Vector<v8::Handle<v8::Array> > stack;
215 RefPtr<IDBKey> key = createIDBKeyFromValue(value, stack, isolate, allowExperimentalTypes);
218 return IDBKey::createInvalid();
222 static bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
224 v8::Local<v8::Object> object = v8Value->ToObject();
225 if (!object->Has(indexOrName))
227 v8Value = object->Get(indexOrName);
232 static bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
234 v8::Local<v8::Object> object = v8Object->ToObject();
235 return object->Set(indexOrName, v8Value);
238 static bool get(v8::Handle<v8::Value>& object, const String& keyPathElement, v8::Handle<v8::Value>& result, v8::Isolate* isolate)
240 if (object->IsString() && keyPathElement == "length") {
241 int32_t length = v8::Handle<v8::String>::Cast(object)->Length();
242 result = v8::Number::New(isolate, length);
245 return object->IsObject() && getValueFrom(v8String(isolate, keyPathElement), result);
248 static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
250 return object->IsObject();
253 static bool set(v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value, v8::Isolate* isolate)
255 return canSet(object, keyPathElement) && setValue(object, v8String(isolate, keyPathElement), v8Value);
258 static v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
260 v8::Handle<v8::Value> currentValue(rootValue);
261 ASSERT(index <= keyPathElements.size());
262 for (size_t i = 0; i < index; ++i) {
263 v8::Handle<v8::Value> parentValue(currentValue);
264 if (!get(parentValue, keyPathElements[i], currentValue, isolate))
265 return v8Undefined();
271 static bool canInjectNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
273 if (!rootValue->IsObject())
276 v8::Handle<v8::Value> currentValue(rootValue);
278 ASSERT(index <= keyPathElements.size());
279 for (size_t i = 0; i < index; ++i) {
280 v8::Handle<v8::Value> parentValue(currentValue);
281 const String& keyPathElement = keyPathElements[i];
282 if (!get(parentValue, keyPathElement, currentValue, isolate))
283 return canSet(parentValue, keyPathElement);
289 static v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
291 v8::Handle<v8::Value> currentValue(rootValue);
293 ASSERT(index <= keyPathElements.size());
294 for (size_t i = 0; i < index; ++i) {
295 v8::Handle<v8::Value> parentValue(currentValue);
296 const String& keyPathElement = keyPathElements[i];
297 if (!get(parentValue, keyPathElement, currentValue, isolate)) {
298 v8::Handle<v8::Object> object = v8::Object::New(isolate);
299 if (!set(parentValue, keyPathElement, object, isolate))
300 return v8Undefined();
301 currentValue = object;
308 static PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(const ScriptValue& value, const String& keyPath, v8::Isolate* isolate, bool allowExperimentalTypes)
310 Vector<String> keyPathElements;
311 IDBKeyPathParseError error;
312 IDBParseKeyPath(keyPath, keyPathElements, error);
313 ASSERT(error == IDBKeyPathParseErrorNone);
314 ASSERT(isolate->InContext());
316 v8::HandleScope handleScope(isolate);
317 v8::Handle<v8::Value> v8Value(value.v8Value());
318 v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size(), isolate));
321 return createIDBKeyFromValue(v8Key, isolate, allowExperimentalTypes);
324 static PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(const ScriptValue& value, const IDBKeyPath& keyPath, v8::Isolate* isolate, bool allowExperimentalTypes = false)
326 ASSERT(!keyPath.isNull());
327 v8::HandleScope handleScope(isolate);
328 if (keyPath.type() == IDBKeyPath::ArrayType) {
329 IDBKey::KeyArray result;
330 const Vector<String>& array = keyPath.array();
331 for (size_t i = 0; i < array.size(); ++i) {
332 RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(value, array[i], isolate, allowExperimentalTypes);
337 return IDBKey::createArray(result);
340 ASSERT(keyPath.type() == IDBKeyPath::StringType);
341 return createIDBKeyFromScriptValueAndKeyPath(value, keyPath.string(), isolate, allowExperimentalTypes);
344 PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* state, const ScriptValue& value, const IDBKeyPath& keyPath)
346 IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath");
347 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
348 return createIDBKeyFromScriptValueAndKeyPath(value, keyPath, isolate);
351 static v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer* buffer, v8::Isolate* isolate)
353 ASSERT(isolate->InContext());
355 return v8::Null(isolate);
357 // FIXME: The extra copy here can be eliminated by allowing SerializedScriptValue to take a raw const char* or const uint8_t*.
358 Vector<uint8_t> value;
359 value.append(buffer->data(), buffer->size());
360 RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(value);
361 return serializedValue->deserialize(isolate);
364 bool injectV8KeyIntoV8Value(v8::Handle<v8::Value> key, v8::Handle<v8::Value> value, const IDBKeyPath& keyPath, v8::Isolate* isolate)
366 IDB_TRACE("injectIDBV8KeyIntoV8Value");
367 ASSERT(isolate->InContext());
369 ASSERT(keyPath.type() == IDBKeyPath::StringType);
370 Vector<String> keyPathElements;
371 IDBKeyPathParseError error;
372 IDBParseKeyPath(keyPath.string(), keyPathElements, error);
373 ASSERT(error == IDBKeyPathParseErrorNone);
375 if (!keyPathElements.size())
378 v8::HandleScope handleScope(isolate);
379 v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(value, keyPathElements, keyPathElements.size() - 1, isolate));
380 if (parent.IsEmpty())
383 if (!set(parent, keyPathElements.last(), key, isolate))
389 bool canInjectIDBKeyIntoScriptValue(DOMRequestState* state, const ScriptValue& scriptValue, const IDBKeyPath& keyPath)
391 IDB_TRACE("canInjectIDBKeyIntoScriptValue");
392 ASSERT(keyPath.type() == IDBKeyPath::StringType);
393 Vector<String> keyPathElements;
394 IDBKeyPathParseError error;
395 IDBParseKeyPath(keyPath.string(), keyPathElements, error);
396 ASSERT(error == IDBKeyPathParseErrorNone);
398 if (!keyPathElements.size())
401 v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
402 return canInjectNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1, state->context()->GetIsolate());
405 ScriptValue idbAnyToScriptValue(DOMRequestState* state, PassRefPtr<IDBAny> any)
407 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
408 ASSERT(isolate->InContext());
409 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext();
410 v8::HandleScope handleScope(isolate);
411 v8::Handle<v8::Value> v8Value(toV8(any.get(), context->Global(), isolate));
412 return ScriptValue(v8Value, isolate);
415 ScriptValue idbKeyToScriptValue(DOMRequestState* state, PassRefPtr<IDBKey> key)
417 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
418 ASSERT(isolate->InContext());
419 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext();
420 v8::HandleScope handleScope(isolate);
421 v8::Handle<v8::Value> v8Value(toV8(key.get(), context->Global(), isolate));
422 return ScriptValue(v8Value, isolate);
425 PassRefPtr<IDBKey> scriptValueToIDBKey(DOMRequestState* state, const ScriptValue& scriptValue)
427 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
428 ASSERT(isolate->InContext());
429 v8::HandleScope handleScope(isolate);
430 v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
431 return createIDBKeyFromValue(v8Value, isolate);
434 PassRefPtr<IDBKeyRange> scriptValueToIDBKeyRange(DOMRequestState* state, const ScriptValue& scriptValue)
436 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
437 v8::HandleScope handleScope(isolate);
438 v8::Handle<v8::Value> value(scriptValue.v8Value());
439 if (V8IDBKeyRange::hasInstance(value, isolate))
440 return V8IDBKeyRange::toNative(value.As<v8::Object>());
445 void assertPrimaryKeyValidOrInjectable(DOMRequestState* state, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> prpKey, const IDBKeyPath& keyPath)
447 RefPtr<IDBKey> key(prpKey);
449 DOMRequestState::Scope scope(*state);
450 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
452 ScriptValue keyValue = idbKeyToScriptValue(state, key);
453 ScriptValue scriptValue(deserializeIDBValueBuffer(buffer.get(), isolate), isolate);
455 // This assertion is about already persisted data, so allow experimental types.
456 const bool allowExperimentalTypes = true;
457 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(scriptValue, keyPath, isolate, allowExperimentalTypes);
458 ASSERT(!expectedKey || expectedKey->isEqual(key.get()));
460 bool injected = injectV8KeyIntoV8Value(keyValue.v8Value(), scriptValue.v8Value(), keyPath, isolate);
461 ASSERT_UNUSED(injected, injected);
465 } // namespace WebCore