Home | History | Annotate | Download | only in v8
      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/modules/v8/IDBBindingUtilities.h"
     28 
     29 #include "bindings/core/v8/SerializedScriptValue.h"
     30 #include "bindings/core/v8/V8Binding.h"
     31 #include "bindings/core/v8/V8DOMStringList.h"
     32 #include "bindings/core/v8/V8HiddenValue.h"
     33 #include "bindings/core/v8/custom/V8ArrayBufferViewCustom.h"
     34 #include "bindings/core/v8/custom/V8Uint8ArrayCustom.h"
     35 #include "bindings/modules/v8/V8IDBCursor.h"
     36 #include "bindings/modules/v8/V8IDBCursorWithValue.h"
     37 #include "bindings/modules/v8/V8IDBDatabase.h"
     38 #include "bindings/modules/v8/V8IDBIndex.h"
     39 #include "bindings/modules/v8/V8IDBKeyRange.h"
     40 #include "bindings/modules/v8/V8IDBObjectStore.h"
     41 #include "bindings/modules/v8/V8IDBRequest.h"
     42 #include "bindings/modules/v8/V8IDBTransaction.h"
     43 #include "modules/indexeddb/IDBKey.h"
     44 #include "modules/indexeddb/IDBKeyPath.h"
     45 #include "modules/indexeddb/IDBKeyRange.h"
     46 #include "modules/indexeddb/IDBTracing.h"
     47 #include "platform/RuntimeEnabledFeatures.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"
     53 
     54 namespace blink {
     55 
     56 static v8::Handle<v8::Value> deserializeIDBValueBuffer(v8::Isolate*, SharedBuffer*, const Vector<blink::WebBlobInfo>*);
     57 
     58 static v8::Handle<v8::Value> toV8(const IDBKeyPath& value, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
     59 {
     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         RefPtrWillBeRawPtr<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);
     70     }
     71     ASSERT_NOT_REACHED();
     72     return v8::Undefined(isolate);
     73 }
     74 
     75 static v8::Handle<v8::Value> toV8(const IDBKey* key, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
     76 {
     77     if (!key) {
     78         // This should be undefined, not null.
     79         // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange
     80         return v8Undefined();
     81     }
     82 
     83     switch (key->type()) {
     84     case IDBKey::InvalidType:
     85     case IDBKey::MinType:
     86         ASSERT_NOT_REACHED();
     87         return v8Undefined();
     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:
     97         {
     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));
    101             return array;
    102         }
    103     }
    104 
    105     ASSERT_NOT_REACHED();
    106     return v8Undefined();
    107 }
    108 
    109 static v8::Handle<v8::Value> toV8(const IDBAny* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate)
    110 {
    111     if (!impl)
    112         return v8::Null(isolate);
    113 
    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         V8HiddenValue::setHiddenValue(isolate, cursor->ToObject(), V8HiddenValue::idbCursorRequest(isolate), request);
    127         return cursor;
    128     }
    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         V8HiddenValue::setHiddenValue(isolate, cursor->ToObject(), V8HiddenValue::idbCursorRequest(isolate), request);
    135         return cursor;
    136     }
    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(isolate, impl->buffer(), impl->blobInfo());
    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(isolate, impl->buffer(), impl->blobInfo());
    157         v8::Handle<v8::Value> key = toV8(impl->key(), creationContext, isolate);
    158         bool injected = injectV8KeyIntoV8Value(isolate, key, value, impl->keyPath());
    159         ASSERT_UNUSED(injected, injected);
    160         return value;
    161     }
    162     }
    163 
    164     ASSERT_NOT_REACHED();
    165     return v8::Undefined(isolate);
    166 }
    167 
    168 static const size_t maximumDepth = 2000;
    169 
    170 static IDBKey* createIDBKeyFromValue(v8::Isolate* isolate, v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack, bool allowExperimentalTypes = false)
    171 {
    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 = blink::V8ArrayBufferView::toImpl(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));
    185     }
    186     if (value->IsArray()) {
    187         v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
    188 
    189         if (stack.contains(array))
    190             return 0;
    191         if (stack.size() >= maximumDepth)
    192             return 0;
    193         stack.append(array);
    194 
    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             IDBKey* subkey = createIDBKeyFromValue(isolate, item, stack, allowExperimentalTypes);
    200             if (!subkey)
    201                 subkeys.append(IDBKey::createInvalid());
    202             else
    203                 subkeys.append(subkey);
    204         }
    205 
    206         stack.removeLast();
    207         return IDBKey::createArray(subkeys);
    208     }
    209     return 0;
    210 }
    211 
    212 static IDBKey* createIDBKeyFromValue(v8::Isolate* isolate, v8::Handle<v8::Value> value, bool allowExperimentalTypes = false)
    213 {
    214     Vector<v8::Handle<v8::Array> > stack;
    215     if (IDBKey* key = createIDBKeyFromValue(isolate, value, stack, allowExperimentalTypes))
    216         return key;
    217     return IDBKey::createInvalid();
    218 }
    219 
    220 template<typename T>
    221 static bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value)
    222 {
    223     v8::Local<v8::Object> object = v8Value->ToObject();
    224     if (!object->Has(indexOrName))
    225         return false;
    226     v8Value = object->Get(indexOrName);
    227     return true;
    228 }
    229 
    230 template<typename T>
    231 static bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value)
    232 {
    233     v8::Local<v8::Object> object = v8Object->ToObject();
    234     return object->Set(indexOrName, v8Value);
    235 }
    236 
    237 static bool get(v8::Isolate* isolate, v8::Handle<v8::Value>& object, const String& keyPathElement, v8::Handle<v8::Value>& result)
    238 {
    239     if (object->IsString() && keyPathElement == "length") {
    240         int32_t length = v8::Handle<v8::String>::Cast(object)->Length();
    241         result = v8::Number::New(isolate, length);
    242         return true;
    243     }
    244     return object->IsObject() && getValueFrom(v8String(isolate, keyPathElement), result);
    245 }
    246 
    247 static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
    248 {
    249     return object->IsObject();
    250 }
    251 
    252 static bool set(v8::Isolate* isolate, v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value)
    253 {
    254     return canSet(object, keyPathElement) && setValue(object, v8String(isolate, keyPathElement), v8Value);
    255 }
    256 
    257 static v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Isolate* isolate, v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index)
    258 {
    259     v8::Handle<v8::Value> currentValue(rootValue);
    260     ASSERT(index <= keyPathElements.size());
    261     for (size_t i = 0; i < index; ++i) {
    262         v8::Handle<v8::Value> parentValue(currentValue);
    263         if (!get(isolate, parentValue, keyPathElements[i], currentValue))
    264             return v8Undefined();
    265     }
    266 
    267     return currentValue;
    268 }
    269 
    270 static bool canInjectNthValueOnKeyPath(v8::Isolate* isolate, v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index)
    271 {
    272     if (!rootValue->IsObject())
    273         return false;
    274 
    275     v8::Handle<v8::Value> currentValue(rootValue);
    276 
    277     ASSERT(index <= keyPathElements.size());
    278     for (size_t i = 0; i < index; ++i) {
    279         v8::Handle<v8::Value> parentValue(currentValue);
    280         const String& keyPathElement = keyPathElements[i];
    281         if (!get(isolate, parentValue, keyPathElement, currentValue))
    282             return canSet(parentValue, keyPathElement);
    283     }
    284     return true;
    285 }
    286 
    287 
    288 static v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Isolate* isolate, v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index)
    289 {
    290     v8::Handle<v8::Value> currentValue(rootValue);
    291 
    292     ASSERT(index <= keyPathElements.size());
    293     for (size_t i = 0; i < index; ++i) {
    294         v8::Handle<v8::Value> parentValue(currentValue);
    295         const String& keyPathElement = keyPathElements[i];
    296         if (!get(isolate, parentValue, keyPathElement, currentValue)) {
    297             v8::Handle<v8::Object> object = v8::Object::New(isolate);
    298             if (!set(isolate, parentValue, keyPathElement, object))
    299                 return v8Undefined();
    300             currentValue = object;
    301         }
    302     }
    303 
    304     return currentValue;
    305 }
    306 
    307 static IDBKey* createIDBKeyFromScriptValueAndKeyPathInternal(v8::Isolate* isolate, const ScriptValue& value, const String& keyPath, bool allowExperimentalTypes)
    308 {
    309     Vector<String> keyPathElements;
    310     IDBKeyPathParseError error;
    311     IDBParseKeyPath(keyPath, keyPathElements, error);
    312     ASSERT(error == IDBKeyPathParseErrorNone);
    313     ASSERT(isolate->InContext());
    314 
    315     v8::HandleScope handleScope(isolate);
    316     v8::Handle<v8::Value> v8Value(value.v8Value());
    317     v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(isolate, v8Value, keyPathElements, keyPathElements.size()));
    318     if (v8Key.IsEmpty())
    319         return 0;
    320     return createIDBKeyFromValue(isolate, v8Key, allowExperimentalTypes);
    321 }
    322 
    323 static IDBKey* createIDBKeyFromScriptValueAndKeyPathInternal(v8::Isolate* isolate, const ScriptValue& value, const IDBKeyPath& keyPath, bool allowExperimentalTypes = false)
    324 {
    325     ASSERT(!keyPath.isNull());
    326     v8::HandleScope handleScope(isolate);
    327     if (keyPath.type() == IDBKeyPath::ArrayType) {
    328         IDBKey::KeyArray result;
    329         const Vector<String>& array = keyPath.array();
    330         for (size_t i = 0; i < array.size(); ++i) {
    331             IDBKey* key = createIDBKeyFromScriptValueAndKeyPathInternal(isolate, value, array[i], allowExperimentalTypes);
    332             if (!key)
    333                 return 0;
    334             result.append(key);
    335         }
    336         return IDBKey::createArray(result);
    337     }
    338 
    339     ASSERT(keyPath.type() == IDBKeyPath::StringType);
    340     return createIDBKeyFromScriptValueAndKeyPathInternal(isolate, value, keyPath.string(), allowExperimentalTypes);
    341 }
    342 
    343 IDBKey* createIDBKeyFromScriptValueAndKeyPath(v8::Isolate* isolate, const ScriptValue& value, const IDBKeyPath& keyPath)
    344 {
    345     IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath");
    346     return createIDBKeyFromScriptValueAndKeyPathInternal(isolate, value, keyPath);
    347 }
    348 
    349 static v8::Handle<v8::Value> deserializeIDBValueBuffer(v8::Isolate* isolate, SharedBuffer* buffer, const Vector<blink::WebBlobInfo>* blobInfo)
    350 {
    351     ASSERT(isolate->InContext());
    352     if (!buffer)
    353         return v8::Null(isolate);
    354 
    355     // FIXME: The extra copy here can be eliminated by allowing SerializedScriptValue to take a raw const char* or const uint8_t*.
    356     Vector<uint8_t> value;
    357     value.append(buffer->data(), buffer->size());
    358     RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(value);
    359     return serializedValue->deserialize(isolate, 0, blobInfo);
    360 }
    361 
    362 bool injectV8KeyIntoV8Value(v8::Isolate* isolate, v8::Handle<v8::Value> key, v8::Handle<v8::Value> value, const IDBKeyPath& keyPath)
    363 {
    364     IDB_TRACE("injectIDBV8KeyIntoV8Value");
    365     ASSERT(isolate->InContext());
    366 
    367     ASSERT(keyPath.type() == IDBKeyPath::StringType);
    368     Vector<String> keyPathElements;
    369     IDBKeyPathParseError error;
    370     IDBParseKeyPath(keyPath.string(), keyPathElements, error);
    371     ASSERT(error == IDBKeyPathParseErrorNone);
    372 
    373     if (!keyPathElements.size())
    374         return false;
    375 
    376     v8::HandleScope handleScope(isolate);
    377     v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(isolate, value, keyPathElements, keyPathElements.size() - 1));
    378     if (parent.IsEmpty())
    379         return false;
    380 
    381     if (!set(isolate, parent, keyPathElements.last(), key))
    382         return false;
    383 
    384     return true;
    385 }
    386 
    387 bool canInjectIDBKeyIntoScriptValue(v8::Isolate* isolate, const ScriptValue& scriptValue, const IDBKeyPath& keyPath)
    388 {
    389     IDB_TRACE("canInjectIDBKeyIntoScriptValue");
    390     ASSERT(keyPath.type() == IDBKeyPath::StringType);
    391     Vector<String> keyPathElements;
    392     IDBKeyPathParseError error;
    393     IDBParseKeyPath(keyPath.string(), keyPathElements, error);
    394     ASSERT(error == IDBKeyPathParseErrorNone);
    395 
    396     if (!keyPathElements.size())
    397         return false;
    398 
    399     v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
    400     return canInjectNthValueOnKeyPath(isolate, v8Value, keyPathElements, keyPathElements.size() - 1);
    401 }
    402 
    403 ScriptValue idbAnyToScriptValue(ScriptState* scriptState, IDBAny* any)
    404 {
    405     v8::Isolate* isolate = scriptState->isolate();
    406     v8::HandleScope handleScope(isolate);
    407     v8::Handle<v8::Value> v8Value(toV8(any, scriptState->context()->Global(), isolate));
    408     return ScriptValue(scriptState, v8Value);
    409 }
    410 
    411 ScriptValue idbKeyToScriptValue(ScriptState* scriptState, IDBKey* key)
    412 {
    413     v8::Isolate* isolate = scriptState->isolate();
    414     v8::HandleScope handleScope(isolate);
    415     v8::Handle<v8::Value> v8Value(toV8(key, scriptState->context()->Global(), isolate));
    416     return ScriptValue(scriptState, v8Value);
    417 }
    418 
    419 IDBKey* scriptValueToIDBKey(v8::Isolate* isolate, const ScriptValue& scriptValue)
    420 {
    421     ASSERT(isolate->InContext());
    422     v8::HandleScope handleScope(isolate);
    423     v8::Handle<v8::Value> v8Value(scriptValue.v8Value());
    424     return createIDBKeyFromValue(isolate, v8Value);
    425 }
    426 
    427 IDBKeyRange* scriptValueToIDBKeyRange(v8::Isolate* isolate, const ScriptValue& scriptValue)
    428 {
    429     v8::HandleScope handleScope(isolate);
    430     v8::Handle<v8::Value> value(scriptValue.v8Value());
    431     return V8IDBKeyRange::toImplWithTypeCheck(isolate, value);
    432 }
    433 
    434 ScriptValue deserializeScriptValue(ScriptState* scriptState, SerializedScriptValue* serializedValue, const Vector<blink::WebBlobInfo>* blobInfo)
    435 {
    436     v8::Isolate* isolate = scriptState->isolate();
    437     v8::HandleScope handleScope(isolate);
    438     if (serializedValue)
    439         return ScriptValue(scriptState, serializedValue->deserialize(isolate, 0, blobInfo));
    440     return ScriptValue(scriptState, v8::Null(isolate));
    441 }
    442 
    443 #if ENABLE(ASSERT)
    444 void assertPrimaryKeyValidOrInjectable(ScriptState* scriptState, PassRefPtr<SharedBuffer> buffer, const Vector<blink::WebBlobInfo>* blobInfo, IDBKey* key, const IDBKeyPath& keyPath)
    445 {
    446     ScriptState::Scope scope(scriptState);
    447     v8::Isolate* isolate = scriptState->isolate();
    448     ScriptValue keyValue = idbKeyToScriptValue(scriptState, key);
    449     ScriptValue scriptValue(scriptState, deserializeIDBValueBuffer(isolate, buffer.get(), blobInfo));
    450 
    451     // This assertion is about already persisted data, so allow experimental types.
    452     const bool allowExperimentalTypes = true;
    453     IDBKey* expectedKey = createIDBKeyFromScriptValueAndKeyPathInternal(isolate, scriptValue, keyPath, allowExperimentalTypes);
    454     ASSERT(!expectedKey || expectedKey->isEqual(key));
    455 
    456     bool injected = injectV8KeyIntoV8Value(isolate, keyValue.v8Value(), scriptValue.v8Value(), keyPath);
    457     ASSERT_UNUSED(injected, injected);
    458 }
    459 #endif
    460 
    461 } // namespace blink
    462