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