| Index: third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
|
| diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4b0ab416d2bc0466fe1764194faf63271d727ecc
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/bindings/core/v8/serialization/V8ScriptValueSerializerTest.cpp
|
| @@ -0,0 +1,182 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "bindings/core/v8/serialization/V8ScriptValueSerializer.h"
|
| +
|
| +#include "bindings/core/v8/ExceptionStatePlaceholder.h"
|
| +#include "bindings/core/v8/ScriptController.h"
|
| +#include "bindings/core/v8/ScriptSourceCode.h"
|
| +#include "bindings/core/v8/V8BindingForTesting.h"
|
| +#include "bindings/core/v8/V8DOMException.h"
|
| +#include "bindings/core/v8/V8ImageData.h"
|
| +#include "bindings/core/v8/V8StringResource.h"
|
| +#include "bindings/core/v8/serialization/V8ScriptValueDeserializer.h"
|
| +#include "core/frame/LocalFrame.h"
|
| +#include "core/html/ImageData.h"
|
| +#include "platform/RuntimeEnabledFeatures.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace blink {
|
| +namespace {
|
| +
|
| +class ScopedEnableV8BasedStructuredClone {
|
| +public:
|
| + ScopedEnableV8BasedStructuredClone()
|
| + : m_wasEnabled(RuntimeEnabledFeatures::v8BasedStructuredCloneEnabled())
|
| + {
|
| + RuntimeEnabledFeatures::setV8BasedStructuredCloneEnabled(true);
|
| + }
|
| + ~ScopedEnableV8BasedStructuredClone()
|
| + {
|
| + RuntimeEnabledFeatures::setV8BasedStructuredCloneEnabled(m_wasEnabled);
|
| + }
|
| +private:
|
| + bool m_wasEnabled;
|
| +};
|
| +
|
| +RefPtr<SerializedScriptValue> serializedValue(const Vector<uint8_t>& bytes)
|
| +{
|
| + // TODO(jbroman): Fix this once SerializedScriptValue can take bytes without
|
| + // endianness swapping.
|
| + DCHECK_EQ(bytes.size() % 2, 0u);
|
| + return SerializedScriptValue::create(String(reinterpret_cast<const UChar*>(&bytes[0]), bytes.size() / 2));
|
| +}
|
| +
|
| +v8::Local<v8::Value> roundTrip(v8::Local<v8::Value> value, V8TestingScope& scope)
|
| +{
|
| + RefPtr<ScriptState> scriptState = scope.getScriptState();
|
| + ExceptionState& exceptionState = scope.getExceptionState();
|
| + RefPtr<SerializedScriptValue> serializedScriptValue =
|
| + V8ScriptValueSerializer(scriptState).serialize(value, nullptr, exceptionState);
|
| + DCHECK_EQ(!serializedScriptValue, exceptionState.hadException());
|
| + EXPECT_TRUE(serializedScriptValue);
|
| + if (!serializedScriptValue)
|
| + return v8::Local<v8::Value>();
|
| + return V8ScriptValueDeserializer(scriptState, serializedScriptValue).deserialize();
|
| +}
|
| +
|
| +v8::Local<v8::Value> eval(const String& source, V8TestingScope& scope)
|
| +{
|
| + return scope.frame().script().executeScriptInMainWorldAndReturnValue(source);
|
| +}
|
| +
|
| +String toJSON(v8::Local<v8::Object> object, const V8TestingScope& scope)
|
| +{
|
| + return v8StringToWebCoreString<String>(
|
| + v8::JSON::Stringify(scope.context(), object).ToLocalChecked(),
|
| + DoNotExternalize);
|
| +}
|
| +
|
| +// Checks for a DOM exception, including a rethrown one.
|
| +::testing::AssertionResult hadDOMException(const StringView& name, ScriptState* scriptState, ExceptionState& exceptionState)
|
| +{
|
| + if (!exceptionState.hadException())
|
| + return ::testing::AssertionFailure() << "no exception thrown";
|
| + DOMException* domException = V8DOMException::toImplWithTypeCheck(scriptState->isolate(), exceptionState.getException());
|
| + if (!domException)
|
| + return ::testing::AssertionFailure() << "exception thrown was not a DOMException";
|
| + if (domException->name() != name)
|
| + return ::testing::AssertionFailure() << "was " << domException->name();
|
| + return ::testing::AssertionSuccess();
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, RoundTripJSONLikeValue)
|
| +{
|
| + // Ensure that simple JavaScript objects work.
|
| + // There are more exhaustive tests of JavaScript objects in V8.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + v8::Local<v8::Value> object = eval("({ foo: [1, 2, 3], bar: 'baz' })", scope);
|
| + DCHECK(object->IsObject());
|
| + v8::Local<v8::Value> result = roundTrip(object, scope);
|
| + ASSERT_TRUE(result->IsObject());
|
| + EXPECT_NE(object, result);
|
| + EXPECT_EQ(toJSON(object.As<v8::Object>(), scope), toJSON(result.As<v8::Object>(), scope));
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, ThrowsDataCloneError)
|
| +{
|
| + // Ensure that a proper DataCloneError DOMException is thrown when issues
|
| + // are encountered in V8 (for example, cloning a symbol). It should be an
|
| + // instance of DOMException, and it should have a proper descriptive
|
| + // message.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + ScriptState* scriptState = scope.getScriptState();
|
| + ExceptionState exceptionState(scope.isolate(), ExceptionState::ExecutionContext, "Window", "postMessage");
|
| + v8::Local<v8::Value> symbol = eval("Symbol()", scope);
|
| + DCHECK(symbol->IsSymbol());
|
| + ASSERT_FALSE(V8ScriptValueSerializer(scriptState).serialize(symbol, nullptr, exceptionState));
|
| + ASSERT_TRUE(hadDOMException("DataCloneError", scriptState, exceptionState));
|
| + DOMException* domException = V8DOMException::toImpl(exceptionState.getException().As<v8::Object>());
|
| + EXPECT_TRUE(domException->toString().contains("postMessage"));
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, RethrowsScriptError)
|
| +{
|
| + // Ensure that other exceptions, like those thrown by script, are properly
|
| + // rethrown.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + ScriptState* scriptState = scope.getScriptState();
|
| + ExceptionState exceptionState(scope.isolate(), ExceptionState::ExecutionContext, "Window", "postMessage");
|
| + v8::Local<v8::Value> exception = eval("myException=new Error()", scope);
|
| + v8::Local<v8::Value> object = eval("({ get a() { throw myException; }})", scope);
|
| + DCHECK(object->IsObject());
|
| + ASSERT_FALSE(V8ScriptValueSerializer(scriptState).serialize(object, nullptr, exceptionState));
|
| + ASSERT_TRUE(exceptionState.hadException());
|
| + EXPECT_EQ(exception, exceptionState.getException());
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, DeserializationErrorReturnsNull)
|
| +{
|
| + // If there's a problem during deserialization, it results in null, but no
|
| + // exception.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + ScriptState* scriptState = scope.getScriptState();
|
| + RefPtr<SerializedScriptValue> invalid = SerializedScriptValue::create("invalid data");
|
| + v8::Local<v8::Value> result = V8ScriptValueDeserializer(scriptState, invalid).deserialize();
|
| + EXPECT_TRUE(result->IsNull());
|
| + EXPECT_FALSE(scope.getExceptionState().hadException());
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, RoundTripImageData)
|
| +{
|
| + // ImageData objects should serialize and deserialize correctly.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + ImageData* imageData = ImageData::create(2, 1, ASSERT_NO_EXCEPTION);
|
| + imageData->data()->data()[0] = 200;
|
| + v8::Local<v8::Value> wrapper = toV8(imageData, scope.context()->Global(), scope.isolate());
|
| + v8::Local<v8::Value> result = roundTrip(wrapper, scope);
|
| + ASSERT_TRUE(V8ImageData::hasInstance(result, scope.isolate()));
|
| + ImageData* newImageData = V8ImageData::toImpl(result.As<v8::Object>());
|
| + EXPECT_NE(imageData, newImageData);
|
| + EXPECT_EQ(imageData->size(), newImageData->size());
|
| + EXPECT_EQ(imageData->data()->length(), newImageData->data()->length());
|
| + EXPECT_EQ(200, newImageData->data()->data()[0]);
|
| +}
|
| +
|
| +TEST(V8ScriptValueSerializerTest, DecodeImageData)
|
| +{
|
| + // Backward compatibility with existing serialized ImageData objects must be
|
| + // maintained. Add more cases if the format changes; don't remove tests for
|
| + // old versions.
|
| + ScopedEnableV8BasedStructuredClone enable;
|
| + V8TestingScope scope;
|
| + ScriptState* scriptState = scope.getScriptState();
|
| + RefPtr<SerializedScriptValue> input = serializedValue({
|
| + 0xff, 0x09, 0x3f, 0x00, 0x23, 0x02, 0x01, 0x08,
|
| + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
| + v8::Local<v8::Value> result = V8ScriptValueDeserializer(scriptState, input).deserialize();
|
| + ASSERT_TRUE(V8ImageData::hasInstance(result, scope.isolate()));
|
| + ImageData* newImageData = V8ImageData::toImpl(result.As<v8::Object>());
|
| + EXPECT_EQ(IntSize(2, 1), newImageData->size());
|
| + EXPECT_EQ(8u, newImageData->data()->length());
|
| + EXPECT_EQ(200, newImageData->data()->data()[0]);
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace blink
|
|
|