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
52namespace WebCore {
53
54v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer*, v8::Isolate*);
55
56static 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
73v8::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
107v8::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
154static const size_t maximumDepth = 2000;
155
156static 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
190static 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
199template<typename T>
200static 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
209template<typename T>
210static 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
216static 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
226static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
227{
228    return object->IsObject();
229}
230
231static 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
236static 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
249static 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
267static 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
286static 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
302PassRefPtr<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
325v8::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
338bool 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
363bool 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
379ScriptValue 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
389ScriptValue 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
399PassRefPtr<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
408PassRefPtr<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
419void 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