Chromium Code Reviews| 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 |
|
jsbell
2017/04/04 16:42:34
nit: truncated comment?
|
| +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. |
|
jsbell
2017/04/04 16:42:34
TODO(jbroman): Update when it is exported by v8.
|
| +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 |