ef23f29f6213707876f80c13e87461d273ff76fd
[WebKit-https.git] / Source / WebCore / bindings / js / IDBBindingUtilities.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Michael Pruett <michael@68k.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30 #include "IDBBindingUtilities.h"
31
32 #include "DOMRequestState.h"
33 #include "IDBIndexInfo.h"
34 #include "IDBIndexMetadata.h"
35 #include "IDBKey.h"
36 #include "IDBKeyData.h"
37 #include "IDBKeyPath.h"
38 #include "IndexKey.h"
39 #include "JSDOMBinding.h"
40 #include "Logging.h"
41 #include "SharedBuffer.h"
42 #include "ThreadSafeDataBuffer.h"
43
44 #include <runtime/DateInstance.h>
45 #include <runtime/ObjectConstructor.h>
46
47 using namespace JSC;
48
49 namespace WebCore {
50
51 static bool get(ExecState* exec, JSValue object, const String& keyPathElement, JSValue& result)
52 {
53     if (object.isString() && keyPathElement == "length") {
54         result = jsNumber(object.toString(exec)->length());
55         return true;
56     }
57     if (!object.isObject())
58         return false;
59     Identifier identifier = Identifier::fromString(&exec->vm(), keyPathElement.utf8().data());
60     if (!asObject(object)->hasProperty(exec, identifier))
61         return false;
62     result = asObject(object)->get(exec, identifier);
63     return true;
64 }
65
66 static bool canSet(JSValue object, const String& keyPathElement)
67 {
68     UNUSED_PARAM(keyPathElement);
69     return object.isObject();
70 }
71
72 static bool set(ExecState* exec, JSValue& object, const String& keyPathElement, JSValue jsValue)
73 {
74     if (!canSet(object, keyPathElement))
75         return false;
76     Identifier identifier = Identifier::fromString(&exec->vm(), keyPathElement.utf8().data());
77     asObject(object)->putDirect(exec->vm(), identifier, jsValue);
78     return true;
79 }
80
81 static JSValue idbKeyToJSValue(ExecState* exec, JSDOMGlobalObject* globalObject, IDBKey* key)
82 {
83     if (!key || !exec) {
84         // This should be undefined, not null.
85         // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange
86         return jsUndefined();
87     }
88
89     Locker<JSLock> locker(exec->vm().apiLock());
90
91     switch (key->type()) {
92     case KeyType::Array:
93         {
94             const Vector<RefPtr<IDBKey>>& inArray = key->array();
95             size_t size = inArray.size();
96             JSArray* outArray = constructEmptyArray(exec, 0, globalObject, size);
97             for (size_t i = 0; i < size; ++i) {
98                 IDBKey* arrayKey = inArray.at(i).get();
99                 outArray->putDirectIndex(exec, i, idbKeyToJSValue(exec, globalObject, arrayKey));
100             }
101             return JSValue(outArray);
102         }
103     case KeyType::String:
104         return jsStringWithCache(exec, key->string());
105     case KeyType::Date:
106         return jsDateOrNull(exec, key->date());
107     case KeyType::Number:
108         return jsNumber(key->number());
109     case KeyType::Min:
110     case KeyType::Max:
111     case KeyType::Invalid:
112         ASSERT_NOT_REACHED();
113         return jsUndefined();
114     }
115
116     ASSERT_NOT_REACHED();
117     return jsUndefined();
118 }
119
120 static const size_t maximumDepth = 2000;
121
122 static RefPtr<IDBKey> createIDBKeyFromValue(ExecState* exec, JSValue value, Vector<JSArray*>& stack)
123 {
124     if (value.isNumber() && !std::isnan(value.toNumber(exec)))
125         return IDBKey::createNumber(value.toNumber(exec));
126     if (value.isString())
127         return IDBKey::createString(value.toString(exec)->value(exec));
128     if (value.inherits(DateInstance::info()) && !std::isnan(valueToDate(exec, value)))
129         return IDBKey::createDate(valueToDate(exec, value));
130     if (value.isObject()) {
131         JSObject* object = asObject(value);
132         if (isJSArray(object) || object->inherits(JSArray::info())) {
133             JSArray* array = asArray(object);
134             size_t length = array->length();
135
136             if (stack.contains(array))
137                 return nullptr;
138             if (stack.size() >= maximumDepth)
139                 return nullptr;
140             stack.append(array);
141
142             Vector<RefPtr<IDBKey>> subkeys;
143             for (size_t i = 0; i < length; i++) {
144                 JSValue item = array->getIndex(exec, i);
145                 RefPtr<IDBKey> subkey = createIDBKeyFromValue(exec, item, stack);
146                 if (!subkey)
147                     subkeys.append(IDBKey::createInvalid());
148                 else
149                     subkeys.append(subkey);
150             }
151
152             stack.removeLast();
153             return IDBKey::createArray(subkeys);
154         }
155     }
156     return nullptr;
157 }
158
159 static RefPtr<IDBKey> createIDBKeyFromValue(ExecState* exec, JSValue value)
160 {
161     Vector<JSArray*> stack;
162     RefPtr<IDBKey> key = createIDBKeyFromValue(exec, value, stack);
163     if (key)
164         return key;
165     return IDBKey::createInvalid();
166 }
167
168 IDBKeyPath idbKeyPathFromValue(ExecState* exec, JSValue keyPathValue)
169 {
170     IDBKeyPath keyPath;
171     if (isJSArray(keyPathValue))
172         keyPath = IDBKeyPath(toNativeArray<String>(exec, keyPathValue));
173     else
174         keyPath = IDBKeyPath(keyPathValue.toString(exec)->value(exec));
175     return keyPath;
176 }
177
178 static JSValue getNthValueOnKeyPath(ExecState* exec, JSValue rootValue, const Vector<String>& keyPathElements, size_t index)
179 {
180     JSValue currentValue(rootValue);
181     ASSERT(index <= keyPathElements.size());
182     for (size_t i = 0; i < index; i++) {
183         JSValue parentValue(currentValue);
184         if (!get(exec, parentValue, keyPathElements[i], currentValue))
185             return jsUndefined();
186     }
187     return currentValue;
188 }
189
190 static RefPtr<IDBKey> internalCreateIDBKeyFromScriptValueAndKeyPath(ExecState* exec, const JSC::JSValue& value, const String& keyPath)
191 {
192     Vector<String> keyPathElements;
193     IDBKeyPathParseError error;
194     IDBParseKeyPath(keyPath, keyPathElements, error);
195     ASSERT(error == IDBKeyPathParseError::None);
196
197     JSValue jsValue = value;
198     jsValue = getNthValueOnKeyPath(exec, jsValue, keyPathElements, keyPathElements.size());
199     if (jsValue.isUndefined())
200         return nullptr;
201     return createIDBKeyFromValue(exec, jsValue);
202 }
203
204 static JSValue ensureNthValueOnKeyPath(ExecState* exec, JSValue rootValue, const Vector<String>& keyPathElements, size_t index)
205 {
206     JSValue currentValue(rootValue);
207
208     ASSERT(index <= keyPathElements.size());
209     for (size_t i = 0; i < index; i++) {
210         JSValue parentValue(currentValue);
211         const String& keyPathElement = keyPathElements[i];
212         if (!get(exec, parentValue, keyPathElement, currentValue)) {
213             JSObject* object = constructEmptyObject(exec);
214             if (!set(exec, parentValue, keyPathElement, JSValue(object)))
215                 return jsUndefined();
216             currentValue = JSValue(object);
217         }
218     }
219
220     return currentValue;
221 }
222
223 static bool canInjectNthValueOnKeyPath(ExecState* exec, JSValue rootValue, const Vector<String>& keyPathElements, size_t index)
224 {
225     if (!rootValue.isObject())
226         return false;
227
228     JSValue currentValue(rootValue);
229
230     ASSERT(index <= keyPathElements.size());
231     for (size_t i = 0; i < index; ++i) {
232         JSValue parentValue(currentValue);
233         const String& keyPathElement = keyPathElements[i];
234         if (!get(exec, parentValue, keyPathElement, currentValue))
235             return canSet(parentValue, keyPathElement);
236     }
237     return true;
238 }
239
240 bool injectIDBKeyIntoScriptValue(DOMRequestState* requestState, PassRefPtr<IDBKey> key, Deprecated::ScriptValue& value, const IDBKeyPath& keyPath)
241 {
242     LOG(StorageAPI, "injectIDBKeyIntoScriptValue");
243
244     ASSERT(keyPath.type() == IndexedDB::KeyPathType::String);
245
246     Vector<String> keyPathElements;
247     IDBKeyPathParseError error;
248     IDBParseKeyPath(keyPath.string(), keyPathElements, error);
249     ASSERT(error == IDBKeyPathParseError::None);
250
251     if (keyPathElements.isEmpty())
252         return false;
253
254     ExecState* exec = requestState->exec();
255
256     JSValue parent = ensureNthValueOnKeyPath(exec, value.jsValue(), keyPathElements, keyPathElements.size() - 1);
257     if (parent.isUndefined())
258         return false;
259
260     if (!set(exec, parent, keyPathElements.last(), idbKeyToJSValue(exec, jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), key.get())))
261         return false;
262
263     return true;
264 }
265
266 RefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(ExecState* exec, const Deprecated::ScriptValue& value, const IDBKeyPath& keyPath)
267 {
268     LOG(StorageAPI, "createIDBKeyFromScriptValueAndKeyPath");
269     ASSERT(!keyPath.isNull());
270
271     if (keyPath.type() == IndexedDB::KeyPathType::Array) {
272         Vector<RefPtr<IDBKey>> result;
273         const Vector<String>& array = keyPath.array();
274         for (size_t i = 0; i < array.size(); i++) {
275             RefPtr<IDBKey> key = internalCreateIDBKeyFromScriptValueAndKeyPath(exec, value, array[i]);
276             if (!key)
277                 return nullptr;
278             result.append(key);
279         }
280         return IDBKey::createArray(result);
281     }
282
283     ASSERT(keyPath.type() == IndexedDB::KeyPathType::String);
284     return internalCreateIDBKeyFromScriptValueAndKeyPath(exec, value, keyPath.string());
285 }
286
287 RefPtr<IDBKey> maybeCreateIDBKeyFromScriptValueAndKeyPath(ExecState& exec, const Deprecated::ScriptValue& value, const IDBKeyPath& keyPath)
288 {
289     ASSERT(!keyPath.isNull());
290
291     if (keyPath.type() == IndexedDB::KeyPathType::Array) {
292         Vector<RefPtr<IDBKey>> result;
293         const Vector<String>& array = keyPath.array();
294         for (size_t i = 0; i < array.size(); i++) {
295             RefPtr<IDBKey> key = internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, array[i]);
296             if (!key)
297                 return nullptr;
298             result.append(key);
299         }
300         return IDBKey::createArray(result);
301     }
302
303     ASSERT(keyPath.type() == IndexedDB::KeyPathType::String);
304     return internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, keyPath.string());
305 }
306
307 RefPtr<IDBKey> maybeCreateIDBKeyFromScriptValueAndKeyPath(ExecState& exec, const JSC::JSValue& value, const IDBKeyPath& keyPath)
308 {
309     ASSERT(!keyPath.isNull());
310
311     if (keyPath.type() == IndexedDB::KeyPathType::Array) {
312         const Vector<String>& array = keyPath.array();
313         Vector<RefPtr<IDBKey>> result;
314         result.reserveInitialCapacity(array.size());
315         for (auto& string : array) {
316             RefPtr<IDBKey> key = internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, string);
317             if (!key)
318                 return nullptr;
319             result.uncheckedAppend(WTF::move(key));
320         }
321         return IDBKey::createArray(WTF::move(result));
322     }
323
324     ASSERT(keyPath.type() == IndexedDB::KeyPathType::String);
325     return internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, keyPath.string());
326 }
327
328 bool canInjectIDBKeyIntoScriptValue(DOMRequestState* requestState, const JSC::JSValue& scriptValue, const IDBKeyPath& keyPath)
329 {
330     LOG(StorageAPI, "canInjectIDBKeyIntoScriptValue");
331
332     JSC::ExecState* exec = requestState->exec();
333     if (!exec)
334         return false;
335
336     return canInjectIDBKeyIntoScriptValue(*exec, scriptValue, keyPath);
337 }
338
339 bool canInjectIDBKeyIntoScriptValue(JSC::ExecState& execState, const JSC::JSValue& scriptValue, const IDBKeyPath& keyPath)
340 {
341     LOG(StorageAPI, "canInjectIDBKeyIntoScriptValue");
342
343     ASSERT(keyPath.type() == IndexedDB::KeyPathType::String);
344     Vector<String> keyPathElements;
345     IDBKeyPathParseError error;
346     IDBParseKeyPath(keyPath.string(), keyPathElements, error);
347     ASSERT(error == IDBKeyPathParseError::None);
348
349     if (!keyPathElements.size())
350         return false;
351
352     return canInjectNthValueOnKeyPath(&execState, scriptValue, keyPathElements, keyPathElements.size() - 1);
353 }
354
355 Deprecated::ScriptValue deserializeIDBValue(DOMRequestState* requestState, PassRefPtr<SerializedScriptValue> prpValue)
356 {
357     ExecState* exec = requestState->exec();
358     RefPtr<SerializedScriptValue> serializedValue = prpValue;
359     JSValue result;
360     if (serializedValue)
361         result = serializedValue->deserialize(exec, exec->lexicalGlobalObject(), 0);
362     else
363         result = jsNull();
364     return Deprecated::ScriptValue(exec->vm(), result);
365 }
366
367 Deprecated::ScriptValue deserializeIDBValueData(ScriptExecutionContext& context, const ThreadSafeDataBuffer& valueData)
368 {
369     DOMRequestState state(&context);
370     auto* execState = state.exec();
371
372     if (!execState)
373         return Deprecated::ScriptValue();
374
375     if (!valueData.data())
376         return Deprecated::ScriptValue(execState->vm(), jsUndefined());
377
378     const Vector<uint8_t>& data = *valueData.data();
379     JSValue result;
380     if (data.size()) {
381         RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(data);
382
383         execState->vm().apiLock().lock();
384         result = serializedValue->deserialize(execState, execState->lexicalGlobalObject(), 0, NonThrowing);
385         execState->vm().apiLock().unlock();
386     } else
387         result = jsNull();
388
389     return Deprecated::ScriptValue(execState->vm(), result);
390 }
391
392 Deprecated::ScriptValue deserializeIDBValueBuffer(DOMRequestState* requestState, PassRefPtr<SharedBuffer> prpBuffer, bool keyIsDefined)
393 {
394     if (prpBuffer) {
395         Vector<uint8_t> value;
396         value.append(prpBuffer->data(), prpBuffer->size());
397         return deserializeIDBValueBuffer(requestState->exec(), value, keyIsDefined);
398     }
399
400     return Deprecated::ScriptValue(requestState->exec()->vm(), jsNull());
401 }
402
403 static JSValue idbValueDataToJSValue(JSC::ExecState& exec, const Vector<uint8_t>& buffer)
404 {
405     if (buffer.isEmpty())
406         return jsNull();
407
408     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(buffer);
409     return serializedValue->deserialize(&exec, exec.lexicalGlobalObject(), 0, NonThrowing);
410 }
411
412 Deprecated::ScriptValue deserializeIDBValueBuffer(JSC::ExecState* exec, const Vector<uint8_t>& buffer, bool keyIsDefined)
413 {
414     ASSERT(exec);
415
416     // If the key doesn't exist, then the value must be undefined (as opposed to null).
417     if (!keyIsDefined) {
418         // We either shouldn't have a buffer or it should be of size 0.
419         ASSERT(!buffer.size());
420         return Deprecated::ScriptValue(exec->vm(), jsUndefined());
421     }
422
423     JSValue result = idbValueDataToJSValue(*exec, buffer);
424     return Deprecated::ScriptValue(exec->vm(), result);
425 }
426
427 JSValue idbValueDataToJSValue(JSC::ExecState& exec, const ThreadSafeDataBuffer& valueData)
428 {
429     if (!valueData.data())
430         return jsUndefined();
431
432     return idbValueDataToJSValue(exec, *valueData.data());
433 }
434
435 Deprecated::ScriptValue idbKeyToScriptValue(DOMRequestState* requestState, PassRefPtr<IDBKey> key)
436 {
437     ExecState* exec = requestState->exec();
438     return Deprecated::ScriptValue(exec->vm(), idbKeyToJSValue(exec, jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), key.get()));
439 }
440
441 RefPtr<IDBKey> scriptValueToIDBKey(DOMRequestState* requestState, const JSC::JSValue& scriptValue)
442 {
443     ExecState* exec = requestState->exec();
444     return createIDBKeyFromValue(exec, scriptValue);
445 }
446
447 RefPtr<IDBKey> scriptValueToIDBKey(ExecState& exec, const JSC::JSValue& scriptValue)
448 {
449     return createIDBKeyFromValue(&exec, scriptValue);
450 }
451
452 Deprecated::ScriptValue idbKeyDataToScriptValue(ScriptExecutionContext* context, const IDBKeyData& keyData)
453 {
454     RefPtr<IDBKey> key = keyData.maybeCreateIDBKey();
455     DOMRequestState requestState(context);
456     return idbKeyToScriptValue(&requestState, key.get());
457 }
458
459 void generateIndexKeysForValue(ExecState* exec, const IDBIndexMetadata& indexMetadata, const Deprecated::ScriptValue& objectValue, Vector<IDBKeyData>& indexKeys)
460 {
461     RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(exec, objectValue, indexMetadata.keyPath);
462
463     if (!indexKey)
464         return;
465
466     if (!indexMetadata.multiEntry || indexKey->type() != KeyType::Array) {
467         if (!indexKey->isValid())
468             return;
469
470         indexKeys.append(IDBKeyData(indexKey.get()));
471     } else {
472         ASSERT(indexMetadata.multiEntry);
473         ASSERT(indexKey->type() == KeyType::Array);
474         indexKey = IDBKey::createMultiEntryArray(indexKey->array());
475
476         if (!indexKey->isValid())
477             return;
478
479         for (auto& i : indexKey->array())
480             indexKeys.append(IDBKeyData(i.get()));
481     }
482 }
483
484 static Vector<IDBKeyData> createKeyPathArray(ExecState& exec, JSValue value, const IDBIndexInfo& info)
485 {
486     Vector<IDBKeyData> keys;
487
488     switch (info.keyPath().type()) {
489     case IndexedDB::KeyPathType::Array:
490         for (auto& entry : info.keyPath().array()) {
491             auto key = internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, entry);
492             if (!key)
493                 return { };
494             keys.append(key.get());
495         }
496         break;
497     case IndexedDB::KeyPathType::String: {
498         auto idbKey = internalCreateIDBKeyFromScriptValueAndKeyPath(&exec, value, info.keyPath().string());
499         if (!idbKey)
500             return { };
501
502         if (info.multiEntry() && idbKey->type() == IndexedDB::Array) {
503             for (auto& key : idbKey->array())
504                 keys.append(key.get());
505         } else
506             keys.append(idbKey.get());
507
508         break;
509     }
510     case IndexedDB::KeyPathType::Null:
511         RELEASE_ASSERT_NOT_REACHED();
512     }
513
514     return keys;
515 }
516
517 void generateIndexKeyForValue(ExecState& exec, const IDBIndexInfo& info, JSValue value, IndexKey& outKey)
518 {
519     auto keyDatas = createKeyPathArray(exec, value, info);
520
521     if (keyDatas.isEmpty())
522         return;
523
524     outKey = IndexKey(WTF::move(keyDatas));
525 }
526
527 } // namespace WebCore
528
529 #endif // ENABLE(INDEXED_DATABASE)