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 "V8DOMStringList.h" 30 #include "V8IDBCursor.h" 31 #include "V8IDBCursorWithValue.h" 32 #include "V8IDBDatabase.h" 33 #include "V8IDBIndex.h" 34 #include "V8IDBKeyRange.h" 35 #include "V8IDBObjectStore.h" 36 #include "V8IDBTransaction.h" 37 #include "bindings/v8/DOMRequestState.h" 38 #include "bindings/v8/SerializedScriptValue.h" 39 #include "bindings/v8/V8Binding.h" 40 #include "bindings/v8/custom/V8ArrayBufferViewCustom.h" 41 #include "bindings/v8/custom/V8Uint8ArrayCustom.h" 42 #include "modules/indexeddb/IDBKey.h" 43 #include "modules/indexeddb/IDBKeyPath.h" 44 #include "modules/indexeddb/IDBKeyRange.h" 45 #include "modules/indexeddb/IDBTracing.h" 46 #include "platform/SharedBuffer.h" 47 #include "wtf/ArrayBufferView.h" 48 #include "wtf/MathExtras.h" 49 #include "wtf/Uint8Array.h" 50 #include "wtf/Vector.h" 51 52 namespace WebCore { 53 54 v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer*, v8::Isolate*); 55 56 static v8::Handle<v8::Value> toV8(const IDBKeyPath& value, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) 57 { 58 switch (value.type()) { 59 case IDBKeyPath::NullType: 60 return v8::Null(isolate); 61 case IDBKeyPath::StringType: 62 return v8String(isolate, value.string()); 63 case IDBKeyPath::ArrayType: 64 RefPtr<DOMStringList> keyPaths = DOMStringList::create(); 65 for (Vector<String>::const_iterator it = value.array().begin(); it != value.array().end(); ++it) 66 keyPaths->append(*it); 67 return toV8(keyPaths.release(), creationContext, isolate); 68 } 69 ASSERT_NOT_REACHED(); 70 return v8::Undefined(isolate); 71 } 72 73 v8::Handle<v8::Value> toV8(const IDBKey* key, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) 74 { 75 if (!key) { 76 // This should be undefined, not null. 77 // Spec: http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBKeyRange 78 return v8Undefined(); 79 } 80 81 switch (key->type()) { 82 case IDBKey::InvalidType: 83 case IDBKey::MinType: 84 ASSERT_NOT_REACHED(); 85 return v8Undefined(); 86 case IDBKey::NumberType: 87 return v8::Number::New(isolate, key->number()); 88 case IDBKey::StringType: 89 return v8String(isolate, key->string()); 90 case IDBKey::BinaryType: 91 return toV8(Uint8Array::create(reinterpret_cast<const unsigned char*>(key->binary()->data()), key->binary()->size()), creationContext, isolate); 92 case IDBKey::DateType: 93 return v8::Date::New(isolate, key->date()); 94 case IDBKey::ArrayType: 95 { 96 v8::Local<v8::Array> array = v8::Array::New(isolate, key->array().size()); 97 for (size_t i = 0; i < key->array().size(); ++i) 98 array->Set(i, toV8(key->array()[i].get(), creationContext, isolate)); 99 return array; 100 } 101 } 102 103 ASSERT_NOT_REACHED(); 104 return v8Undefined(); 105 } 106 107 v8::Handle<v8::Value> toV8(const IDBAny* impl, v8::Handle<v8::Object> creationContext, v8::Isolate* isolate) 108 { 109 if (!impl) 110 return v8::Null(isolate); 111 112 switch (impl->type()) { 113 case IDBAny::UndefinedType: 114 return v8::Undefined(isolate); 115 case IDBAny::NullType: 116 return v8::Null(isolate); 117 case IDBAny::DOMStringListType: 118 return toV8(impl->domStringList(), creationContext, isolate); 119 case IDBAny::IDBCursorType: 120 return toV8(impl->idbCursor(), creationContext, isolate); 121 case IDBAny::IDBCursorWithValueType: 122 return toV8(impl->idbCursorWithValue(), creationContext, isolate); 123 case IDBAny::IDBDatabaseType: 124 return toV8(impl->idbDatabase(), creationContext, isolate); 125 case IDBAny::IDBIndexType: 126 return toV8(impl->idbIndex(), creationContext, isolate); 127 case IDBAny::IDBObjectStoreType: 128 return toV8(impl->idbObjectStore(), creationContext, isolate); 129 case IDBAny::IDBTransactionType: 130 return toV8(impl->idbTransaction(), creationContext, isolate); 131 case IDBAny::BufferType: 132 return deserializeIDBValueBuffer(impl->buffer(), isolate); 133 case IDBAny::StringType: 134 return v8String(isolate, impl->string()); 135 case IDBAny::IntegerType: 136 return v8::Number::New(isolate, impl->integer()); 137 case IDBAny::KeyType: 138 return toV8(impl->key(), creationContext, isolate); 139 case IDBAny::KeyPathType: 140 return toV8(impl->keyPath(), creationContext, isolate); 141 case IDBAny::BufferKeyAndKeyPathType: { 142 v8::Handle<v8::Value> value = deserializeIDBValueBuffer(impl->buffer(), isolate); 143 v8::Handle<v8::Value> key = toV8(impl->key(), creationContext, isolate); 144 bool injected = injectV8KeyIntoV8Value(key, value, impl->keyPath(), isolate); 145 ASSERT_UNUSED(injected, injected); 146 return value; 147 } 148 } 149 150 ASSERT_NOT_REACHED(); 151 return v8::Undefined(isolate); 152 } 153 154 static const size_t maximumDepth = 2000; 155 156 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, Vector<v8::Handle<v8::Array> >& stack, v8::Isolate* isolate) 157 { 158 if (value->IsNumber() && !std::isnan(value->NumberValue())) 159 return IDBKey::createNumber(value->NumberValue()); 160 if (value->IsString()) 161 return IDBKey::createString(toCoreString(value.As<v8::String>())); 162 if (value->IsDate() && !std::isnan(value->NumberValue())) 163 return IDBKey::createDate(value->NumberValue()); 164 if (value->IsArray()) { 165 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); 166 167 if (stack.contains(array)) 168 return 0; 169 if (stack.size() >= maximumDepth) 170 return 0; 171 stack.append(array); 172 173 IDBKey::KeyArray subkeys; 174 uint32_t length = array->Length(); 175 for (uint32_t i = 0; i < length; ++i) { 176 v8::Local<v8::Value> item = array->Get(v8::Int32::New(i, isolate)); 177 RefPtr<IDBKey> subkey = createIDBKeyFromValue(item, stack, isolate); 178 if (!subkey) 179 subkeys.append(IDBKey::createInvalid()); 180 else 181 subkeys.append(subkey); 182 } 183 184 stack.removeLast(); 185 return IDBKey::createArray(subkeys); 186 } 187 return 0; 188 } 189 190 static PassRefPtr<IDBKey> createIDBKeyFromValue(v8::Handle<v8::Value> value, v8::Isolate* isolate) 191 { 192 Vector<v8::Handle<v8::Array> > stack; 193 RefPtr<IDBKey> key = createIDBKeyFromValue(value, stack, isolate); 194 if (key) 195 return key; 196 return IDBKey::createInvalid(); 197 } 198 199 template<typename T> 200 static bool getValueFrom(T indexOrName, v8::Handle<v8::Value>& v8Value) 201 { 202 v8::Local<v8::Object> object = v8Value->ToObject(); 203 if (!object->Has(indexOrName)) 204 return false; 205 v8Value = object->Get(indexOrName); 206 return true; 207 } 208 209 template<typename T> 210 static bool setValue(v8::Handle<v8::Value>& v8Object, T indexOrName, const v8::Handle<v8::Value>& v8Value) 211 { 212 v8::Local<v8::Object> object = v8Object->ToObject(); 213 return object->Set(indexOrName, v8Value); 214 } 215 216 static bool get(v8::Handle<v8::Value>& object, const String& keyPathElement, v8::Handle<v8::Value>& result, v8::Isolate* isolate) 217 { 218 if (object->IsString() && keyPathElement == "length") { 219 int32_t length = v8::Handle<v8::String>::Cast(object)->Length(); 220 result = v8::Number::New(isolate, length); 221 return true; 222 } 223 return object->IsObject() && getValueFrom(v8String(isolate, keyPathElement), result); 224 } 225 226 static bool canSet(v8::Handle<v8::Value>& object, const String& keyPathElement) 227 { 228 return object->IsObject(); 229 } 230 231 static bool set(v8::Handle<v8::Value>& object, const String& keyPathElement, const v8::Handle<v8::Value>& v8Value, v8::Isolate* isolate) 232 { 233 return canSet(object, keyPathElement) && setValue(object, v8String(isolate, keyPathElement), v8Value); 234 } 235 236 static v8::Handle<v8::Value> getNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate) 237 { 238 v8::Handle<v8::Value> currentValue(rootValue); 239 ASSERT(index <= keyPathElements.size()); 240 for (size_t i = 0; i < index; ++i) { 241 v8::Handle<v8::Value> parentValue(currentValue); 242 if (!get(parentValue, keyPathElements[i], currentValue, isolate)) 243 return v8Undefined(); 244 } 245 246 return currentValue; 247 } 248 249 static bool canInjectNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate) 250 { 251 if (!rootValue->IsObject()) 252 return false; 253 254 v8::Handle<v8::Value> currentValue(rootValue); 255 256 ASSERT(index <= keyPathElements.size()); 257 for (size_t i = 0; i < index; ++i) { 258 v8::Handle<v8::Value> parentValue(currentValue); 259 const String& keyPathElement = keyPathElements[i]; 260 if (!get(parentValue, keyPathElement, currentValue, isolate)) 261 return canSet(parentValue, keyPathElement); 262 } 263 return true; 264 } 265 266 267 static v8::Handle<v8::Value> ensureNthValueOnKeyPath(v8::Handle<v8::Value>& rootValue, const Vector<String>& keyPathElements, size_t index, v8::Isolate* isolate) 268 { 269 v8::Handle<v8::Value> currentValue(rootValue); 270 271 ASSERT(index <= keyPathElements.size()); 272 for (size_t i = 0; i < index; ++i) { 273 v8::Handle<v8::Value> parentValue(currentValue); 274 const String& keyPathElement = keyPathElements[i]; 275 if (!get(parentValue, keyPathElement, currentValue, isolate)) { 276 v8::Handle<v8::Object> object = v8::Object::New(); 277 if (!set(parentValue, keyPathElement, object, isolate)) 278 return v8Undefined(); 279 currentValue = object; 280 } 281 } 282 283 return currentValue; 284 } 285 286 static PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(const ScriptValue& value, const String& keyPath, v8::Isolate* isolate) 287 { 288 Vector<String> keyPathElements; 289 IDBKeyPathParseError error; 290 IDBParseKeyPath(keyPath, keyPathElements, error); 291 ASSERT(error == IDBKeyPathParseErrorNone); 292 ASSERT(isolate->InContext()); 293 294 v8::HandleScope handleScope(isolate); 295 v8::Handle<v8::Value> v8Value(value.v8Value()); 296 v8::Handle<v8::Value> v8Key(getNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size(), isolate)); 297 if (v8Key.IsEmpty()) 298 return 0; 299 return createIDBKeyFromValue(v8Key, isolate); 300 } 301 302 PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* state, const ScriptValue& value, const IDBKeyPath& keyPath) 303 { 304 IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath"); 305 ASSERT(!keyPath.isNull()); 306 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 307 ASSERT(isolate->InContext()); 308 v8::HandleScope handleScope(isolate); 309 if (keyPath.type() == IDBKeyPath::ArrayType) { 310 IDBKey::KeyArray result; 311 const Vector<String>& array = keyPath.array(); 312 for (size_t i = 0; i < array.size(); ++i) { 313 RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(value, array[i], isolate); 314 if (!key) 315 return 0; 316 result.append(key); 317 } 318 return IDBKey::createArray(result); 319 } 320 321 ASSERT(keyPath.type() == IDBKeyPath::StringType); 322 return createIDBKeyFromScriptValueAndKeyPath(value, keyPath.string(), isolate); 323 } 324 325 v8::Handle<v8::Value> deserializeIDBValueBuffer(SharedBuffer* buffer, v8::Isolate* isolate) 326 { 327 ASSERT(isolate->InContext()); 328 if (!buffer) 329 return v8::Null(isolate); 330 331 // FIXME: The extra copy here can be eliminated by allowing SerializedScriptValue to take a raw const char* or const uint8_t*. 332 Vector<uint8_t> value; 333 value.append(buffer->data(), buffer->size()); 334 RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::createFromWireBytes(value); 335 return serializedValue->deserialize(isolate); 336 } 337 338 bool injectV8KeyIntoV8Value(v8::Handle<v8::Value> key, v8::Handle<v8::Value> value, const IDBKeyPath& keyPath, v8::Isolate* isolate) 339 { 340 IDB_TRACE("injectIDBV8KeyIntoV8Value"); 341 ASSERT(isolate->InContext()); 342 343 ASSERT(keyPath.type() == IDBKeyPath::StringType); 344 Vector<String> keyPathElements; 345 IDBKeyPathParseError error; 346 IDBParseKeyPath(keyPath.string(), keyPathElements, error); 347 ASSERT(error == IDBKeyPathParseErrorNone); 348 349 if (!keyPathElements.size()) 350 return false; 351 352 v8::HandleScope handleScope(isolate); 353 v8::Handle<v8::Value> parent(ensureNthValueOnKeyPath(value, keyPathElements, keyPathElements.size() - 1, isolate)); 354 if (parent.IsEmpty()) 355 return false; 356 357 if (!set(parent, keyPathElements.last(), key, isolate)) 358 return false; 359 360 return true; 361 } 362 363 bool canInjectIDBKeyIntoScriptValue(DOMRequestState* state, const ScriptValue& scriptValue, const IDBKeyPath& keyPath) 364 { 365 IDB_TRACE("canInjectIDBKeyIntoScriptValue"); 366 ASSERT(keyPath.type() == IDBKeyPath::StringType); 367 Vector<String> keyPathElements; 368 IDBKeyPathParseError error; 369 IDBParseKeyPath(keyPath.string(), keyPathElements, error); 370 ASSERT(error == IDBKeyPathParseErrorNone); 371 372 if (!keyPathElements.size()) 373 return false; 374 375 v8::Handle<v8::Value> v8Value(scriptValue.v8Value()); 376 return canInjectNthValueOnKeyPath(v8Value, keyPathElements, keyPathElements.size() - 1, state->context()->GetIsolate()); 377 } 378 379 ScriptValue idbAnyToScriptValue(DOMRequestState* state, PassRefPtr<IDBAny> any) 380 { 381 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 382 ASSERT(isolate->InContext()); 383 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext(); 384 v8::HandleScope handleScope(isolate); 385 v8::Handle<v8::Value> v8Value(toV8(any.get(), context->Global(), isolate)); 386 return ScriptValue(v8Value, isolate); 387 } 388 389 ScriptValue idbKeyToScriptValue(DOMRequestState* state, PassRefPtr<IDBKey> key) 390 { 391 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 392 ASSERT(isolate->InContext()); 393 v8::Local<v8::Context> context = state ? state->context() : isolate->GetCurrentContext(); 394 v8::HandleScope handleScope(isolate); 395 v8::Handle<v8::Value> v8Value(toV8(key.get(), context->Global(), isolate)); 396 return ScriptValue(v8Value, isolate); 397 } 398 399 PassRefPtr<IDBKey> scriptValueToIDBKey(DOMRequestState* state, const ScriptValue& scriptValue) 400 { 401 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 402 ASSERT(isolate->InContext()); 403 v8::HandleScope handleScope(isolate); 404 v8::Handle<v8::Value> v8Value(scriptValue.v8Value()); 405 return createIDBKeyFromValue(v8Value, isolate); 406 } 407 408 PassRefPtr<IDBKeyRange> scriptValueToIDBKeyRange(DOMRequestState* state, const ScriptValue& scriptValue) 409 { 410 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 411 v8::HandleScope handleScope(isolate); 412 v8::Handle<v8::Value> value(scriptValue.v8Value()); 413 if (V8IDBKeyRange::hasInstance(value, isolate, worldType(isolate))) 414 return V8IDBKeyRange::toNative(value.As<v8::Object>()); 415 return 0; 416 } 417 418 #ifndef NDEBUG 419 void assertPrimaryKeyValidOrInjectable(DOMRequestState* state, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> prpKey, const IDBKeyPath& keyPath) 420 { 421 RefPtr<IDBKey> key(prpKey); 422 423 DOMRequestState::Scope scope(*state); 424 v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); 425 426 ScriptValue keyValue = idbKeyToScriptValue(state, key); 427 ScriptValue scriptValue(deserializeIDBValueBuffer(buffer.get(), isolate), isolate); 428 429 RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(state, scriptValue, keyPath); 430 ASSERT(!expectedKey || expectedKey->isEqual(key.get())); 431 432 bool injected = injectV8KeyIntoV8Value(keyValue.v8Value(), scriptValue.v8Value(), keyPath, isolate); 433 ASSERT_UNUSED(injected, injected); 434 } 435 #endif 436 437 } // namespace WebCore 438