1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
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.
13 *
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.
24 */
25
26 #include "config.h"
27 #include "bindings/v8/IDBBindingUtilities.h"
28
29 #include "V8DOMStringList.h"
30 #include "V8IDBCursor.h"
31 #include "V8IDBCursorWithValue.h"
32 #include "V8IDBDatabase.h"
33 #include "V8IDBIndex.h"
34 #include "V8IDBKeyRange.h"
35 #include "V8IDBObjectStore.h"
36 #include "V8IDBTransaction.h"
37 #include "bindings/v8/DOMRequestState.h"
38 #include "bindings/v8/SerializedScriptValue.h"
39 #include "bindings/v8/V8Binding.h"
40 #include "bindings/v8/custom/V8ArrayBufferViewCustom.h"
41 #include "bindings/v8/custom/V8Uint8ArrayCustom.h"
42 #include "modules/indexeddb/IDBKey.h"
43 #include "modules/indexeddb/IDBKeyPath.h"
44 #include "modules/indexeddb/IDBKeyRange.h"
45 #include "modules/indexeddb/IDBTracing.h"
46 #include "platform/SharedBuffer.h"
47 #include "wtf/ArrayBufferView.h"
48 #include "wtf/MathExtras.h"
49 #include "wtf/Uint8Array.h"
50 #include "wtf/Vector.h"
51
52 namespace WebCore {
53
54 v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer*, v8::Isolate*);
55
toV8(const IDBKeyPath & value,v8::Handle<v8::Object> creationContext,v8::Isolate * isolate)56 static v8::Handle<v8::Value> toV8(const IDBKeyPath& value, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
57 {
58 switch (value.type()) {
59 case IDBKeyPath::NullType:
60 return v8::Null(isolate);
61 case IDBKeyPath::StringType:
62 return v8String(isolate, value.string());
63 case IDBKeyPath::ArrayType:
64 RefPtr<DOMStringList> keyPaths = DOMStringList::create();
65 for (Vector<String>::const_iterator it = value.array().begin(); it != value.array().end(); ++it)
66 keyPaths->append(*it);
67 return toV8(keyPaths.release(), creationContext, isolate);
68 }
69 ASSERT_NOT_REACHED();
70 return v8::Undefined(isolate);
71 }
72
toV8(const IDBKey * key,v8::Handle<v8::Object> creationContext,v8::Isolate * isolate)73 v8::Handle<v8::Value> toV8(const IDBKey* key, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
74 {
75 if (!key) {
76 // This should be undefined, not null.
77 // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange
78 return v8Undefined();
79 }
80
81 switch (key->type()) {
82 case IDBKey::InvalidType:
83 case IDBKey::MinType:
84 ASSERT_NOT_REACHED();
85 return v8Undefined();
86 case IDBKey::NumberType:
87 return v8::Number::New(isolate, key->number());
88 case IDBKey::StringType:
89 return v8String(isolate, key->string());
90 case IDBKey::BinaryType:
91 return toV8(Uint8Array::create(reinterpret_cast<const unsigned char*>(key->binary()->data()), key->binary()->size()), creationContext, isolate);
92 case IDBKey::DateType:
93 return v8::Date::New(isolate, key->date());
94 case IDBKey::ArrayType:
95 {
96 v8::Local<v8::Array> array = v8::Array::New(isolate, key->array().size());
97 for (size_t i = 0; i < key->array().size(); ++i)
98 array->Set(i, toV8(key->array()[i].get(), creationContext, isolate));
99 return array;
100 }
101 }
102
103 ASSERT_NOT_REACHED();
104 return v8Undefined();
105 }
106
toV8(const IDBAny * impl,v8::Handle<v8::Object> creationContext,v8::Isolate * isolate)107 v8::Handle<v8::Value> toV8(const IDBAny* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
108 {
109 if (!impl)
110 return v8::Null(isolate);
111
112 switch (impl->type()) {
113 case IDBAny::UndefinedType:
114 return v8::Undefined(isolate);
115 case IDBAny::NullType:
116 return v8::Null(isolate);
117 case IDBAny::DOMStringListType:
118 return toV8(impl->domStringList(), creationContext, isolate);
119 case IDBAny::IDBCursorType:
120 return toV8(impl->idbCursor(), creationContext, isolate);
121 case IDBAny::IDBCursorWithValueType:
122 return toV8(impl->idbCursorWithValue(), creationContext, isolate);
123 case IDBAny::IDBDatabaseType:
124 return toV8(impl->idbDatabase(), creationContext, isolate);
125 case IDBAny::IDBIndexType:
126 return toV8(impl->idbIndex(), creationContext, isolate);
127 case IDBAny::IDBObjectStoreType:
128 return toV8(impl->idbObjectStore(), creationContext, isolate);
129 case IDBAny::IDBTransactionType:
130 return toV8(impl->idbTransaction(), creationContext, isolate);
131 case IDBAny::BufferType:
132 return deserializeIDBValueBuffer(impl->buffer(), isolate);
133 case IDBAny::StringType:
134 return v8String(isolate, impl->string());
135 case IDBAny::IntegerType:
136 return v8::Number::New(isolate, impl->integer());
137 case IDBAny::KeyType:
138 return toV8(impl->key(), creationContext, isolate);
139 case IDBAny::KeyPathType:
140 return toV8(impl->keyPath(), creationContext, isolate);
141 case IDBAny::BufferKeyAndKeyPathType: {
142 v8::Handle<v8::Value> value = deserializeIDBValueBuffer(impl->buffer(), isolate);
143 v8::Handle<v8::Value> key = toV8(impl->key(), creationContext, isolate);
144 bool injected = injectV8KeyIntoV8Value(key, value, impl->keyPath(), isolate);
145 ASSERT_UNUSED(injected, injected);
146 return value;
147 }
148 }
149
150 ASSERT_NOT_REACHED();
151 return v8::Undefined(isolate);
152 }
153
154 static const size_t maximumDepth = 2000;
155
createIDBKeyFromValue(v8::Handle<v8::Value> value,Vector<v8::Handle<v8::Array>> & stack,v8::Isolate * isolate)156 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack, v8::Isolate* isolate)
157 {
158 if (value->IsNumber() && !std::isnan(value->NumberValue()))
159 return IDBKey::createNumber(value->NumberValue());
160 if (value->IsString())
161 return IDBKey::createString(toCoreString(value.As<v8::String>()));
162 if (value->IsDate() && !std::isnan(value->NumberValue()))
163 return IDBKey::createDate(value->NumberValue());
164 if (value->IsArray()) {
165 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
166
167 if (stack.contains(array))
168 return 0;
169 if (stack.size() >= maximumDepth)
170 return 0;
171 stack.append(array);
172
173 IDBKey::KeyArray subkeys;
174 uint32_t length = array->Length();
175 for (uint32_t i = 0; i < length; ++i) {
176 v8::Local<v8::Value> item = array->Get(v8::Int32::New(i, isolate));
177 RefPtr<IDBKey> subkey = createIDBKeyFromValue(item, stack, isolate);
178 if (!subkey)
179 subkeys.append(IDBKey::createInvalid());
180 else
181 subkeys.append(subkey);
182 }
183
184 stack.removeLast();
185 return IDBKey::createArray(subkeys);
186 }
187 return 0;
188 }
189
createIDBKeyFromValue(v8::Handle<v8::Value> value,v8::Isolate * isolate)190 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, v8::Isolate* isolate)
191 {
192 Vector<v8::Handle<v8::Array> > stack;
193 RefPtr<IDBKey> key = createIDBKeyFromValue(value, stack, isolate);
194 if (key)
195 return key;
196 return IDBKey::createInvalid();
197 }
198
199 template<typename T>
getValueFrom(T indexOrName,v8::Handle<v8::Value> & v8Value)200 static bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
201 {
202 v8::Local<v8::Object> object = v8Value->ToObject();
203 if (!object->Has(indexOrName))
204 return false;
205 v8Value = object->Get(indexOrName);
206 return true;
207 }
208
209 template<typename T>
setValue(v8::Handle<v8::Value> & v8Object,T indexOrName,const v8::Handle<v8::Value> & v8Value)210 static bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
211 {
212 v8::Local<v8::Object> object = v8Object->ToObject();
213 return object->Set(indexOrName, v8Value);
214 }
215
get(v8::Handle<v8::Value> & object,const String & keyPathElement,v8::Handle<v8::Value> & result,v8::Isolate * isolate)216 static bool get(v8::Handle<v8::Value>& object, const String& keyPathElement, v8::Handle<v8::Value>& result, v8::Isolate* isolate)
217 {
218 if (object->IsString() && keyPathElement == "length") {
219 int32_t length = v8::Handle<v8::String>::Cast(object)->Length();
220 result = v8::Number::New(isolate, length);
221 return true;
222 }
223 return object->IsObject() && getValueFrom(v8String(isolate, keyPathElement), result);
224 }
225
canSet(v8::Handle<v8::Value> & object,const String & keyPathElement)226 static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
227 {
228 return object->IsObject();
229 }
230
set(v8::Handle<v8::Value> & object,const String & keyPathElement,const v8::Handle<v8::Value> & v8Value,v8::Isolate * isolate)231 static bool set(v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value, v8::Isolate* isolate)
232 {
233 return canSet(object, keyPathElement) && setValue(object, v8String(isolate, keyPathElement), v8Value);
234 }
235
getNthValueOnKeyPath(v8::Handle<v8::Value> & rootValue,const Vector<String> & keyPathElements,size_t index,v8::Isolate * isolate)236 static v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
237 {
238 v8::Handle<v8::Value> currentValue(rootValue);
239 ASSERT(index <= keyPathElements.size());
240 for (size_t i = 0; i < index; ++i) {
241 v8::Handle<v8::Value> parentValue(currentValue);
242 if (!get(parentValue, keyPathElements[i], currentValue, isolate))
243 return v8Undefined();
244 }
245
246 return currentValue;
247 }
248
canInjectNthValueOnKeyPath(v8::Handle<v8::Value> & rootValue,const Vector<String> & keyPathElements,size_t index,v8::Isolate * isolate)249 static bool canInjectNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
250 {
251 if (!rootValue->IsObject())
252 return false;
253
254 v8::Handle<v8::Value> currentValue(rootValue);
255
256 ASSERT(index <= keyPathElements.size());
257 for (size_t i = 0; i < index; ++i) {
258 v8::Handle<v8::Value> parentValue(currentValue);
259 const String& keyPathElement = keyPathElements[i];
260 if (!get(parentValue, keyPathElement, currentValue, isolate))
261 return canSet(parentValue, keyPathElement);
262 }
263 return true;
264 }
265
266
ensureNthValueOnKeyPath(v8::Handle<v8::Value> & rootValue,const Vector<String> & keyPathElements,size_t index,v8::Isolate * isolate)267 static v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
268 {
269 v8::Handle<v8::Value> currentValue(rootValue);
270
271 ASSERT(index <= keyPathElements.size());
272 for (size_t i = 0; i < index; ++i) {
273 v8::Handle<v8::Value> parentValue(currentValue);
274 const String& keyPathElement = keyPathElements[i];
275 if (!get(parentValue, keyPathElement, currentValue, isolate)) {
276 v8::Handle<v8::Object> object = v8::Object::New();
277 if (!set(parentValue, keyPathElement, object, isolate))
278 return v8Undefined();
279 currentValue = object;
280 }
281 }
282
283 return currentValue;
284 }
285
createIDBKeyFromScriptValueAndKeyPath(const ScriptValue & value,const String & keyPath,v8::Isolate * isolate)286 static PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(const ScriptValue& value, const String& keyPath, v8::Isolate* isolate)
287 {
288 Vector<String> keyPathElements;
289 IDBKeyPathParseError error;
290 IDBParseKeyPath(keyPath, keyPathElements, error);
291 ASSERT(error == IDBKeyPathParseErrorNone);
292 ASSERT(isolate->InContext());
293
294 v8::HandleScope handleScope(isolate);
295 v8::Handle<v8::Value> v8Value(value.v8Value());
296 v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size(), isolate));
297 if (v8Key.IsEmpty())
298 return 0;
299 return createIDBKeyFromValue(v8Key, isolate);
300 }
301
createIDBKeyFromScriptValueAndKeyPath(DOMRequestState * state,const ScriptValue & value,const IDBKeyPath & keyPath)302 PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* state, const ScriptValue& value, const IDBKeyPath& keyPath)
303 {
304 IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath");
305 ASSERT(!keyPath.isNull());
306 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
307 ASSERT(isolate->InContext());
308 v8::HandleScope handleScope(isolate);
309 if (keyPath.type() == IDBKeyPath::ArrayType) {
310 IDBKey::KeyArray result;
311 const Vector<String>& array = keyPath.array();
312 for (size_t i = 0; i < array.size(); ++i) {
313 RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(value, array[i], isolate);
314 if (!key)
315 return 0;
316 result.append(key);
317 }
318 return IDBKey::createArray(result);
319 }
320
321 ASSERT(keyPath.type() == IDBKeyPath::StringType);
322 return createIDBKeyFromScriptValueAndKeyPath(value, keyPath.string(), isolate);
323 }
324
deserializeIDBValueBuffer(SharedBuffer * buffer,v8::Isolate * isolate)325 v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer* buffer, v8::Isolate* isolate)
326 {
327 ASSERT(isolate->InContext());
328 if (!buffer)
329 return v8::Null(isolate);
330
331 // FIXME: The extra copy here can be eliminated by allowing SerializedScriptValue to take a raw const char* or const uint8_t*.
332 Vector<uint8_t> value;
333 value.append(buffer->data(), buffer->size());
334 RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(value);
335 return serializedValue->deserialize(isolate);
336 }
337
injectV8KeyIntoV8Value(v8::Handle<v8::Value> key,v8::Handle<v8::Value> value,const IDBKeyPath & keyPath,v8::Isolate * isolate)338 bool injectV8KeyIntoV8Value(v8::Handle<v8::Value> key, v8::Handle<v8::Value> value, const IDBKeyPath& keyPath, v8::Isolate* isolate)
339 {
340 IDB_TRACE("injectIDBV8KeyIntoV8Value");
341 ASSERT(isolate->InContext());
342
343 ASSERT(keyPath.type() == IDBKeyPath::StringType);
344 Vector<String> keyPathElements;
345 IDBKeyPathParseError error;
346 IDBParseKeyPath(keyPath.string(), keyPathElements, error);
347 ASSERT(error == IDBKeyPathParseErrorNone);
348
349 if (!keyPathElements.size())
350 return false;
351
352 v8::HandleScope handleScope(isolate);
353 v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(value, keyPathElements, keyPathElements.size() - 1, isolate));
354 if (parent.IsEmpty())
355 return false;
356
357 if (!set(parent, keyPathElements.last(), key, isolate))
358 return false;
359
360 return true;
361 }
362
canInjectIDBKeyIntoScriptValue(DOMRequestState * state,const ScriptValue & scriptValue,const IDBKeyPath & keyPath)363 bool canInjectIDBKeyIntoScriptValue(DOMRequestState* state, const ScriptValue& scriptValue, const IDBKeyPath& keyPath)
364 {
365 IDB_TRACE("canInjectIDBKeyIntoScriptValue");
366 ASSERT(keyPath.type() == IDBKeyPath::StringType);
367 Vector<String> keyPathElements;
368 IDBKeyPathParseError error;
369 IDBParseKeyPath(keyPath.string(), keyPathElements, error);
370 ASSERT(error == IDBKeyPathParseErrorNone);
371
372 if (!keyPathElements.size())
373 return false;
374
375 v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
376 return canInjectNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1, state->context()->GetIsolate());
377 }
378
idbAnyToScriptValue(DOMRequestState * state,PassRefPtr<IDBAny> any)379 ScriptValue idbAnyToScriptValue(DOMRequestState* state, PassRefPtr<IDBAny> any)
380 {
381 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
382 ASSERT(isolate->InContext());
383 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext();
384 v8::HandleScope handleScope(isolate);
385 v8::Handle<v8::Value> v8Value(toV8(any.get(), context->Global(), isolate));
386 return ScriptValue(v8Value, isolate);
387 }
388
idbKeyToScriptValue(DOMRequestState * state,PassRefPtr<IDBKey> key)389 ScriptValue idbKeyToScriptValue(DOMRequestState* state, PassRefPtr<IDBKey> key)
390 {
391 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
392 ASSERT(isolate->InContext());
393 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext();
394 v8::HandleScope handleScope(isolate);
395 v8::Handle<v8::Value> v8Value(toV8(key.get(), context->Global(), isolate));
396 return ScriptValue(v8Value, isolate);
397 }
398
scriptValueToIDBKey(DOMRequestState * state,const ScriptValue & scriptValue)399 PassRefPtr<IDBKey> scriptValueToIDBKey(DOMRequestState* state, const ScriptValue& scriptValue)
400 {
401 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
402 ASSERT(isolate->InContext());
403 v8::HandleScope handleScope(isolate);
404 v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
405 return createIDBKeyFromValue(v8Value, isolate);
406 }
407
scriptValueToIDBKeyRange(DOMRequestState * state,const ScriptValue & scriptValue)408 PassRefPtr<IDBKeyRange> scriptValueToIDBKeyRange(DOMRequestState* state, const ScriptValue& scriptValue)
409 {
410 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
411 v8::HandleScope handleScope(isolate);
412 v8::Handle<v8::Value> value(scriptValue.v8Value());
413 if (V8IDBKeyRange::hasInstance(value, isolate, worldType(isolate)))
414 return V8IDBKeyRange::toNative(value.As<v8::Object>());
415 return 0;
416 }
417
418 #ifndef NDEBUG
assertPrimaryKeyValidOrInjectable(DOMRequestState * state,PassRefPtr<SharedBuffer> buffer,PassRefPtr<IDBKey> prpKey,const IDBKeyPath & keyPath)419 void assertPrimaryKeyValidOrInjectable(DOMRequestState* state, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> prpKey, const IDBKeyPath& keyPath)
420 {
421 RefPtr<IDBKey> key(prpKey);
422
423 DOMRequestState::Scope scope(*state);
424 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
425
426 ScriptValue keyValue = idbKeyToScriptValue(state, key);
427 ScriptValue scriptValue(deserializeIDBValueBuffer(buffer.get(), isolate), isolate);
428
429 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(state, scriptValue, keyPath);
430 ASSERT(!expectedKey || expectedKey->isEqual(key.get()));
431
432 bool injected = injectV8KeyIntoV8Value(keyValue.v8Value(), scriptValue.v8Value(), keyPath, isolate);
433 ASSERT_UNUSED(injected, injected);
434 }
435 #endif
436
437 } // namespace WebCore
438