blob: b1fc03626fa9a32a2fd4c4c8db6bcf037a8f77e5 [file] [log] [blame]
// 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 "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_blob_info.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_deserializer.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_blob.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_matrix.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_matrix_read_only.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_init.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_read_only.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_quad.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_file.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_file_list.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_image_data.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_message_port.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_mojo_handle.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_offscreen_canvas.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_readable_stream.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_string_resource.h"
#include "third_party/blink/renderer/core/fileapi/blob.h"
#include "third_party/blink/renderer/core/fileapi/file.h"
#include "third_party/blink/renderer/core/fileapi/file_list.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/geometry/dom_matrix.h"
#include "third_party/blink/renderer/core/geometry/dom_matrix_read_only.h"
#include "third_party/blink/renderer/core/geometry/dom_point.h"
#include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
#include "third_party/blink/renderer/core/geometry/dom_quad.h"
#include "third_party/blink/renderer/core/geometry/dom_rect.h"
#include "third_party/blink/renderer/core/geometry/dom_rect_read_only.h"
#include "third_party/blink/renderer/core/html/canvas/image_data.h"
#include "third_party/blink/renderer/core/messaging/message_port.h"
#include "third_party/blink/renderer/core/mojo/mojo_handle.h"
#include "third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h"
#include "third_party/blink/renderer/core/streams/readable_stream.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/file_metadata.h"
#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/date_math.h"
#include "third_party/blink/renderer/platform/wtf/time.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace blink {
namespace {
v8::Local<v8::Value> RoundTrip(
v8::Local<v8::Value> value,
V8TestingScope& scope,
ExceptionState* override_exception_state = nullptr,
Transferables* transferables = nullptr,
WebBlobInfoArray* blob_info = nullptr) {
ScriptState* script_state = scope.GetScriptState();
ExceptionState& exception_state = override_exception_state
? *override_exception_state
: scope.GetExceptionState();
// Extract message ports and disentangle them.
Vector<MessagePortChannel> channels;
if (transferables) {
channels = MessagePort::DisentanglePorts(scope.GetExecutionContext(),
transferables->message_ports,
exception_state);
if (exception_state.HadException())
return v8::Local<v8::Value>();
}
V8ScriptValueSerializer::Options serialize_options;
serialize_options.transferables = transferables;
serialize_options.blob_info = blob_info;
V8ScriptValueSerializer serializer(script_state, serialize_options);
scoped_refptr<SerializedScriptValue> serialized_script_value =
serializer.Serialize(value, exception_state);
DCHECK_EQ(!serialized_script_value, exception_state.HadException());
if (!serialized_script_value)
return v8::Local<v8::Value>();
// If there are message ports, make new ones and entangle them.
MessagePortArray* transferred_message_ports = MessagePort::EntanglePorts(
*scope.GetExecutionContext(), std::move(channels));
UnpackedSerializedScriptValue* unpacked =
SerializedScriptValue::Unpack(std::move(serialized_script_value));
V8ScriptValueDeserializer::Options deserialize_options;
deserialize_options.message_ports = transferred_message_ports;
deserialize_options.blob_info = blob_info;
V8ScriptValueDeserializer deserializer(script_state, unpacked,
deserialize_options);
return deserializer.Deserialize();
}
v8::Local<v8::Value> Eval(const String& source, V8TestingScope& scope) {
return scope.GetFrame()
.GetScriptController()
.ExecuteScriptInMainWorldAndReturnValue(source, KURL(),
SanitizeScriptErrors::kSanitize);
}
String ToJSON(v8::Local<v8::Object> object, const V8TestingScope& scope) {
return ToBlinkString<String>(
v8::JSON::Stringify(scope.GetContext(), object).ToLocalChecked(),
kDoNotExternalize);
}
} // namespace
scoped_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));
}
// Checks for a DOM exception, including a rethrown one.
testing::AssertionResult HadDOMExceptionInCoreTest(
const StringView& name,
ScriptState* script_state,
ExceptionState& exception_state) {
if (!exception_state.HadException())
return testing::AssertionFailure() << "no exception thrown";
DOMException* dom_exception = V8DOMException::ToImplWithTypeCheck(
script_state->GetIsolate(), exception_state.GetException());
if (!dom_exception)
return testing::AssertionFailure()
<< "exception thrown was not a DOMException";
if (dom_exception->name() != name)
return testing::AssertionFailure() << "was " << dom_exception->name();
return testing::AssertionSuccess();
}
namespace {
TEST(V8ScriptValueSerializerTest, RoundTripJSONLikeValue) {
// Ensure that simple JavaScript objects work.
// There are more exhaustive tests of JavaScript objects in V8.
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.
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "Window",
"postMessage");
v8::Local<v8::Value> symbol = Eval("Symbol()", scope);
DCHECK(symbol->IsSymbol());
ASSERT_FALSE(
V8ScriptValueSerializer(script_state).Serialize(symbol, exception_state));
ASSERT_TRUE(HadDOMExceptionInCoreTest("DataCloneError", script_state,
exception_state));
DOMException* dom_exception =
V8DOMException::ToImpl(exception_state.GetException().As<v8::Object>());
EXPECT_TRUE(dom_exception->message().Contains("postMessage"));
}
TEST(V8ScriptValueSerializerTest, RethrowsScriptError) {
// Ensure that other exceptions, like those thrown by script, are properly
// rethrown.
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "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(script_state).Serialize(object, exception_state));
ASSERT_TRUE(exception_state.HadException());
EXPECT_EQ(exception, exception_state.GetException());
}
TEST(V8ScriptValueSerializerTest, DeserializationErrorReturnsNull) {
// If there's a problem during deserialization, it results in null, but no
// exception.
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> invalid =
SerializedScriptValue::Create("invalid data");
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, invalid).Deserialize();
EXPECT_TRUE(result->IsNull());
EXPECT_FALSE(scope.GetExceptionState().HadException());
}
TEST(V8ScriptValueSerializerTest, NeuteringHappensAfterSerialization) {
// This object will throw an exception before the [[Transfer]] step.
// As a result, the ArrayBuffer will not be transferred.
V8TestingScope scope;
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "Window",
"postMessage");
DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(1, 1);
ASSERT_FALSE(array_buffer->IsNeutered());
v8::Local<v8::Value> object = Eval("({ get a() { throw 'party'; }})", scope);
Transferables transferables;
transferables.array_buffers.push_back(array_buffer);
RoundTrip(object, scope, &exception_state, &transferables);
ASSERT_TRUE(exception_state.HadException());
EXPECT_FALSE(HadDOMExceptionInCoreTest(
"DataCloneError", scope.GetScriptState(), exception_state));
EXPECT_FALSE(array_buffer->IsNeutered());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMPoint) {
// DOMPoint objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMPoint* point = DOMPoint::Create(1, 2, 3, 4);
v8::Local<v8::Value> wrapper =
ToV8(point, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMPoint::hasInstance(result, scope.GetIsolate()));
DOMPoint* new_point = V8DOMPoint::ToImpl(result.As<v8::Object>());
EXPECT_NE(point, new_point);
EXPECT_EQ(point->x(), new_point->x());
EXPECT_EQ(point->y(), new_point->y());
EXPECT_EQ(point->z(), new_point->z());
EXPECT_EQ(point->w(), new_point->w());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMPoint) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x11, 0xff, 0x0d, 0x5c, 'Q', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMPoint::hasInstance(result, scope.GetIsolate()));
DOMPoint* point = V8DOMPoint::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1, point->x());
EXPECT_EQ(2, point->y());
EXPECT_EQ(3, point->z());
EXPECT_EQ(4, point->w());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMPointReadOnly) {
// DOMPointReadOnly objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMPointReadOnly* point = DOMPointReadOnly::Create(1, 2, 3, 4);
v8::Local<v8::Value> wrapper =
ToV8(point, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMPointReadOnly::hasInstance(result, scope.GetIsolate()));
EXPECT_FALSE(V8DOMPoint::hasInstance(result, scope.GetIsolate()));
DOMPointReadOnly* new_point =
V8DOMPointReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_NE(point, new_point);
EXPECT_EQ(point->x(), new_point->x());
EXPECT_EQ(point->y(), new_point->y());
EXPECT_EQ(point->z(), new_point->z());
EXPECT_EQ(point->w(), new_point->w());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMPointReadOnly) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x11, 0xff, 0x0d, 0x5c, 'W', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMPointReadOnly::hasInstance(result, scope.GetIsolate()));
DOMPointReadOnly* point = V8DOMPointReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1, point->x());
EXPECT_EQ(2, point->y());
EXPECT_EQ(3, point->z());
EXPECT_EQ(4, point->w());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMRect) {
// DOMRect objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMRect* rect = DOMRect::Create(1, 2, 3, 4);
v8::Local<v8::Value> wrapper =
ToV8(rect, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMRect::hasInstance(result, scope.GetIsolate()));
DOMRect* new_rect = V8DOMRect::ToImpl(result.As<v8::Object>());
EXPECT_NE(rect, new_rect);
EXPECT_EQ(rect->x(), new_rect->x());
EXPECT_EQ(rect->y(), new_rect->y());
EXPECT_EQ(rect->width(), new_rect->width());
EXPECT_EQ(rect->height(), new_rect->height());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMRect) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x11, 0xff, 0x0d, 0x5c, 'E', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMRect::hasInstance(result, scope.GetIsolate()));
DOMRect* rect = V8DOMRect::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1, rect->x());
EXPECT_EQ(2, rect->y());
EXPECT_EQ(3, rect->width());
EXPECT_EQ(4, rect->height());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMRectReadOnly) {
// DOMRectReadOnly objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMRectReadOnly* rect = DOMRectReadOnly::Create(1, 2, 3, 4);
v8::Local<v8::Value> wrapper =
ToV8(rect, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMRectReadOnly::hasInstance(result, scope.GetIsolate()));
EXPECT_FALSE(V8DOMRect::hasInstance(result, scope.GetIsolate()));
DOMRectReadOnly* new_rect =
V8DOMRectReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_NE(rect, new_rect);
EXPECT_EQ(rect->x(), new_rect->x());
EXPECT_EQ(rect->y(), new_rect->y());
EXPECT_EQ(rect->width(), new_rect->width());
EXPECT_EQ(rect->height(), new_rect->height());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMRectReadOnly) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x11, 0xff, 0x0d, 0x5c, 'R', 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMRectReadOnly::hasInstance(result, scope.GetIsolate()));
DOMRectReadOnly* rect = V8DOMRectReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1, rect->x());
EXPECT_EQ(2, rect->y());
EXPECT_EQ(3, rect->width());
EXPECT_EQ(4, rect->height());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMQuad) {
// DOMQuad objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMPointInit* pi1 = DOMPointInit::Create();
pi1->setX(1);
pi1->setY(5);
pi1->setZ(9);
pi1->setW(13);
DOMPointInit* pi2 = DOMPointInit::Create();
pi2->setX(2);
pi2->setY(6);
pi2->setZ(10);
pi2->setW(14);
DOMPointInit* pi3 = DOMPointInit::Create();
pi3->setX(3);
pi3->setY(7);
pi3->setZ(11);
pi3->setW(15);
DOMPointInit* pi4 = DOMPointInit::Create();
pi4->setX(4);
pi4->setY(8);
pi4->setZ(12);
pi4->setW(16);
DOMQuad* quad = DOMQuad::Create(pi1, pi2, pi3, pi4);
v8::Local<v8::Value> wrapper =
ToV8(quad, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMQuad::hasInstance(result, scope.GetIsolate()));
DOMQuad* new_quad = V8DOMQuad::ToImpl(result.As<v8::Object>());
EXPECT_NE(quad, new_quad);
EXPECT_NE(quad->p1(), new_quad->p1());
EXPECT_NE(quad->p2(), new_quad->p2());
EXPECT_NE(quad->p3(), new_quad->p3());
EXPECT_NE(quad->p4(), new_quad->p4());
EXPECT_EQ(quad->p1()->x(), new_quad->p1()->x());
EXPECT_EQ(quad->p1()->y(), new_quad->p1()->y());
EXPECT_EQ(quad->p1()->z(), new_quad->p1()->z());
EXPECT_EQ(quad->p1()->w(), new_quad->p1()->w());
EXPECT_EQ(quad->p2()->x(), new_quad->p2()->x());
EXPECT_EQ(quad->p2()->y(), new_quad->p2()->y());
EXPECT_EQ(quad->p2()->z(), new_quad->p2()->z());
EXPECT_EQ(quad->p2()->w(), new_quad->p2()->w());
EXPECT_EQ(quad->p3()->x(), new_quad->p3()->x());
EXPECT_EQ(quad->p3()->y(), new_quad->p3()->y());
EXPECT_EQ(quad->p3()->z(), new_quad->p3()->z());
EXPECT_EQ(quad->p3()->w(), new_quad->p3()->w());
EXPECT_EQ(quad->p4()->x(), new_quad->p4()->x());
EXPECT_EQ(quad->p4()->y(), new_quad->p4()->y());
EXPECT_EQ(quad->p4()->z(), new_quad->p4()->z());
EXPECT_EQ(quad->p4()->w(), new_quad->p4()->w());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMQuad) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x11, 0xff, 0x0d, 0x5c, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x22, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2e, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x40});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMQuad::hasInstance(result, scope.GetIsolate()));
DOMQuad* quad = V8DOMQuad::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1, quad->p1()->x());
EXPECT_EQ(5, quad->p1()->y());
EXPECT_EQ(9, quad->p1()->z());
EXPECT_EQ(13, quad->p1()->w());
EXPECT_EQ(2, quad->p2()->x());
EXPECT_EQ(6, quad->p2()->y());
EXPECT_EQ(10, quad->p2()->z());
EXPECT_EQ(14, quad->p2()->w());
EXPECT_EQ(3, quad->p3()->x());
EXPECT_EQ(7, quad->p3()->y());
EXPECT_EQ(11, quad->p3()->z());
EXPECT_EQ(15, quad->p3()->w());
EXPECT_EQ(4, quad->p4()->x());
EXPECT_EQ(8, quad->p4()->y());
EXPECT_EQ(12, quad->p4()->z());
EXPECT_EQ(16, quad->p4()->w());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMMatrix2D) {
// DOMMatrix objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMMatrixInit* init = DOMMatrixInit::Create();
init->setIs2D(true);
init->setA(1.0);
init->setB(2.0);
init->setC(3.0);
init->setD(4.0);
init->setE(5.0);
init->setF(6.0);
DOMMatrix* matrix = DOMMatrix::fromMatrix(init, scope.GetExceptionState());
EXPECT_TRUE(matrix->is2D());
v8::Local<v8::Value> wrapper =
ToV8(matrix, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrix* new_matrix = V8DOMMatrix::ToImpl(result.As<v8::Object>());
EXPECT_NE(matrix, new_matrix);
EXPECT_TRUE(new_matrix->is2D());
EXPECT_EQ(matrix->a(), new_matrix->a());
EXPECT_EQ(matrix->b(), new_matrix->b());
EXPECT_EQ(matrix->c(), new_matrix->c());
EXPECT_EQ(matrix->d(), new_matrix->d());
EXPECT_EQ(matrix->e(), new_matrix->e());
EXPECT_EQ(matrix->f(), new_matrix->f());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMMatrix2D) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue({
0xff, 0x11, 0xff, 0x0d, 0x5c, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0xff, 0x11, 0xff, 0x0d, 0x5c, 0x49,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40,
});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrix* matrix = V8DOMMatrix::ToImpl(result.As<v8::Object>());
EXPECT_TRUE(matrix->is2D());
EXPECT_EQ(1.0, matrix->a());
EXPECT_EQ(2.0, matrix->b());
EXPECT_EQ(3.0, matrix->c());
EXPECT_EQ(4.0, matrix->d());
EXPECT_EQ(5.0, matrix->e());
EXPECT_EQ(6.0, matrix->f());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMMatrixReadOnly2D) {
// DOMMatrix objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMMatrixInit* init = DOMMatrixInit::Create();
init->setIs2D(true);
init->setA(1.0);
init->setB(2.0);
init->setC(3.0);
init->setD(4.0);
init->setE(5.0);
init->setF(6.0);
DOMMatrixReadOnly* matrix =
DOMMatrixReadOnly::fromMatrix(init, scope.GetExceptionState());
EXPECT_TRUE(matrix->is2D());
v8::Local<v8::Value> wrapper =
ToV8(matrix, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMMatrixReadOnly::hasInstance(result, scope.GetIsolate()));
EXPECT_FALSE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrixReadOnly* new_matrix =
V8DOMMatrixReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_NE(matrix, new_matrix);
EXPECT_TRUE(new_matrix->is2D());
EXPECT_EQ(matrix->a(), new_matrix->a());
EXPECT_EQ(matrix->b(), new_matrix->b());
EXPECT_EQ(matrix->c(), new_matrix->c());
EXPECT_EQ(matrix->d(), new_matrix->d());
EXPECT_EQ(matrix->e(), new_matrix->e());
EXPECT_EQ(matrix->f(), new_matrix->f());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMMatrixReadOnly2D) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue({
0xff, 0x11, 0xff, 0x0d, 0x5c, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x18, 0x40, 0xff, 0x11, 0xff, 0x0d, 0x5c, 0x49,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x14, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x40,
});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMMatrixReadOnly::hasInstance(result, scope.GetIsolate()));
DOMMatrixReadOnly* matrix =
V8DOMMatrixReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_TRUE(matrix->is2D());
EXPECT_EQ(1.0, matrix->a());
EXPECT_EQ(2.0, matrix->b());
EXPECT_EQ(3.0, matrix->c());
EXPECT_EQ(4.0, matrix->d());
EXPECT_EQ(5.0, matrix->e());
EXPECT_EQ(6.0, matrix->f());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMMatrix) {
// DOMMatrix objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMMatrixInit* init = DOMMatrixInit::Create();
init->setIs2D(false);
init->setM11(1.1);
init->setM12(1.2);
init->setM13(1.3);
init->setM14(1.4);
init->setM21(2.1);
init->setM22(2.2);
init->setM23(2.3);
init->setM24(2.4);
init->setM31(3.1);
init->setM32(3.2);
init->setM33(3.3);
init->setM34(3.4);
init->setM41(4.1);
init->setM42(4.2);
init->setM43(4.3);
init->setM44(4.4);
DOMMatrix* matrix = DOMMatrix::fromMatrix(init, scope.GetExceptionState());
EXPECT_FALSE(matrix->is2D());
v8::Local<v8::Value> wrapper =
ToV8(matrix, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrix* new_matrix = V8DOMMatrix::ToImpl(result.As<v8::Object>());
EXPECT_NE(matrix, new_matrix);
EXPECT_FALSE(new_matrix->is2D());
EXPECT_EQ(matrix->m11(), new_matrix->m11());
EXPECT_EQ(matrix->m12(), new_matrix->m12());
EXPECT_EQ(matrix->m13(), new_matrix->m13());
EXPECT_EQ(matrix->m14(), new_matrix->m14());
EXPECT_EQ(matrix->m21(), new_matrix->m21());
EXPECT_EQ(matrix->m22(), new_matrix->m22());
EXPECT_EQ(matrix->m23(), new_matrix->m23());
EXPECT_EQ(matrix->m24(), new_matrix->m24());
EXPECT_EQ(matrix->m31(), new_matrix->m31());
EXPECT_EQ(matrix->m32(), new_matrix->m32());
EXPECT_EQ(matrix->m33(), new_matrix->m33());
EXPECT_EQ(matrix->m34(), new_matrix->m34());
EXPECT_EQ(matrix->m41(), new_matrix->m41());
EXPECT_EQ(matrix->m42(), new_matrix->m42());
EXPECT_EQ(matrix->m43(), new_matrix->m43());
EXPECT_EQ(matrix->m44(), new_matrix->m44());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMMatrix) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue({
0xff, 0x11, 0xff, 0x0d, 0x5c, 0x59, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0xf1, 0x3f, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xf3, 0x3f, 0xcd, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0xf6, 0x3f, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x40, 0x9a, 0x99,
0x99, 0x99, 0x99, 0x99, 0x01, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x02, 0x40, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x03, 0x40, 0xcd, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc, 0x08, 0x40, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0x09, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0a, 0x40, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x0b, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x10, 0x40, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x11, 0x40, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0x11, 0x40,
});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrix* matrix = V8DOMMatrix::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(matrix->is2D());
EXPECT_EQ(1.1, matrix->m11());
EXPECT_EQ(1.2, matrix->m12());
EXPECT_EQ(1.3, matrix->m13());
EXPECT_EQ(1.4, matrix->m14());
EXPECT_EQ(2.1, matrix->m21());
EXPECT_EQ(2.2, matrix->m22());
EXPECT_EQ(2.3, matrix->m23());
EXPECT_EQ(2.4, matrix->m24());
EXPECT_EQ(3.1, matrix->m31());
EXPECT_EQ(3.2, matrix->m32());
EXPECT_EQ(3.3, matrix->m33());
EXPECT_EQ(3.4, matrix->m34());
EXPECT_EQ(4.1, matrix->m41());
EXPECT_EQ(4.2, matrix->m42());
EXPECT_EQ(4.3, matrix->m43());
EXPECT_EQ(4.4, matrix->m44());
}
TEST(V8ScriptValueSerializerTest, RoundTripDOMMatrixReadOnly) {
// DOMMatrixReadOnly objects should serialize and deserialize correctly.
V8TestingScope scope;
DOMMatrixInit* init = DOMMatrixInit::Create();
init->setIs2D(false);
init->setM11(1.1);
init->setM12(1.2);
init->setM13(1.3);
init->setM14(1.4);
init->setM21(2.1);
init->setM22(2.2);
init->setM23(2.3);
init->setM24(2.4);
init->setM31(3.1);
init->setM32(3.2);
init->setM33(3.3);
init->setM34(3.4);
init->setM41(4.1);
init->setM42(4.2);
init->setM43(4.3);
init->setM44(4.4);
DOMMatrixReadOnly* matrix =
DOMMatrixReadOnly::fromMatrix(init, scope.GetExceptionState());
EXPECT_FALSE(matrix->is2D());
v8::Local<v8::Value> wrapper =
ToV8(matrix, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8DOMMatrixReadOnly::hasInstance(result, scope.GetIsolate()));
EXPECT_FALSE(V8DOMMatrix::hasInstance(result, scope.GetIsolate()));
DOMMatrixReadOnly* new_matrix =
V8DOMMatrixReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_NE(matrix, new_matrix);
EXPECT_FALSE(new_matrix->is2D());
EXPECT_EQ(matrix->m11(), new_matrix->m11());
EXPECT_EQ(matrix->m12(), new_matrix->m12());
EXPECT_EQ(matrix->m13(), new_matrix->m13());
EXPECT_EQ(matrix->m14(), new_matrix->m14());
EXPECT_EQ(matrix->m21(), new_matrix->m21());
EXPECT_EQ(matrix->m22(), new_matrix->m22());
EXPECT_EQ(matrix->m23(), new_matrix->m23());
EXPECT_EQ(matrix->m24(), new_matrix->m24());
EXPECT_EQ(matrix->m31(), new_matrix->m31());
EXPECT_EQ(matrix->m32(), new_matrix->m32());
EXPECT_EQ(matrix->m33(), new_matrix->m33());
EXPECT_EQ(matrix->m34(), new_matrix->m34());
EXPECT_EQ(matrix->m41(), new_matrix->m41());
EXPECT_EQ(matrix->m42(), new_matrix->m42());
EXPECT_EQ(matrix->m43(), new_matrix->m43());
EXPECT_EQ(matrix->m44(), new_matrix->m44());
}
TEST(V8ScriptValueSerializerTest, DecodeDOMMatrixReadOnly) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue({
0xff, 0x11, 0xff, 0x0d, 0x5c, 0x55, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0xf1, 0x3f, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xf3, 0x3f, 0xcd, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0xf6, 0x3f, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x40, 0x9a, 0x99,
0x99, 0x99, 0x99, 0x99, 0x01, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x02, 0x40, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x03, 0x40, 0xcd, 0xcc,
0xcc, 0xcc, 0xcc, 0xcc, 0x08, 0x40, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0x09, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0a, 0x40, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x0b, 0x40, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
0x10, 0x40, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0x40, 0x33, 0x33,
0x33, 0x33, 0x33, 0x33, 0x11, 0x40, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99,
0x11, 0x40,
});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8DOMMatrixReadOnly::hasInstance(result, scope.GetIsolate()));
DOMMatrixReadOnly* matrix =
V8DOMMatrixReadOnly::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(matrix->is2D());
EXPECT_EQ(1.1, matrix->m11());
EXPECT_EQ(1.2, matrix->m12());
EXPECT_EQ(1.3, matrix->m13());
EXPECT_EQ(1.4, matrix->m14());
EXPECT_EQ(2.1, matrix->m21());
EXPECT_EQ(2.2, matrix->m22());
EXPECT_EQ(2.3, matrix->m23());
EXPECT_EQ(2.4, matrix->m24());
EXPECT_EQ(3.1, matrix->m31());
EXPECT_EQ(3.2, matrix->m32());
EXPECT_EQ(3.3, matrix->m33());
EXPECT_EQ(3.4, matrix->m34());
EXPECT_EQ(4.1, matrix->m41());
EXPECT_EQ(4.2, matrix->m42());
EXPECT_EQ(4.3, matrix->m43());
EXPECT_EQ(4.4, matrix->m44());
}
TEST(V8ScriptValueSerializerTest, RoundTripImageData) {
// ImageData objects should serialize and deserialize correctly.
V8TestingScope scope;
ImageData* image_data = ImageData::Create(2, 1, ASSERT_NO_EXCEPTION);
image_data->data()->Data()[0] = 200;
v8::Local<v8::Value> wrapper =
ToV8(image_data, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8ImageData::hasInstance(result, scope.GetIsolate()));
ImageData* new_image_data = V8ImageData::ToImpl(result.As<v8::Object>());
EXPECT_NE(image_data, new_image_data);
EXPECT_EQ(image_data->Size(), new_image_data->Size());
EXPECT_EQ(image_data->data()->length(), new_image_data->data()->length());
EXPECT_EQ(200, new_image_data->data()->Data()[0]);
}
TEST(V8ScriptValueSerializerTest, RoundTripImageDataWithColorSpaceInfo) {
// ImageData objects with color space information should serialize and
// deserialize correctly.
V8TestingScope scope;
ImageDataColorSettings* color_settings = ImageDataColorSettings::Create();
color_settings->setColorSpace("p3");
color_settings->setStorageFormat("float32");
ImageData* image_data =
ImageData::CreateImageData(2, 1, color_settings, ASSERT_NO_EXCEPTION);
static_cast<unsigned char*>(image_data->BufferBase()->Data())[0] = 200;
v8::Local<v8::Value> wrapper =
ToV8(image_data, scope.GetContext()->Global(), scope.GetIsolate());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8ImageData::hasInstance(result, scope.GetIsolate()));
ImageData* new_image_data = V8ImageData::ToImpl(result.As<v8::Object>());
EXPECT_NE(image_data, new_image_data);
EXPECT_EQ(image_data->Size(), new_image_data->Size());
ImageDataColorSettings* new_color_settings =
new_image_data->getColorSettings();
EXPECT_EQ("p3", new_color_settings->colorSpace());
EXPECT_EQ("float32", new_color_settings->storageFormat());
EXPECT_EQ(image_data->BufferBase()->ByteLength(),
new_image_data->BufferBase()->ByteLength());
EXPECT_EQ(200, static_cast<unsigned char*>(
new_image_data->BufferBase()->Data())[0]);
}
TEST(V8ScriptValueSerializerTest, DecodeImageDataV9) {
// Backward compatibility with existing serialized ImageData objects must be
// maintained. Add more cases if the format changes; don't remove tests for
// old versions.
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_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(script_state, input).Deserialize();
ASSERT_TRUE(V8ImageData::hasInstance(result, scope.GetIsolate()));
ImageData* new_image_data = V8ImageData::ToImpl(result.As<v8::Object>());
EXPECT_EQ(IntSize(2, 1), new_image_data->Size());
EXPECT_EQ(8u, new_image_data->data()->length());
EXPECT_EQ(200, new_image_data->data()->Data()[0]);
}
TEST(V8ScriptValueSerializerTest, DecodeImageDataV16) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x10, 0xff, 0x0c, 0x23, 0x02, 0x01, 0x08, 0xc8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8ImageData::hasInstance(result, scope.GetIsolate()));
ImageData* new_image_data = V8ImageData::ToImpl(result.As<v8::Object>());
EXPECT_EQ(IntSize(2, 1), new_image_data->Size());
EXPECT_EQ(8u, new_image_data->data()->length());
EXPECT_EQ(200, new_image_data->data()->Data()[0]);
}
TEST(V8ScriptValueSerializerTest, DecodeImageDataV18) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x23, 0x01, 0x03, 0x03, 0x02, 0x00, 0x02,
0x01, 0x20, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8ImageData::hasInstance(result, scope.GetIsolate()));
ImageData* new_image_data = V8ImageData::ToImpl(result.As<v8::Object>());
EXPECT_EQ(IntSize(2, 1), new_image_data->Size());
ImageDataColorSettings* new_color_settings =
new_image_data->getColorSettings();
EXPECT_EQ("p3", new_color_settings->colorSpace());
EXPECT_EQ("float32", new_color_settings->storageFormat());
EXPECT_EQ(32u, new_image_data->BufferBase()->ByteLength());
EXPECT_EQ(200, static_cast<unsigned char*>(
new_image_data->BufferBase()->Data())[0]);
}
MessagePort* MakeMessagePort(ExecutionContext* execution_context,
::MojoHandle* unowned_handle_out = nullptr) {
MessagePort* port = MessagePort::Create(*execution_context);
mojo::MessagePipe pipe;
::MojoHandle unowned_handle = pipe.handle0.get().value();
port->Entangle(std::move(pipe.handle0));
EXPECT_TRUE(port->IsEntangled());
EXPECT_EQ(unowned_handle, port->EntangledHandleForTesting());
if (unowned_handle_out)
*unowned_handle_out = unowned_handle;
return port;
}
TEST(V8ScriptValueSerializerTest, RoundTripMessagePort) {
V8TestingScope scope;
::MojoHandle unowned_handle;
MessagePort* port =
MakeMessagePort(scope.GetExecutionContext(), &unowned_handle);
v8::Local<v8::Value> wrapper = ToV8(port, scope.GetScriptState());
Transferables transferables;
transferables.message_ports.push_back(port);
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, &transferables);
ASSERT_TRUE(V8MessagePort::hasInstance(result, scope.GetIsolate()));
MessagePort* new_port = V8MessagePort::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(port->IsEntangled());
EXPECT_TRUE(new_port->IsEntangled());
EXPECT_EQ(unowned_handle, new_port->EntangledHandleForTesting());
}
TEST(V8ScriptValueSerializerTest, NeuteredMessagePortThrowsDataCloneError) {
V8TestingScope scope;
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "Window",
"postMessage");
MessagePort* port = MessagePort::Create(*scope.GetExecutionContext());
EXPECT_TRUE(port->IsNeutered());
v8::Local<v8::Value> wrapper = ToV8(port, scope.GetScriptState());
Transferables transferables;
transferables.message_ports.push_back(port);
RoundTrip(wrapper, scope, &exception_state, &transferables);
ASSERT_TRUE(HadDOMExceptionInCoreTest(
"DataCloneError", scope.GetScriptState(), exception_state));
}
TEST(V8ScriptValueSerializerTest,
UntransferredMessagePortThrowsDataCloneError) {
V8TestingScope scope;
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "Window",
"postMessage");
MessagePort* port = MakeMessagePort(scope.GetExecutionContext());
v8::Local<v8::Value> wrapper = ToV8(port, scope.GetScriptState());
Transferables transferables;
RoundTrip(wrapper, scope, &exception_state, &transferables);
ASSERT_TRUE(HadDOMExceptionInCoreTest(
"DataCloneError", scope.GetScriptState(), exception_state));
}
TEST(V8ScriptValueSerializerTest, OutOfRangeMessagePortIndex) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x4d, 0x01});
MessagePort* port1 = MakeMessagePort(scope.GetExecutionContext());
MessagePort* port2 = MakeMessagePort(scope.GetExecutionContext());
{
V8ScriptValueDeserializer deserializer(script_state, input);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
{
V8ScriptValueDeserializer::Options options;
options.message_ports = MakeGarbageCollected<MessagePortArray>();
V8ScriptValueDeserializer deserializer(script_state, input, options);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
{
V8ScriptValueDeserializer::Options options;
options.message_ports = MakeGarbageCollected<MessagePortArray>();
options.message_ports->push_back(port1);
V8ScriptValueDeserializer deserializer(script_state, input, options);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
{
V8ScriptValueDeserializer::Options options;
options.message_ports = MakeGarbageCollected<MessagePortArray>();
options.message_ports->push_back(port1);
options.message_ports->push_back(port2);
V8ScriptValueDeserializer deserializer(script_state, input, options);
v8::Local<v8::Value> result = deserializer.Deserialize();
ASSERT_TRUE(V8MessagePort::hasInstance(result, scope.GetIsolate()));
EXPECT_EQ(port2, V8MessagePort::ToImpl(result.As<v8::Object>()));
}
}
TEST(V8ScriptValueSerializerTest, RoundTripMojoHandle) {
V8TestingScope scope;
mojo::MessagePipe pipe;
MojoHandle* handle =
MojoHandle::Create(mojo::ScopedHandle::From(std::move(pipe.handle0)));
v8::Local<v8::Value> wrapper = ToV8(handle, scope.GetScriptState());
Transferables transferables;
transferables.mojo_handles.push_back(handle);
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, &transferables);
ASSERT_TRUE(V8MojoHandle::hasInstance(result, scope.GetIsolate()));
MojoHandle* new_handle = V8MojoHandle::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(handle->TakeHandle().is_valid());
EXPECT_TRUE(new_handle->TakeHandle().is_valid());
}
TEST(V8ScriptValueSerializerTest, UntransferredMojoHandleThrowsDataCloneError) {
V8TestingScope scope;
ExceptionState exception_state(scope.GetIsolate(),
ExceptionState::kExecutionContext, "Window",
"postMessage");
mojo::MessagePipe pipe;
MojoHandle* handle =
MojoHandle::Create(mojo::ScopedHandle::From(std::move(pipe.handle0)));
v8::Local<v8::Value> wrapper = ToV8(handle, scope.GetScriptState());
Transferables transferables;
RoundTrip(wrapper, scope, &exception_state, &transferables);
ASSERT_TRUE(HadDOMExceptionInCoreTest(
"DataCloneError", scope.GetScriptState(), exception_state));
}
// Decode tests for backward compatibility are not required for message ports
// and Mojo handles because they cannot be persisted to disk.
// A more exhaustive set of ImageBitmap cases are covered by web tests.
TEST(V8ScriptValueSerializerTest, RoundTripImageBitmap) {
V8TestingScope scope;
// Make a 10x7 red ImageBitmap.
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 7);
surface->getCanvas()->clear(SK_ColorRED);
ImageBitmap* image_bitmap = ImageBitmap::Create(
StaticBitmapImage::Create(surface->makeImageSnapshot()));
ASSERT_TRUE(image_bitmap->BitmapImage());
// Serialize and deserialize it.
v8::Local<v8::Value> wrapper = ToV8(image_bitmap, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.GetIsolate()));
ImageBitmap* new_image_bitmap =
V8ImageBitmap::ToImpl(result.As<v8::Object>());
ASSERT_TRUE(new_image_bitmap->BitmapImage());
ASSERT_EQ(IntSize(10, 7), new_image_bitmap->Size());
// Check that the pixel at (3, 3) is red.
uint8_t pixel[4] = {};
ASSERT_TRUE(new_image_bitmap->BitmapImage()
->PaintImageForCurrentFrame()
.GetSkImage()
->readPixels(SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType,
kPremul_SkAlphaType),
&pixel, 4, 3, 3));
ASSERT_THAT(pixel, testing::ElementsAre(255, 0, 0, 255));
}
TEST(V8ScriptValueSerializerTest, RoundTripImageBitmapWithColorSpaceInfo) {
V8TestingScope scope;
// Make a 10x7 red ImageBitmap in P3 color space.
SkImageInfo info = SkImageInfo::Make(
10, 7, kRGBA_F16_SkColorType, kPremul_SkAlphaType,
SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
SkColorSpace::kDCIP3_D65_Gamut));
sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
surface->getCanvas()->clear(SK_ColorRED);
ImageBitmap* image_bitmap = ImageBitmap::Create(
StaticBitmapImage::Create(surface->makeImageSnapshot()));
ASSERT_TRUE(image_bitmap->BitmapImage());
// Serialize and deserialize it.
v8::Local<v8::Value> wrapper = ToV8(image_bitmap, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.GetIsolate()));
ImageBitmap* new_image_bitmap =
V8ImageBitmap::ToImpl(result.As<v8::Object>());
ASSERT_TRUE(new_image_bitmap->BitmapImage());
ASSERT_EQ(IntSize(10, 7), new_image_bitmap->Size());
// Check the color settings.
CanvasColorParams color_params = new_image_bitmap->GetCanvasColorParams();
EXPECT_EQ(kP3CanvasColorSpace, color_params.ColorSpace());
EXPECT_EQ(kF16CanvasPixelFormat, color_params.PixelFormat());
// Check that the pixel at (3, 3) is red. We expect red in P3 to be
// {0x94, 0x3A, 0x3F, 0x28, 0x5F, 0x24, 0x00, 0x3C} when each color
// component is presented as a half float in Skia. However, difference in
// GPU hardware may result in small differences in lower significant byte in
// Skia color conversion pipeline. Hence, we use a tolerance of 2 here.
uint8_t pixel[8] = {};
ASSERT_TRUE(new_image_bitmap->BitmapImage()
->PaintImageForCurrentFrame()
.GetSkImage()
->readPixels(info.makeWH(1, 1), &pixel, 8, 3, 3));
uint8_t p3_red[8] = {0x94, 0x3A, 0x3F, 0x28, 0x5F, 0x24, 0x00, 0x3C};
bool approximate_match = true;
uint8_t tolerance = 2;
for (int i = 0; i < 8; i++) {
if (std::abs(p3_red[i] - pixel[i]) > tolerance) {
approximate_match = false;
break;
}
}
ASSERT_TRUE(approximate_match);
}
TEST(V8ScriptValueSerializerTest, DecodeImageBitmap) {
// Backward compatibility with existing serialized ImageBitmap objects must be
// maintained. Add more cases if the format changes; don't remove tests for
// old versions.
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
// This is checked by platform instead of by SK_PMCOLOR_BYTE_ORDER because
// this test intends to ensure that a platform can decode images it has
// previously written. At format version 9, Android writes RGBA and every
// other platform writes BGRA.
#if defined(OS_ANDROID)
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x67, 0x01, 0x01, 0x02, 0x01,
0x08, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff});
#else
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x67, 0x01, 0x01, 0x02, 0x01,
0x08, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff});
#endif
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.GetIsolate()));
ImageBitmap* new_image_bitmap =
V8ImageBitmap::ToImpl(result.As<v8::Object>());
ASSERT_EQ(IntSize(2, 1), new_image_bitmap->Size());
// Check that the pixels are opaque red and green, respectively.
uint8_t pixels[8] = {};
ASSERT_TRUE(new_image_bitmap->BitmapImage()
->PaintImageForCurrentFrame()
.GetSkImage()
->readPixels(SkImageInfo::Make(2, 1, kRGBA_8888_SkColorType,
kPremul_SkAlphaType),
&pixels, 8, 0, 0));
ASSERT_THAT(pixels, testing::ElementsAre(255, 0, 0, 255, 0, 255, 0, 255));
}
TEST(V8ScriptValueSerializerTest, DecodeImageBitmapV18) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x01, 0x04, 0x01,
0x05, 0x01, 0x00, 0x02, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(script_state, input).Deserialize();
ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.GetIsolate()));
ImageBitmap* new_image_bitmap =
V8ImageBitmap::ToImpl(result.As<v8::Object>());
ASSERT_EQ(IntSize(2, 1), new_image_bitmap->Size());
// Check the color settings.
CanvasColorParams color_params = new_image_bitmap->GetCanvasColorParams();
EXPECT_EQ(kP3CanvasColorSpace, color_params.ColorSpace());
EXPECT_EQ(kF16CanvasPixelFormat, color_params.PixelFormat());
// Check that the pixel at (1, 0) is red.
uint8_t pixel[8] = {};
SkImageInfo info = SkImageInfo::Make(
1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType,
SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
SkColorSpace::kDCIP3_D65_Gamut));
ASSERT_TRUE(new_image_bitmap->BitmapImage()
->PaintImageForCurrentFrame()
.GetSkImage()
->readPixels(info, &pixel, 8, 1, 0));
// The reference values are the hex representation of red in P3 (as stored
// in half floats by Skia).
ASSERT_THAT(pixel, testing::ElementsAre(0x94, 0x3A, 0x3F, 0x28, 0x5F, 0x24,
0x0, 0x3C));
}
TEST(V8ScriptValueSerializerTest, InvalidImageBitmapDecode) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
{
// Too many bytes declared in pixel data.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x09, 0x3f, 0x00, 0x67, 0x01, 0x01, 0x02, 0x01, 0x09,
0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Too few bytes declared in pixel data.
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x67, 0x01, 0x01, 0x02, 0x01,
0x07, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for origin clean data.
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x67, 0x02, 0x01, 0x02, 0x01,
0x08, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for premultiplied bit.
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x67, 0x01, 0x02, 0x02, 0x01,
0x08, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
}
TEST(V8ScriptValueSerializerTest, InvalidImageBitmapDecodeV18) {
V8TestingScope scope;
ScriptState* script_state = scope.GetScriptState();
{
// Too many bytes declared in pixel data.
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02,
0x03, 0x04, 0x01, 0x05, 0x01, 0x00, 0x02, 0x01, 0x11,
0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c, 0x94,
0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c, 0x00, 0x00});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Too few bytes declared in pixel data.
scoped_refptr<SerializedScriptValue> input = SerializedValue({
0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x03, 0x04,
0x01, 0x05, 0x01, 0x00, 0x02, 0x01, 0x0f, 0x94, 0x3a, 0x3f, 0x28,
0x5f, 0x24, 0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for color space data.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x05, 0x02, 0x03, 0x04, 0x01,
0x05, 0x01, 0x00, 0x02, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for pixel format data.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x04, 0x04, 0x01,
0x05, 0x01, 0x00, 0x02, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for origin clean data.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x03, 0x04, 0x02,
0x05, 0x01, 0x00, 0x02, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Nonsense for premultiplied bit.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x03, 0x04, 0x01,
0x05, 0x02, 0x00, 0x02, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
{
// Wrong size declared in pixel data.
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x12, 0xff, 0x0d, 0x5c, 0x67, 0x01, 0x03, 0x02, 0x03, 0x04, 0x01,
0x05, 0x01, 0x00, 0x03, 0x01, 0x10, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24,
0x00, 0x3c, 0x94, 0x3a, 0x3f, 0x28, 0x5f, 0x24, 0x00, 0x3c});
EXPECT_TRUE(
V8ScriptValueDeserializer(script_state, input).Deserialize()->IsNull());
}
}
TEST(V8ScriptValueSerializerTest, TransferImageBitmap) {
// More thorough tests exist in web_tests/.
V8TestingScope scope;
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(10, 7);
surface->getCanvas()->clear(SK_ColorRED);
sk_sp<SkImage> image = surface->makeImageSnapshot();
ImageBitmap* image_bitmap =
ImageBitmap::Create(StaticBitmapImage::Create(image));
ASSERT_TRUE(image_bitmap->BitmapImage());
v8::Local<v8::Value> wrapper = ToV8(image_bitmap, scope.GetScriptState());
Transferables transferables;
transferables.image_bitmaps.push_back(image_bitmap);
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, &transferables);
ASSERT_TRUE(V8ImageBitmap::hasInstance(result, scope.GetIsolate()));
ImageBitmap* new_image_bitmap =
V8ImageBitmap::ToImpl(result.As<v8::Object>());
ASSERT_TRUE(new_image_bitmap->BitmapImage());
ASSERT_EQ(IntSize(10, 7), new_image_bitmap->Size());
// Check that the pixel at (3, 3) is red.
uint8_t pixel[4] = {};
sk_sp<SkImage> new_image =
new_image_bitmap->BitmapImage()->PaintImageForCurrentFrame().GetSkImage();
ASSERT_TRUE(new_image->readPixels(
SkImageInfo::Make(1, 1, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
&pixel, 4, 3, 3));
ASSERT_THAT(pixel, testing::ElementsAre(255, 0, 0, 255));
// Check also that the underlying image contents were transferred.
EXPECT_EQ(image, new_image);
EXPECT_TRUE(image_bitmap->IsNeutered());
}
TEST(V8ScriptValueSerializerTest, TransferOffscreenCanvas) {
// More exhaustive tests in web_tests/. This is a sanity check.
V8TestingScope scope;
OffscreenCanvas* canvas = OffscreenCanvas::Create(10, 7);
canvas->SetPlaceholderCanvasId(519);
v8::Local<v8::Value> wrapper = ToV8(canvas, scope.GetScriptState());
Transferables transferables;
transferables.offscreen_canvases.push_back(canvas);
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, &transferables);
ASSERT_TRUE(V8OffscreenCanvas::hasInstance(result, scope.GetIsolate()));
OffscreenCanvas* new_canvas =
V8OffscreenCanvas::ToImpl(result.As<v8::Object>());
EXPECT_EQ(IntSize(10, 7), new_canvas->Size());
EXPECT_EQ(519u, new_canvas->PlaceholderCanvasId());
EXPECT_TRUE(canvas->IsNeutered());
EXPECT_FALSE(new_canvas->IsNeutered());
}
TEST(V8ScriptValueSerializerTest, RoundTripBlob) {
V8TestingScope scope;
const char kHelloWorld[] = "Hello world!";
Blob* blob =
Blob::Create(reinterpret_cast<const unsigned char*>(&kHelloWorld),
sizeof(kHelloWorld), "text/plain");
String uuid = blob->Uuid();
EXPECT_FALSE(uuid.IsEmpty());
v8::Local<v8::Value> wrapper = ToV8(blob, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8Blob::hasInstance(result, scope.GetIsolate()));
Blob* new_blob = V8Blob::ToImpl(result.As<v8::Object>());
EXPECT_EQ("text/plain", new_blob->type());
EXPECT_EQ(sizeof(kHelloWorld), new_blob->size());
EXPECT_EQ(uuid, new_blob->Uuid());
}
TEST(V8ScriptValueSerializerTest, DecodeBlob) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x09, 0x3f, 0x00, 0x62, 0x24, 0x64, 0x38, 0x37, 0x35, 0x64,
0x66, 0x63, 0x32, 0x2d, 0x34, 0x35, 0x30, 0x35, 0x2d, 0x34, 0x36,
0x31, 0x62, 0x2d, 0x39, 0x38, 0x66, 0x65, 0x2d, 0x30, 0x63, 0x66,
0x36, 0x63, 0x63, 0x35, 0x65, 0x61, 0x66, 0x34, 0x34, 0x0a, 0x74,
0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x0c});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8Blob::hasInstance(result, scope.GetIsolate()));
Blob* new_blob = V8Blob::ToImpl(result.As<v8::Object>());
EXPECT_EQ("d875dfc2-4505-461b-98fe-0cf6cc5eaf44", new_blob->Uuid());
EXPECT_EQ("text/plain", new_blob->type());
EXPECT_EQ(12u, new_blob->size());
}
TEST(V8ScriptValueSerializerTest, RoundTripBlobIndex) {
V8TestingScope scope;
const char kHelloWorld[] = "Hello world!";
Blob* blob =
Blob::Create(reinterpret_cast<const unsigned char*>(&kHelloWorld),
sizeof(kHelloWorld), "text/plain");
String uuid = blob->Uuid();
EXPECT_FALSE(uuid.IsEmpty());
v8::Local<v8::Value> wrapper = ToV8(blob, scope.GetScriptState());
WebBlobInfoArray blob_info_array;
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, nullptr, &blob_info_array);
// As before, the resulting blob should be correct.
ASSERT_TRUE(V8Blob::hasInstance(result, scope.GetIsolate()));
Blob* new_blob = V8Blob::ToImpl(result.As<v8::Object>());
EXPECT_EQ("text/plain", new_blob->type());
EXPECT_EQ(sizeof(kHelloWorld), new_blob->size());
EXPECT_EQ(uuid, new_blob->Uuid());
// The blob info array should also contain the blob details since it was
// serialized by index into this array.
ASSERT_EQ(1u, blob_info_array.size());
const WebBlobInfo& info = blob_info_array[0];
EXPECT_FALSE(info.IsFile());
EXPECT_EQ(uuid, String(info.Uuid()));
EXPECT_EQ("text/plain", info.GetType());
EXPECT_EQ(sizeof(kHelloWorld), static_cast<size_t>(info.size()));
}
TEST(V8ScriptValueSerializerTest, DecodeBlobIndex) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x69, 0x00});
WebBlobInfoArray blob_info_array;
blob_info_array.emplace_back(WebBlobInfo::BlobForTesting(
"d875dfc2-4505-461b-98fe-0cf6cc5eaf44", "text/plain", 12));
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
v8::Local<v8::Value> result = deserializer.Deserialize();
ASSERT_TRUE(V8Blob::hasInstance(result, scope.GetIsolate()));
Blob* new_blob = V8Blob::ToImpl(result.As<v8::Object>());
EXPECT_EQ("d875dfc2-4505-461b-98fe-0cf6cc5eaf44", new_blob->Uuid());
EXPECT_EQ("text/plain", new_blob->type());
EXPECT_EQ(12u, new_blob->size());
}
TEST(V8ScriptValueSerializerTest, DecodeBlobIndexOutOfRange) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x69, 0x01});
{
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
{
WebBlobInfoArray blob_info_array;
blob_info_array.emplace_back(WebBlobInfo::BlobForTesting(
"d875dfc2-4505-461b-98fe-0cf6cc5eaf44", "text/plain", 12));
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
}
TEST(V8ScriptValueSerializerTest, RoundTripFileNative) {
V8TestingScope scope;
File* file = File::Create("/native/path");
v8::Local<v8::Value> wrapper = ToV8(file, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_TRUE(new_file->HasBackingFile());
EXPECT_EQ("/native/path", new_file->GetPath());
EXPECT_TRUE(new_file->FileSystemURL().IsEmpty());
}
TEST(V8ScriptValueSerializerTest, RoundTripFileBackedByBlob) {
V8TestingScope scope;
const double kModificationTime = 0.0;
scoped_refptr<BlobDataHandle> blob_data_handle = BlobDataHandle::Create();
File* file =
File::Create("/native/path", kModificationTime, blob_data_handle);
v8::Local<v8::Value> wrapper = ToV8(file, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(new_file->HasBackingFile());
EXPECT_TRUE(file->GetPath().IsEmpty());
EXPECT_TRUE(new_file->FileSystemURL().IsEmpty());
}
TEST(V8ScriptValueSerializerTest, RoundTripFileNativeSnapshot) {
V8TestingScope scope;
FileMetadata metadata;
metadata.platform_path = "/native/snapshot";
File* file =
File::CreateForFileSystemFile("name", metadata, File::kIsUserVisible);
v8::Local<v8::Value> wrapper = ToV8(file, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_TRUE(new_file->HasBackingFile());
EXPECT_EQ("/native/snapshot", new_file->GetPath());
EXPECT_TRUE(new_file->FileSystemURL().IsEmpty());
}
TEST(V8ScriptValueSerializerTest, RoundTripFileNonNativeSnapshot) {
// Preserving behavior, filesystem URL is not preserved across cloning.
V8TestingScope scope;
KURL url("filesystem:http://example.com/isolated/hash/non-native-file");
File* file =
File::CreateForFileSystemFile(url, FileMetadata(), File::kIsUserVisible);
v8::Local<v8::Value> wrapper = ToV8(file, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_FALSE(new_file->HasBackingFile());
EXPECT_TRUE(file->GetPath().IsEmpty());
EXPECT_TRUE(new_file->FileSystemURL().IsEmpty());
}
// Used for checking that times provided are between now and the current time
// when the checker was constructed, according to WTF::currentTime.
class TimeIntervalChecker {
public:
TimeIntervalChecker() : start_time_(WTF::CurrentTime()) {}
bool WasAliveAt(double time_in_milliseconds) {
double time = time_in_milliseconds / kMsPerSecond;
return start_time_ <= time && time <= WTF::CurrentTime();
}
private:
const double start_time_;
};
TEST(V8ScriptValueSerializerTest, DecodeFileV3) {
V8TestingScope scope;
TimeIntervalChecker time_interval_checker;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x03, 0x3f, 0x00, 0x66, 0x04, 'p', 'a', 't', 'h', 0x24, 'f',
'4', 'a', '6', 'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd',
'-', '4', 'd', 'c', '3', '-', 'b', '6', '7', 'c', '-', 'a',
'7', '7', '9', 'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a,
't', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_FALSE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(0u, new_file->size());
EXPECT_TRUE(time_interval_checker.WasAliveAt(new_file->lastModifiedDate()));
EXPECT_EQ(File::kIsUserVisible, new_file->GetUserVisibility());
}
TEST(V8ScriptValueSerializerTest, DecodeFileV4) {
V8TestingScope scope;
TimeIntervalChecker time_interval_checker;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x04, 0x3f, 0x00, 0x66, 0x04, 'p', 'a', 't', 'h', 0x04, 'n',
'a', 'm', 'e', 0x03, 'r', 'e', 'l', 0x24, 'f', '4', 'a', '6',
'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd', '-', '4', 'd',
'c', '3', '-', 'b', '6', '7', 'c', '-', 'a', '7', '7', '9',
'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a, 't', 'e', 'x',
't', '/', 'p', 'l', 'a', 'i', 'n', 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("rel", new_file->webkitRelativePath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_FALSE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(0u, new_file->size());
EXPECT_TRUE(time_interval_checker.WasAliveAt(new_file->lastModifiedDate()));
EXPECT_EQ(File::kIsUserVisible, new_file->GetUserVisibility());
}
TEST(V8ScriptValueSerializerTest, DecodeFileV4WithSnapshot) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x04, 0x3f, 0x00, 0x66, 0x04, 'p', 'a', 't', 'h', 0x04, 'n',
'a', 'm', 'e', 0x03, 'r', 'e', 'l', 0x24, 'f', '4', 'a', '6',
'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd', '-', '4', 'd',
'c', '3', '-', 'b', '6', '7', 'c', '-', 'a', '7', '7', '9',
'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a, 't', 'e', 'x',
't', '/', 'p', 'l', 'a', 'i', 'n', 0x01, 0x80, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd0, 0xbf});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("rel", new_file->webkitRelativePath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_TRUE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(512u, new_file->size());
// From v4 to v7, the last modified time is written in seconds.
// So -0.25 represents 250 ms before the Unix epoch.
EXPECT_EQ(-250.0, new_file->lastModifiedDate());
}
TEST(V8ScriptValueSerializerTest, DecodeFileV7) {
V8TestingScope scope;
TimeIntervalChecker time_interval_checker;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x07, 0x3f, 0x00, 0x66, 0x04, 'p', 'a', 't', 'h', 0x04, 'n',
'a', 'm', 'e', 0x03, 'r', 'e', 'l', 0x24, 'f', '4', 'a', '6',
'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd', '-', '4', 'd',
'c', '3', '-', 'b', '6', '7', 'c', '-', 'a', '7', '7', '9',
'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a, 't', 'e', 'x',
't', '/', 'p', 'l', 'a', 'i', 'n', 0x00, 0x00, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("rel", new_file->webkitRelativePath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_FALSE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(0u, new_file->size());
EXPECT_TRUE(time_interval_checker.WasAliveAt(new_file->lastModifiedDate()));
EXPECT_EQ(File::kIsNotUserVisible, new_file->GetUserVisibility());
}
TEST(V8ScriptValueSerializerTest, DecodeFileV8WithSnapshot) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x08, 0x3f, 0x00, 0x66, 0x04, 'p', 'a', 't', 'h', 0x04, 'n',
'a', 'm', 'e', 0x03, 'r', 'e', 'l', 0x24, 'f', '4', 'a', '6',
'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd', '-', '4', 'd',
'c', '3', '-', 'b', '6', '7', 'c', '-', 'a', '7', '7', '9',
'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a, 't', 'e', 'x',
't', '/', 'p', 'l', 'a', 'i', 'n', 0x01, 0x80, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd0, 0xbf, 0x01, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("rel", new_file->webkitRelativePath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_TRUE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(512u, new_file->size());
// From v8, the last modified time is written in milliseconds.
// So -0.25 represents 0.25 ms before the Unix epoch.
EXPECT_EQ(-0.25, new_file->lastModifiedDate());
EXPECT_EQ(File::kIsUserVisible, new_file->GetUserVisibility());
}
TEST(V8ScriptValueSerializerTest, RoundTripFileIndex) {
V8TestingScope scope;
File* file = File::Create("/native/path");
v8::Local<v8::Value> wrapper = ToV8(file, scope.GetScriptState());
WebBlobInfoArray blob_info_array;
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, nullptr, &blob_info_array);
// As above, the resulting blob should be correct.
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_TRUE(new_file->HasBackingFile());
EXPECT_EQ("/native/path", new_file->GetPath());
EXPECT_TRUE(new_file->FileSystemURL().IsEmpty());
// The blob info array should also contain the details since it was serialized
// by index into this array.
ASSERT_EQ(1u, blob_info_array.size());
const WebBlobInfo& info = blob_info_array[0];
EXPECT_TRUE(info.IsFile());
EXPECT_EQ("/native/path", info.FilePath());
EXPECT_EQ(file->Uuid(), String(info.Uuid()));
}
TEST(V8ScriptValueSerializerTest, DecodeFileIndex) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x65, 0x00});
WebBlobInfoArray blob_info_array;
blob_info_array.emplace_back(
WebBlobInfo::FileForTesting("d875dfc2-4505-461b-98fe-0cf6cc5eaf44",
"/native/path", "path", "text/plain"));
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
v8::Local<v8::Value> result = deserializer.Deserialize();
ASSERT_TRUE(V8File::hasInstance(result, scope.GetIsolate()));
File* new_file = V8File::ToImpl(result.As<v8::Object>());
EXPECT_EQ("d875dfc2-4505-461b-98fe-0cf6cc5eaf44", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_EQ("/native/path", new_file->GetPath());
EXPECT_EQ("path", new_file->name());
}
TEST(V8ScriptValueSerializerTest, DecodeFileIndexOutOfRange) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x65, 0x01});
{
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
{
WebBlobInfoArray blob_info_array;
blob_info_array.emplace_back(
WebBlobInfo::FileForTesting("d875dfc2-4505-461b-98fe-0cf6cc5eaf44",
"/native/path", "path", "text/plain"));
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
ASSERT_TRUE(deserializer.Deserialize()->IsNull());
}
}
// Most of the logic for FileList is shared with File, so the tests here are
// fairly basic.
TEST(V8ScriptValueSerializerTest, RoundTripFileList) {
V8TestingScope scope;
FileList* file_list = FileList::Create();
file_list->Append(File::Create("/native/path"));
file_list->Append(File::Create("/native/path2"));
v8::Local<v8::Value> wrapper = ToV8(file_list, scope.GetScriptState());
v8::Local<v8::Value> result = RoundTrip(wrapper, scope);
ASSERT_TRUE(V8FileList::hasInstance(result, scope.GetIsolate()));
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
ASSERT_EQ(2u, new_file_list->length());
EXPECT_EQ("/native/path", new_file_list->item(0)->GetPath());
EXPECT_EQ("/native/path2", new_file_list->item(1)->GetPath());
}
TEST(V8ScriptValueSerializerTest, DecodeEmptyFileList) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x6c, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8FileList::hasInstance(result, scope.GetIsolate()));
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
EXPECT_EQ(0u, new_file_list->length());
}
TEST(V8ScriptValueSerializerTest, DecodeFileListWithInvalidLength) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x6c, 0x01});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
EXPECT_TRUE(result->IsNull());
}
TEST(V8ScriptValueSerializerTest, DecodeFileListV8WithoutSnapshot) {
V8TestingScope scope;
TimeIntervalChecker time_interval_checker;
scoped_refptr<SerializedScriptValue> input = SerializedValue(
{0xff, 0x08, 0x3f, 0x00, 0x6c, 0x01, 0x04, 'p', 'a', 't', 'h', 0x04,
'n', 'a', 'm', 'e', 0x03, 'r', 'e', 'l', 0x24, 'f', '4', 'a',
'6', 'e', 'd', 'd', '5', '-', '6', '5', 'a', 'd', '-', '4',
'd', 'c', '3', '-', 'b', '6', '7', 'c', '-', 'a', '7', '7',
'9', 'c', '0', '2', 'f', '0', 'f', 'a', '3', 0x0a, 't', 'e',
'x', 't', '/', 'p', 'l', 'a', 'i', 'n', 0x00, 0x00});
v8::Local<v8::Value> result =
V8ScriptValueDeserializer(scope.GetScriptState(), input).Deserialize();
ASSERT_TRUE(V8FileList::hasInstance(result, scope.GetIsolate()));
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1u, new_file_list->length());
File* new_file = new_file_list->item(0);
EXPECT_EQ("path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("rel", new_file->webkitRelativePath());
EXPECT_EQ("f4a6edd5-65ad-4dc3-b67c-a779c02f0fa3", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
EXPECT_FALSE(new_file->HasValidSnapshotMetadata());
EXPECT_EQ(0u, new_file->size());
EXPECT_TRUE(time_interval_checker.WasAliveAt(new_file->lastModifiedDate()));
EXPECT_EQ(File::kIsNotUserVisible, new_file->GetUserVisibility());
}
TEST(V8ScriptValueSerializerTest, RoundTripFileListIndex) {
V8TestingScope scope;
FileList* file_list = FileList::Create();
file_list->Append(File::Create("/native/path"));
file_list->Append(File::Create("/native/path2"));
v8::Local<v8::Value> wrapper = ToV8(file_list, scope.GetScriptState());
WebBlobInfoArray blob_info_array;
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, nullptr, nullptr, &blob_info_array);
// FileList should be produced correctly.
ASSERT_TRUE(V8FileList::hasInstance(result, scope.GetIsolate()));
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
ASSERT_EQ(2u, new_file_list->length());
EXPECT_EQ("/native/path", new_file_list->item(0)->GetPath());
EXPECT_EQ("/native/path2", new_file_list->item(1)->GetPath());
// And the blob info array should be populated.
ASSERT_EQ(2u, blob_info_array.size());
EXPECT_TRUE(blob_info_array[0].IsFile());
EXPECT_EQ("/native/path", blob_info_array[0].FilePath());
EXPECT_TRUE(blob_info_array[1].IsFile());
EXPECT_EQ("/native/path2", blob_info_array[1].FilePath());
}
TEST(V8ScriptValueSerializerTest, DecodeEmptyFileListIndex) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x4c, 0x00});
WebBlobInfoArray blob_info_array;
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
v8::Local<v8::Value> result = deserializer.Deserialize();
ASSERT_TRUE(V8FileList::hasInstance(result, scope.GetIsolate()));
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
EXPECT_EQ(0u, new_file_list->length());
}
TEST(V8ScriptValueSerializerTest, DecodeFileListIndexWithInvalidLength) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x4c, 0x02});
WebBlobInfoArray blob_info_array;
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
v8::Local<v8::Value> result = deserializer.Deserialize();
EXPECT_TRUE(result->IsNull());
}
TEST(V8ScriptValueSerializerTest, DecodeFileListIndex) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x09, 0x3f, 0x00, 0x4c, 0x01, 0x00, 0x00});
WebBlobInfoArray blob_info_array;
blob_info_array.emplace_back(
WebBlobInfo::FileForTesting("d875dfc2-4505-461b-98fe-0cf6cc5eaf44",
"/native/path", "name", "text/plain"));
V8ScriptValueDeserializer::Options options;
options.blob_info = &blob_info_array;
V8ScriptValueDeserializer deserializer(scope.GetScriptState(), input,
options);
v8::Local<v8::Value> result = deserializer.Deserialize();
FileList* new_file_list = V8FileList::ToImpl(result.As<v8::Object>());
EXPECT_EQ(1u, new_file_list->length());
File* new_file = new_file_list->item(0);
EXPECT_EQ("/native/path", new_file->GetPath());
EXPECT_EQ("name", new_file->name());
EXPECT_EQ("d875dfc2-4505-461b-98fe-0cf6cc5eaf44", new_file->Uuid());
EXPECT_EQ("text/plain", new_file->type());
}
// Decode tests aren't included here because they're slightly non-trivial (an
// element with the right ID must actually exist) and this feature is both
// unshipped and likely to not use this mechanism when it does.
// TODO(jbroman): Update this if that turns out not to be the case.
TEST(V8ScriptValueSerializerTest, DecodeHardcodedNullValue) {
V8TestingScope scope;
EXPECT_TRUE(V8ScriptValueDeserializer(scope.GetScriptState(),
SerializedScriptValue::NullValue())
.Deserialize()
->IsNull());
}
// This is not the most efficient way to write a small version, but it's
// technically admissible. We should handle this in a consistent way to avoid
// DCHECK failure. Thus this is "true" encoded slightly strangely.
TEST(V8ScriptValueSerializerTest, DecodeWithInefficientVersionEnvelope) {
V8TestingScope scope;
scoped_refptr<SerializedScriptValue> input =
SerializedValue({0xff, 0x80, 0x09, 0xff, 0x09, 0x54});
EXPECT_TRUE(
V8ScriptValueDeserializer(scope.GetScriptState(), std::move(input))
.Deserialize()
->IsTrue());
}
// Sanity check for transferring ReadableStreams. This is mostly tested via
// layout tests.
TEST(V8ScriptValueSerializerTest, RoundTripReadableStream) {
ScopedTransferableStreamsForTest enable_transferable_streams(true);
V8TestingScope scope;
auto* isolate = scope.GetIsolate();
auto* script_state = scope.GetScriptState();
auto* rs = ReadableStream::Create(script_state, ASSERT_NO_EXCEPTION);
v8::Local<v8::Value> wrapper = ToV8(rs, script_state);
Vector<ScriptValue> transferable_array = {ScriptValue(script_state, wrapper)};
Transferables transferables;
ASSERT_TRUE(SerializedScriptValue::ExtractTransferables(
isolate, transferable_array, transferables, ASSERT_NO_EXCEPTION));
v8::Local<v8::Value> result =
RoundTrip(wrapper, scope, &ASSERT_NO_EXCEPTION, &transferables);
EXPECT_TRUE(result->IsObject());
ASSERT_TRUE(V8ReadableStream::hasInstance(result, isolate));
ReadableStream* transferred =
V8ReadableStream::ToImpl(result.As<v8::Object>());
EXPECT_NE(rs, transferred);
EXPECT_TRUE(rs->locked(script_state, ASSERT_NO_EXCEPTION));
EXPECT_FALSE(transferred->locked(script_state, ASSERT_NO_EXCEPTION));
}
} // namespace
} // namespace blink