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