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 "bindings/v8/DOMRequestState.h"
30#include "bindings/v8/SerializedScriptValue.h"
31#include "bindings/v8/V8Binding.h"
32#include "core/platform/SharedBuffer.h"
33#include "modules/indexeddb/IDBKey.h"
34#include "modules/indexeddb/IDBKeyPath.h"
35#include "modules/indexeddb/IDBTracing.h"
36#include "wtf/MathExtras.h"
37#include "wtf/Vector.h"
38
39namespace WebCore {
40
41static v8::Handle<v8::Value> idbKeyToV8Value(IDBKey* key, v8::Isolate* isolate)
42{
43    if (!key) {
44        // This should be undefined, not null.
45        // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange
46        return v8Undefined();
47    }
48
49    switch (key->type()) {
50    case IDBKey::InvalidType:
51    case IDBKey::MinType:
52        ASSERT_NOT_REACHED();
53        return v8Undefined();
54    case IDBKey::NumberType:
55        return v8::Number::New(key->number());
56    case IDBKey::StringType:
57        return v8String(key->string(), isolate);
58    case IDBKey::DateType:
59        return v8::Date::New(key->date());
60    case IDBKey::ArrayType:
61        {
62            v8::Local<v8::Array> array = v8::Array::New(key->array().size());
63            for (size_t i = 0; i < key->array().size(); ++i)
64                array->Set(i, idbKeyToV8Value(key->array()[i].get(), isolate));
65            return array;
66        }
67    }
68
69    ASSERT_NOT_REACHED();
70    return v8Undefined();
71}
72
73static const size_t maximumDepth = 2000;
74
75static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack)
76{
77    if (value->IsNumber() && !std::isnan(value->NumberValue()))
78        return IDBKey::createNumber(value->NumberValue());
79    if (value->IsString())
80        return IDBKey::createString(toWebCoreString(value));
81    if (value->IsDate() && !std::isnan(value->NumberValue()))
82        return IDBKey::createDate(value->NumberValue());
83    if (value->IsArray()) {
84        v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
85
86        if (stack.contains(array))
87            return 0;
88        if (stack.size() >= maximumDepth)
89            return 0;
90        stack.append(array);
91
92        IDBKey::KeyArray subkeys;
93        uint32_t length = array->Length();
94        for (uint32_t i = 0; i < length; ++i) {
95            v8::Local<v8::Value> item = array->Get(v8::Int32::New(i));
96            RefPtr<IDBKey> subkey = createIDBKeyFromValue(item, stack);
97            if (!subkey)
98                subkeys.append(IDBKey::createInvalid());
99            else
100                subkeys.append(subkey);
101        }
102
103        stack.removeLast();
104        return IDBKey::createArray(subkeys);
105    }
106    return 0;
107}
108
109PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value)
110{
111    Vector<v8::Handle<v8::Array> > stack;
112    RefPtr<IDBKey> key = createIDBKeyFromValue(value, stack);
113    if (key)
114        return key;
115    return IDBKey::createInvalid();
116}
117
118template<typename T>
119static bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
120{
121    v8::Local<v8::Object> object = v8Value->ToObject();
122    if (!object->Has(indexOrName))
123        return false;
124    v8Value = object->Get(indexOrName);
125    return true;
126}
127
128template<typename T>
129static bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
130{
131    v8::Local<v8::Object> object = v8Object->ToObject();
132    return object->Set(indexOrName, v8Value);
133}
134
135static bool get(v8::Handle<v8::Value>& object, const String& keyPathElement, v8::Handle<v8::Value>& result, v8::Isolate* isolate)
136{
137    if (object->IsString() && keyPathElement == "length") {
138        int32_t length = v8::Handle<v8::String>::Cast(object)->Length();
139        result = v8::Number::New(length);
140        return true;
141    }
142    return object->IsObject() && getValueFrom(v8String(keyPathElement, isolate), result);
143}
144
145static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
146{
147    return object->IsObject();
148}
149
150static bool set(v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value, v8::Isolate* isolate)
151{
152    return canSet(object, keyPathElement) && setValue(object, v8String(keyPathElement, isolate), v8Value);
153}
154
155static v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
156{
157    v8::Handle<v8::Value> currentValue(rootValue);
158    ASSERT(index <= keyPathElements.size());
159    for (size_t i = 0; i < index; ++i) {
160        v8::Handle<v8::Value> parentValue(currentValue);
161        if (!get(parentValue, keyPathElements[i], currentValue, isolate))
162            return v8Undefined();
163    }
164
165    return currentValue;
166}
167
168static bool canInjectNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
169{
170    if (!rootValue->IsObject())
171        return false;
172
173    v8::Handle<v8::Value> currentValue(rootValue);
174
175    ASSERT(index <= keyPathElements.size());
176    for (size_t i = 0; i < index; ++i) {
177        v8::Handle<v8::Value> parentValue(currentValue);
178        const String& keyPathElement = keyPathElements[i];
179        if (!get(parentValue, keyPathElement, currentValue, isolate))
180            return canSet(parentValue, keyPathElement);
181    }
182    return true;
183}
184
185
186static v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate)
187{
188    v8::Handle<v8::Value> currentValue(rootValue);
189
190    ASSERT(index <= keyPathElements.size());
191    for (size_t i = 0; i < index; ++i) {
192        v8::Handle<v8::Value> parentValue(currentValue);
193        const String& keyPathElement = keyPathElements[i];
194        if (!get(parentValue, keyPathElement, currentValue, isolate)) {
195            v8::Handle<v8::Object> object = v8::Object::New();
196            if (!set(parentValue, keyPathElement, object, isolate))
197                return v8Undefined();
198            currentValue = object;
199        }
200    }
201
202    return currentValue;
203}
204
205static PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(const ScriptValue& value, const String& keyPath, v8::Isolate* isolate)
206{
207    Vector<String> keyPathElements;
208    IDBKeyPathParseError error;
209    IDBParseKeyPath(keyPath, keyPathElements, error);
210    ASSERT(error == IDBKeyPathParseErrorNone);
211    ASSERT(v8::Context::InContext());
212
213    v8::HandleScope handleScope(isolate);
214    v8::Handle<v8::Value> v8Value(value.v8Value());
215    v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size(), isolate));
216    if (v8Key.IsEmpty())
217        return 0;
218    return createIDBKeyFromValue(v8Key);
219}
220
221PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* state, const ScriptValue& value, const IDBKeyPath& keyPath)
222{
223    IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath");
224    ASSERT(!keyPath.isNull());
225    ASSERT(v8::Context::InContext());
226
227
228    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
229    v8::HandleScope handleScope(isolate);
230    if (keyPath.type() == IDBKeyPath::ArrayType) {
231        IDBKey::KeyArray result;
232        const Vector<String>& array = keyPath.array();
233        for (size_t i = 0; i < array.size(); ++i) {
234            RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(value, array[i], isolate);
235            if (!key)
236                return 0;
237            result.append(key);
238        }
239        return IDBKey::createArray(result);
240    }
241
242    ASSERT(keyPath.type() == IDBKeyPath::StringType);
243    return createIDBKeyFromScriptValueAndKeyPath(value, keyPath.string(), isolate);
244}
245
246ScriptValue deserializeIDBValue(DOMRequestState* state, PassRefPtr<SerializedScriptValue> prpValue)
247{
248    ASSERT(v8::Context::InContext());
249    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
250    v8::HandleScope handleScope(isolate);
251    RefPtr<SerializedScriptValue> serializedValue = prpValue;
252    if (serializedValue)
253        return ScriptValue(serializedValue->deserialize());
254    return ScriptValue(v8::Null());
255}
256
257ScriptValue deserializeIDBValueBuffer(DOMRequestState* state, PassRefPtr<SharedBuffer> prpBuffer)
258{
259    ASSERT(v8::Context::InContext());
260    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
261    v8::HandleScope handleScope(isolate);
262    RefPtr<SharedBuffer> buffer = prpBuffer;
263    if (buffer) {
264        // FIXME: The extra copy here can be eliminated by allowing SerializedScriptValue to take a raw const char* or const uint8_t*.
265        Vector<uint8_t> value;
266        value.append(buffer->data(), buffer->size());
267        RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(value);
268        return ScriptValue(serializedValue->deserialize());
269    }
270    return ScriptValue(v8::Null());
271}
272
273bool injectIDBKeyIntoScriptValue(DOMRequestState* state, PassRefPtr<IDBKey> key, ScriptValue& value, const IDBKeyPath& keyPath)
274{
275    IDB_TRACE("injectIDBKeyIntoScriptValue");
276    ASSERT(v8::Context::InContext());
277
278    ASSERT(keyPath.type() == IDBKeyPath::StringType);
279    Vector<String> keyPathElements;
280    IDBKeyPathParseError error;
281    IDBParseKeyPath(keyPath.string(), keyPathElements, error);
282    ASSERT(error == IDBKeyPathParseErrorNone);
283
284    if (!keyPathElements.size())
285        return 0;
286
287    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
288    v8::HandleScope handleScope(isolate);
289    v8::Handle<v8::Value> v8Value(value.v8Value());
290    v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1, isolate));
291    if (parent.IsEmpty())
292        return false;
293
294    if (!set(parent, keyPathElements.last(), idbKeyToV8Value(key.get(), isolate), isolate))
295        return false;
296
297    return true;
298}
299
300bool canInjectIDBKeyIntoScriptValue(DOMRequestState* state, const ScriptValue& scriptValue, const IDBKeyPath& keyPath)
301{
302    IDB_TRACE("canInjectIDBKeyIntoScriptValue");
303    ASSERT(keyPath.type() == IDBKeyPath::StringType);
304    Vector<String> keyPathElements;
305    IDBKeyPathParseError error;
306    IDBParseKeyPath(keyPath.string(), keyPathElements, error);
307    ASSERT(error == IDBKeyPathParseErrorNone);
308
309    if (!keyPathElements.size())
310        return false;
311
312    v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
313    return canInjectNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1, state->context()->GetIsolate());
314}
315
316ScriptValue idbKeyToScriptValue(DOMRequestState* state, PassRefPtr<IDBKey> key)
317{
318    ASSERT(v8::Context::InContext());
319    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
320    v8::HandleScope handleScope(isolate);
321    v8::Handle<v8::Value> v8Value(idbKeyToV8Value(key.get(), state->context()->GetIsolate()));
322    return ScriptValue(v8Value);
323}
324
325PassRefPtr<IDBKey> scriptValueToIDBKey(DOMRequestState* state, const ScriptValue& scriptValue)
326{
327    ASSERT(v8::Context::InContext());
328    v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent();
329    v8::HandleScope handleScope(isolate);
330    v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
331    return createIDBKeyFromValue(v8Value);
332}
333
334} // namespace WebCore
335