| Index: third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp | 
| diff --git a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp | 
| index 9c23c90eb51dbfbc854a10b7428d6eebea9945c9..3fb2c598f644d0a6ab49c984f91921365459b1c1 100644 | 
| --- a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp | 
| +++ b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp | 
| @@ -25,13 +25,24 @@ | 
|  | 
| #include "bindings/modules/v8/V8BindingForModules.h" | 
|  | 
| +#include "bindings/core/v8/SerializationTag.h" | 
| +#include "bindings/core/v8/SerializedScriptValue.h" | 
| #include "bindings/core/v8/ToV8.h" | 
| #include "bindings/core/v8/V8Binding.h" | 
| #include "bindings/core/v8/V8BindingForTesting.h" | 
| #include "bindings/core/v8/V8PerIsolateData.h" | 
| #include "bindings/modules/v8/ToV8ForModules.h" | 
| +#include "modules/indexeddb/IDBAny.h" | 
| #include "modules/indexeddb/IDBKey.h" | 
| #include "modules/indexeddb/IDBKeyPath.h" | 
| +#include "modules/indexeddb/IDBValue.h" | 
| +#include "platform/SharedBuffer.h" | 
| +#include "public/platform/WebBlobInfo.h" | 
| +#include "public/platform/WebData.h" | 
| +#include "public/platform/WebString.h" | 
| +#include "public/platform/modules/indexeddb/WebIDBKey.h" | 
| +#include "public/platform/modules/indexeddb/WebIDBKeyPath.h" | 
| +#include "public/platform/modules/indexeddb/WebIDBValue.h" | 
| #include "testing/gtest/include/gtest/gtest.h" | 
|  | 
| namespace blink { | 
| @@ -116,6 +127,62 @@ void checkKeyPathNumberValue(v8::Isolate* isolate, | 
| ASSERT_TRUE(expected == idbKey->number()); | 
| } | 
|  | 
| +// SerializedScriptValue header format offsets are inferred from the Blink and | 
| +// V8 serialization code. The code below DCHECKs that | 
| +constexpr static size_t kSSVHeaderBlinkVersionOffset = 0; | 
| +constexpr static size_t kSSVHeaderBlinkVersionTagOffset = 1; | 
| +constexpr static size_t kSSVHeaderV8VersionOffset = 2; | 
| +constexpr static size_t kSSVHeaderV8VersionTagOffset = 3; | 
| + | 
| +// 13 is v8::internal::kLatestVersion in v8/src/value-serializer.cc at the | 
| +// time when this test was written. Unlike Blink, V8 does not currently export | 
| +// its serialization version, so this number might get stale. | 
| +constexpr static unsigned char kV8LatestKnownVersion = 13; | 
| + | 
| +// Follows the same steps as the IndexedDB value serialization code. | 
| +void serializeV8Value(v8::Local<v8::Value> value, | 
| +                      v8::Isolate* isolate, | 
| +                      Vector<char>* wireBytes) { | 
| +  NonThrowableExceptionState nonThrowableExceptionState; | 
| + | 
| +  SerializedScriptValue::SerializeOptions options; | 
| +  RefPtr<SerializedScriptValue> serializedValue = | 
| +      SerializedScriptValue::serialize(isolate, value, options, | 
| +                                       nonThrowableExceptionState); | 
| +  serializedValue->toWireBytes(*wireBytes); | 
| + | 
| +  // Sanity check that the serialization header has not changed, as the tests | 
| +  // that use this method rely on the header format. | 
| +  // | 
| +  // The cast from char* to unsigned char* is necessary to avoid VS2015 warning | 
| +  // C4309 (truncation of constant value). This happens because VersionTag is | 
| +  // 0xFF. | 
| +  const unsigned char* wireData = | 
| +      reinterpret_cast<unsigned char*>(wireBytes->data()); | 
| +  ASSERT_EQ( | 
| +      static_cast<unsigned char>(SerializedScriptValue::wireFormatVersion), | 
| +      wireData[kSSVHeaderBlinkVersionOffset]); | 
| +  ASSERT_EQ(static_cast<unsigned char>(VersionTag), | 
| +            wireData[kSSVHeaderBlinkVersionTagOffset]); | 
| + | 
| +  ASSERT_GE(static_cast<unsigned char>(kV8LatestKnownVersion), | 
| +            wireData[kSSVHeaderV8VersionOffset]); | 
| +  ASSERT_EQ(static_cast<unsigned char>(VersionTag), | 
| +            wireData[kSSVHeaderV8VersionTagOffset]); | 
| +} | 
| + | 
| +PassRefPtr<IDBValue> createIDBValue(v8::Isolate* isolate, | 
| +                                    Vector<char>& wireBytes, | 
| +                                    double primaryKey, | 
| +                                    const WebString& keyPath) { | 
| +  WebData webData(SharedBuffer::adoptVector(wireBytes)); | 
| +  Vector<WebBlobInfo> webBlobInfo; | 
| +  WebIDBKey webIdbKey = WebIDBKey::createNumber(primaryKey); | 
| +  WebIDBKeyPath webIdbKeyPath(keyPath); | 
| +  WebIDBValue webIdbValue(webData, webBlobInfo, webIdbKey, webIdbKeyPath); | 
| +  return IDBValue::create(webIdbValue, isolate); | 
| +} | 
| + | 
| TEST(IDBKeyFromValueAndKeyPathTest, TopLevelPropertyStringValue) { | 
| V8TestingScope scope; | 
| v8::Isolate* isolate = scope.isolate(); | 
| @@ -238,4 +305,89 @@ TEST(InjectIDBKeyTest, SubProperty) { | 
| scriptObject, "foo.xyz.foo"); | 
| } | 
|  | 
| +TEST(DeserializeIDBValueTest, CurrentVersions) { | 
| +  V8TestingScope scope; | 
| +  v8::Isolate* isolate = scope.isolate(); | 
| + | 
| +  Vector<char> objectBytes; | 
| +  v8::Local<v8::Object> emptyObject = v8::Object::New(isolate); | 
| +  serializeV8Value(emptyObject, isolate, &objectBytes); | 
| +  RefPtr<IDBValue> idbValue = createIDBValue(isolate, objectBytes, 42.0, "foo"); | 
| + | 
| +  v8::Local<v8::Value> v8Value = | 
| +      deserializeIDBValue(isolate, scope.context()->Global(), idbValue.get()); | 
| +  EXPECT_TRUE(!scope.getExceptionState().hadException()); | 
| + | 
| +  ASSERT_TRUE(v8Value->IsObject()); | 
| +  v8::Local<v8::Object> v8ValueObject = v8Value.As<v8::Object>(); | 
| +  v8::Local<v8::Value> v8NumberValue = | 
| +      v8ValueObject->Get(scope.context(), v8AtomicString(isolate, "foo")) | 
| +          .ToLocalChecked(); | 
| +  ASSERT_TRUE(v8NumberValue->IsNumber()); | 
| +  v8::Local<v8::Number> v8Number = v8NumberValue.As<v8::Number>(); | 
| +  EXPECT_EQ(v8Number->Value(), 42.0); | 
| +} | 
| + | 
| +TEST(DeserializeIDBValueTest, FutureV8Version) { | 
| +  V8TestingScope scope; | 
| +  v8::Isolate* isolate = scope.isolate(); | 
| + | 
| +  // Pretend that the object was serialized by a future version of V8. | 
| +  Vector<char> objectBytes; | 
| +  v8::Local<v8::Object> emptyObject = v8::Object::New(isolate); | 
| +  serializeV8Value(emptyObject, isolate, &objectBytes); | 
| +  objectBytes[kSSVHeaderV8VersionTagOffset] += 1; | 
| + | 
| +  // The call sequence below mimics IndexedDB's usage pattern when attempting to | 
| +  // read a value in an object store with a key generator and a key path, but | 
| +  // the serialized value uses a newer format version. | 
| +  // | 
| +  // http://crbug.com/703704 has a reproduction for this test's circumstances. | 
| +  RefPtr<IDBValue> idbValue = createIDBValue(isolate, objectBytes, 42.0, "foo"); | 
| + | 
| +  v8::Local<v8::Value> v8Value = | 
| +      deserializeIDBValue(isolate, scope.context()->Global(), idbValue.get()); | 
| +  EXPECT_TRUE(!scope.getExceptionState().hadException()); | 
| +  EXPECT_TRUE(v8Value->IsNull()); | 
| +} | 
| + | 
| +TEST(DeserializeIDBValueTest, InjectionIntoNonObject) { | 
| +  V8TestingScope scope; | 
| +  v8::Isolate* isolate = scope.isolate(); | 
| + | 
| +  // Simulate a storage corruption where an object is read back as a number. | 
| +  // This test uses a one-segment key path. | 
| +  Vector<char> objectBytes; | 
| +  v8::Local<v8::Number> number = v8::Number::New(isolate, 42.0); | 
| +  serializeV8Value(number, isolate, &objectBytes); | 
| +  RefPtr<IDBValue> idbValue = createIDBValue(isolate, objectBytes, 42.0, "foo"); | 
| + | 
| +  v8::Local<v8::Value> v8Value = | 
| +      deserializeIDBValue(isolate, scope.context()->Global(), idbValue.get()); | 
| +  EXPECT_TRUE(!scope.getExceptionState().hadException()); | 
| +  ASSERT_TRUE(v8Value->IsNumber()); | 
| +  v8::Local<v8::Number> v8Number = v8Value.As<v8::Number>(); | 
| +  EXPECT_EQ(v8Number->Value(), 42.0); | 
| +} | 
| + | 
| +TEST(DeserializeIDBValueTest, NestedInjectionIntoNonObject) { | 
| +  V8TestingScope scope; | 
| +  v8::Isolate* isolate = scope.isolate(); | 
| + | 
| +  // Simulate a storage corruption where an object is read back as a number. | 
| +  // This test uses a multiple-segment key path. | 
| +  Vector<char> objectBytes; | 
| +  v8::Local<v8::Number> number = v8::Number::New(isolate, 42.0); | 
| +  serializeV8Value(number, isolate, &objectBytes); | 
| +  RefPtr<IDBValue> idbValue = | 
| +      createIDBValue(isolate, objectBytes, 42.0, "foo.bar"); | 
| + | 
| +  v8::Local<v8::Value> v8Value = | 
| +      deserializeIDBValue(isolate, scope.context()->Global(), idbValue.get()); | 
| +  EXPECT_TRUE(!scope.getExceptionState().hadException()); | 
| +  ASSERT_TRUE(v8Value->IsNumber()); | 
| +  v8::Local<v8::Number> v8Number = v8Value.As<v8::Number>(); | 
| +  EXPECT_EQ(v8Number->Value(), 42.0); | 
| +} | 
| + | 
| }  // namespace blink | 
|  |