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/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 
     39 namespace WebCore {
     40 
     41 static 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 
     73 static const size_t maximumDepth = 2000;
     74 
     75 static 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 
    109 PassRefPtr<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 
    118 template<typename T>
    119 static 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 
    128 template<typename T>
    129 static 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 
    135 static 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 
    145 static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement)
    146 {
    147     return object->IsObject();
    148 }
    149 
    150 static 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 
    155 static 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 
    168 static 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 
    186 static 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 
    205 static 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 
    221 PassRefPtr<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 
    246 ScriptValue 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 
    257 ScriptValue 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 
    273 bool 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 
    300 bool 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 
    316 ScriptValue 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 
    325 PassRefPtr<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