1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/memory/scoped_ptr.h"
8 #include "base/stl_util.h"
9 #include "base/test/values_test_util.h"
10 #include "base/values.h"
11 #include "content/child/v8_value_converter_impl.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "v8/include/v8.h"
17 // To improve the performance of
18 // V8ValueConverterImpl::UpdateAndCheckUniqueness, identity hashes of objects
19 // are used during checking for duplicates. For testing purposes we need to
20 // ignore the hash sometimes. Create this helper object to avoid using identity
21 // hashes for the lifetime of the helper.
22 class ScopedAvoidIdentityHashForTesting
{
24 // The hashes will be ignored in |converter|, which must not be NULL and it
25 // must outlive the created instance of this helper.
26 explicit ScopedAvoidIdentityHashForTesting(
27 content::V8ValueConverterImpl
* converter
);
28 ~ScopedAvoidIdentityHashForTesting();
31 content::V8ValueConverterImpl
* converter_
;
33 DISALLOW_COPY_AND_ASSIGN(ScopedAvoidIdentityHashForTesting
);
36 ScopedAvoidIdentityHashForTesting::ScopedAvoidIdentityHashForTesting(
37 content::V8ValueConverterImpl
* converter
)
38 : converter_(converter
) {
40 converter_
->avoid_identity_hash_for_testing_
= true;
43 ScopedAvoidIdentityHashForTesting::~ScopedAvoidIdentityHashForTesting() {
44 converter_
->avoid_identity_hash_for_testing_
= false;
47 class V8ValueConverterImplTest
: public testing::Test
{
49 V8ValueConverterImplTest()
50 : isolate_(v8::Isolate::GetCurrent()) {
54 void SetUp() override
{
55 v8::HandleScope
handle_scope(isolate_
);
56 v8::Local
<v8::ObjectTemplate
> global
= v8::ObjectTemplate::New(isolate_
);
57 context_
.Reset(isolate_
, v8::Context::New(isolate_
, NULL
, global
));
60 void TearDown() override
{ context_
.Reset(); }
62 std::string
GetString(base::DictionaryValue
* value
, const std::string
& key
) {
64 if (!value
->GetString(key
, &temp
)) {
71 std::string
GetString(v8::Local
<v8::Object
> value
, const std::string
& key
) {
72 v8::Local
<v8::String
> temp
=
73 value
->Get(v8::String::NewFromUtf8(isolate_
, key
.c_str()))
79 v8::String::Utf8Value
utf8(temp
);
80 return std::string(*utf8
, utf8
.length());
83 std::string
GetString(base::ListValue
* value
, uint32 index
) {
85 if (!value
->GetString(static_cast<size_t>(index
), &temp
)) {
92 std::string
GetString(v8::Local
<v8::Array
> value
, uint32 index
) {
93 v8::Local
<v8::String
> temp
= value
->Get(index
).As
<v8::String
>();
98 v8::String::Utf8Value
utf8(temp
);
99 return std::string(*utf8
, utf8
.length());
102 bool IsNull(base::DictionaryValue
* value
, const std::string
& key
) {
103 base::Value
* child
= NULL
;
104 if (!value
->Get(key
, &child
)) {
108 return child
->GetType() == base::Value::TYPE_NULL
;
111 bool IsNull(v8::Local
<v8::Object
> value
, const std::string
& key
) {
112 v8::Local
<v8::Value
> child
=
113 value
->Get(v8::String::NewFromUtf8(isolate_
, key
.c_str()));
114 if (child
.IsEmpty()) {
118 return child
->IsNull();
121 bool IsNull(base::ListValue
* value
, uint32 index
) {
122 base::Value
* child
= NULL
;
123 if (!value
->Get(static_cast<size_t>(index
), &child
)) {
127 return child
->GetType() == base::Value::TYPE_NULL
;
130 bool IsNull(v8::Local
<v8::Array
> value
, uint32 index
) {
131 v8::Local
<v8::Value
> child
= value
->Get(index
);
132 if (child
.IsEmpty()) {
136 return child
->IsNull();
139 void TestWeirdType(const V8ValueConverterImpl
& converter
,
140 v8::Local
<v8::Value
> val
,
141 base::Value::Type expected_type
,
142 scoped_ptr
<base::Value
> expected_value
) {
143 v8::Local
<v8::Context
> context
=
144 v8::Local
<v8::Context
>::New(isolate_
, context_
);
145 scoped_ptr
<base::Value
> raw(converter
.FromV8Value(val
, context
));
147 if (expected_value
) {
148 ASSERT_TRUE(raw
.get());
149 EXPECT_TRUE(expected_value
->Equals(raw
.get()));
150 EXPECT_EQ(expected_type
, raw
->GetType());
152 EXPECT_FALSE(raw
.get());
155 v8::Local
<v8::Object
> object(v8::Object::New(isolate_
));
156 object
->Set(v8::String::NewFromUtf8(isolate_
, "test"), val
);
157 scoped_ptr
<base::DictionaryValue
> dictionary(
158 static_cast<base::DictionaryValue
*>(
159 converter
.FromV8Value(object
, context
)));
160 ASSERT_TRUE(dictionary
.get());
162 if (expected_value
) {
163 base::Value
* temp
= NULL
;
164 ASSERT_TRUE(dictionary
->Get("test", &temp
));
165 EXPECT_EQ(expected_type
, temp
->GetType());
166 EXPECT_TRUE(expected_value
->Equals(temp
));
168 EXPECT_FALSE(dictionary
->HasKey("test"));
171 v8::Local
<v8::Array
> array(v8::Array::New(isolate_
));
173 scoped_ptr
<base::ListValue
> list(
174 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
175 ASSERT_TRUE(list
.get());
176 if (expected_value
) {
177 base::Value
* temp
= NULL
;
178 ASSERT_TRUE(list
->Get(0, &temp
));
179 EXPECT_EQ(expected_type
, temp
->GetType());
180 EXPECT_TRUE(expected_value
->Equals(temp
));
182 // Arrays should preserve their length, and convert unconvertible
184 base::Value
* temp
= NULL
;
185 ASSERT_TRUE(list
->Get(0, &temp
));
186 EXPECT_EQ(base::Value::TYPE_NULL
, temp
->GetType());
190 v8::Isolate
* isolate_
;
192 // Context for the JavaScript in the test.
193 v8::Persistent
<v8::Context
> context_
;
196 TEST_F(V8ValueConverterImplTest
, BasicRoundTrip
) {
197 scoped_ptr
<base::Value
> original_root
= base::test::ParseJson(
199 " \"null\": null, \n"
200 " \"true\": true, \n"
201 " \"false\": false, \n"
202 " \"positive-int\": 42, \n"
203 " \"negative-int\": -42, \n"
205 " \"double\": 88.8, \n"
206 " \"big-integral-double\": 9007199254740992.0, \n" // 2.0^53
207 " \"string\": \"foobar\", \n"
208 " \"empty-string\": \"\", \n"
209 " \"dictionary\": { \n"
210 " \"foo\": \"bar\",\n"
211 " \"hot\": \"dog\",\n"
213 " \"empty-dictionary\": {}, \n"
214 " \"list\": [ \"monkey\", \"balls\" ], \n"
215 " \"empty-list\": [], \n"
218 v8::HandleScope
handle_scope(isolate_
);
219 v8::Local
<v8::Context
> context
=
220 v8::Local
<v8::Context
>::New(isolate_
, context_
);
221 v8::Context::Scope
context_scope(context
);
223 V8ValueConverterImpl converter
;
224 v8::Local
<v8::Object
> v8_object
=
225 converter
.ToV8Value(original_root
.get(), context
).As
<v8::Object
>();
226 ASSERT_FALSE(v8_object
.IsEmpty());
228 EXPECT_EQ(static_cast<const base::DictionaryValue
&>(*original_root
).size(),
229 v8_object
->GetPropertyNames()->Length());
231 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "null"))->IsNull());
233 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "true"))->IsTrue());
235 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "false"))->IsFalse());
236 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "positive-int"))
238 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "negative-int"))
241 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "zero"))->IsInt32());
243 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "double"))->IsNumber());
244 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(
245 isolate_
, "big-integral-double"))->IsNumber());
247 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "string"))->IsString());
248 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "empty-string"))
250 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "dictionary"))
252 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(
253 isolate_
, "empty-dictionary"))->IsObject());
255 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "list"))->IsArray());
256 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "empty-list"))
259 scoped_ptr
<base::Value
> new_root(converter
.FromV8Value(v8_object
, context
));
260 EXPECT_NE(original_root
.get(), new_root
.get());
261 EXPECT_TRUE(original_root
->Equals(new_root
.get()));
264 TEST_F(V8ValueConverterImplTest
, KeysWithDots
) {
265 scoped_ptr
<base::Value
> original
=
266 base::test::ParseJson("{ \"foo.bar\": \"baz\" }");
268 v8::HandleScope
handle_scope(isolate_
);
269 v8::Local
<v8::Context
> context
=
270 v8::Local
<v8::Context
>::New(isolate_
, context_
);
271 v8::Context::Scope
context_scope(context
);
273 V8ValueConverterImpl converter
;
274 scoped_ptr
<base::Value
> copy(
275 converter
.FromV8Value(
276 converter
.ToV8Value(original
.get(), context
), context
));
278 EXPECT_TRUE(original
->Equals(copy
.get()));
281 TEST_F(V8ValueConverterImplTest
, ObjectExceptions
) {
282 v8::HandleScope
handle_scope(isolate_
);
283 v8::Local
<v8::Context
> context
=
284 v8::Local
<v8::Context
>::New(isolate_
, context_
);
285 v8::Context::Scope
context_scope(context
);
287 // Set up objects to throw when reading or writing 'foo'.
289 "Object.prototype.__defineSetter__('foo', "
290 " function() { throw new Error('muah!'); });"
291 "Object.prototype.__defineGetter__('foo', "
292 " function() { throw new Error('muah!'); });";
294 v8::Local
<v8::Script
> script(
295 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
298 v8::Local
<v8::Object
> object(v8::Object::New(isolate_
));
299 object
->Set(v8::String::NewFromUtf8(isolate_
, "bar"),
300 v8::String::NewFromUtf8(isolate_
, "bar"));
302 // Converting from v8 value should replace the foo property with null.
303 V8ValueConverterImpl converter
;
304 scoped_ptr
<base::DictionaryValue
> converted(
305 static_cast<base::DictionaryValue
*>(
306 converter
.FromV8Value(object
, context
)));
307 EXPECT_TRUE(converted
.get());
308 // http://code.google.com/p/v8/issues/detail?id=1342
309 // EXPECT_EQ(2u, converted->size());
310 // EXPECT_TRUE(IsNull(converted.get(), "foo"));
311 EXPECT_EQ(1u, converted
->size());
312 EXPECT_EQ("bar", GetString(converted
.get(), "bar"));
314 // Converting to v8 value should drop the foo property.
315 converted
->SetString("foo", "foo");
316 v8::Local
<v8::Object
> copy
=
317 converter
.ToV8Value(converted
.get(), context
).As
<v8::Object
>();
318 EXPECT_FALSE(copy
.IsEmpty());
319 EXPECT_EQ(2u, copy
->GetPropertyNames()->Length());
320 EXPECT_EQ("bar", GetString(copy
, "bar"));
323 TEST_F(V8ValueConverterImplTest
, ArrayExceptions
) {
324 v8::HandleScope
handle_scope(isolate_
);
325 v8::Local
<v8::Context
> context
=
326 v8::Local
<v8::Context
>::New(isolate_
, context_
);
327 v8::Context::Scope
context_scope(context
);
329 const char* source
= "(function() {"
331 "arr.__defineSetter__(0, "
332 " function() { throw new Error('muah!'); });"
333 "arr.__defineGetter__(0, "
334 " function() { throw new Error('muah!'); });"
339 v8::Local
<v8::Script
> script(
340 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
341 v8::Local
<v8::Array
> array
= script
->Run().As
<v8::Array
>();
342 ASSERT_FALSE(array
.IsEmpty());
344 // Converting from v8 value should replace the first item with null.
345 V8ValueConverterImpl converter
;
346 scoped_ptr
<base::ListValue
> converted(static_cast<base::ListValue
*>(
347 converter
.FromV8Value(array
, context
)));
348 ASSERT_TRUE(converted
.get());
349 // http://code.google.com/p/v8/issues/detail?id=1342
350 EXPECT_EQ(2u, converted
->GetSize());
351 EXPECT_TRUE(IsNull(converted
.get(), 0));
353 // Converting to v8 value should drop the first item and leave a hole.
354 converted
.reset(static_cast<base::ListValue
*>(
355 base::test::ParseJson("[ \"foo\", \"bar\" ]").release()));
356 v8::Local
<v8::Array
> copy
=
357 converter
.ToV8Value(converted
.get(), context
).As
<v8::Array
>();
358 ASSERT_FALSE(copy
.IsEmpty());
359 EXPECT_EQ(2u, copy
->Length());
360 EXPECT_EQ("bar", GetString(copy
, 1));
363 TEST_F(V8ValueConverterImplTest
, WeirdTypes
) {
364 v8::HandleScope
handle_scope(isolate_
);
365 v8::Local
<v8::Context
> context
=
366 v8::Local
<v8::Context
>::New(isolate_
, context_
);
367 v8::Context::Scope
context_scope(context
);
369 v8::Local
<v8::RegExp
> regex(v8::RegExp::New(
370 v8::String::NewFromUtf8(isolate_
, "."), v8::RegExp::kNone
));
372 V8ValueConverterImpl converter
;
373 TestWeirdType(converter
,
374 v8::Undefined(isolate_
),
375 base::Value::TYPE_NULL
, // Arbitrary type, result is NULL.
376 scoped_ptr
<base::Value
>());
377 TestWeirdType(converter
,
378 v8::Date::New(isolate_
, 1000),
379 base::Value::TYPE_DICTIONARY
,
380 scoped_ptr
<base::Value
>(new base::DictionaryValue()));
381 TestWeirdType(converter
,
383 base::Value::TYPE_DICTIONARY
,
384 scoped_ptr
<base::Value
>(new base::DictionaryValue()));
386 converter
.SetDateAllowed(true);
387 TestWeirdType(converter
,
388 v8::Date::New(isolate_
, 1000),
389 base::Value::TYPE_DOUBLE
,
390 scoped_ptr
<base::Value
>(new base::FundamentalValue(1.0)));
392 converter
.SetRegExpAllowed(true);
393 TestWeirdType(converter
,
395 base::Value::TYPE_STRING
,
396 scoped_ptr
<base::Value
>(new base::StringValue("/./")));
399 TEST_F(V8ValueConverterImplTest
, Prototype
) {
400 v8::HandleScope
handle_scope(isolate_
);
401 v8::Local
<v8::Context
> context
=
402 v8::Local
<v8::Context
>::New(isolate_
, context_
);
403 v8::Context::Scope
context_scope(context
);
405 const char* source
= "(function() {"
406 "Object.prototype.foo = 'foo';"
410 v8::Local
<v8::Script
> script(
411 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
412 v8::Local
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
413 ASSERT_FALSE(object
.IsEmpty());
415 V8ValueConverterImpl converter
;
416 scoped_ptr
<base::DictionaryValue
> result(
417 static_cast<base::DictionaryValue
*>(
418 converter
.FromV8Value(object
, context
)));
419 ASSERT_TRUE(result
.get());
420 EXPECT_EQ(0u, result
->size());
423 TEST_F(V8ValueConverterImplTest
, StripNullFromObjects
) {
424 v8::HandleScope
handle_scope(isolate_
);
425 v8::Local
<v8::Context
> context
=
426 v8::Local
<v8::Context
>::New(isolate_
, context_
);
427 v8::Context::Scope
context_scope(context
);
429 const char* source
= "(function() {"
430 "return { foo: undefined, bar: null };"
433 v8::Local
<v8::Script
> script(
434 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
435 v8::Local
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
436 ASSERT_FALSE(object
.IsEmpty());
438 V8ValueConverterImpl converter
;
439 converter
.SetStripNullFromObjects(true);
441 scoped_ptr
<base::DictionaryValue
> result(
442 static_cast<base::DictionaryValue
*>(
443 converter
.FromV8Value(object
, context
)));
444 ASSERT_TRUE(result
.get());
445 EXPECT_EQ(0u, result
->size());
448 TEST_F(V8ValueConverterImplTest
, RecursiveObjects
) {
449 v8::HandleScope
handle_scope(isolate_
);
450 v8::Local
<v8::Context
> context
=
451 v8::Local
<v8::Context
>::New(isolate_
, context_
);
452 v8::Context::Scope
context_scope(context
);
454 V8ValueConverterImpl converter
;
456 v8::Local
<v8::Object
> object
= v8::Object::New(isolate_
).As
<v8::Object
>();
457 ASSERT_FALSE(object
.IsEmpty());
458 object
->Set(v8::String::NewFromUtf8(isolate_
, "foo"),
459 v8::String::NewFromUtf8(isolate_
, "bar"));
460 object
->Set(v8::String::NewFromUtf8(isolate_
, "obj"), object
);
462 scoped_ptr
<base::DictionaryValue
> object_result(
463 static_cast<base::DictionaryValue
*>(
464 converter
.FromV8Value(object
, context
)));
465 ASSERT_TRUE(object_result
.get());
466 EXPECT_EQ(2u, object_result
->size());
467 EXPECT_TRUE(IsNull(object_result
.get(), "obj"));
469 v8::Local
<v8::Array
> array
= v8::Array::New(isolate_
).As
<v8::Array
>();
470 ASSERT_FALSE(array
.IsEmpty());
471 array
->Set(0, v8::String::NewFromUtf8(isolate_
, "1"));
472 array
->Set(1, array
);
474 scoped_ptr
<base::ListValue
> list_result(
475 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
476 ASSERT_TRUE(list_result
.get());
477 EXPECT_EQ(2u, list_result
->GetSize());
478 EXPECT_TRUE(IsNull(list_result
.get(), 1));
481 TEST_F(V8ValueConverterImplTest
, WeirdProperties
) {
482 v8::HandleScope
handle_scope(isolate_
);
483 v8::Local
<v8::Context
> context
=
484 v8::Local
<v8::Context
>::New(isolate_
, context_
);
485 v8::Context::Scope
context_scope(context
);
487 const char* source
= "(function() {"
498 v8::Local
<v8::Script
> script(
499 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
500 v8::Local
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
501 ASSERT_FALSE(object
.IsEmpty());
503 V8ValueConverterImpl converter
;
504 scoped_ptr
<base::Value
> actual(converter
.FromV8Value(object
, context
));
506 scoped_ptr
<base::Value
> expected
= base::test::ParseJson(
508 " \"1\": \"foo\", \n"
509 " \"2\": \"bar\", \n"
510 " \"true\": \"baz\", \n"
511 " \"false\": \"qux\", \n"
512 " \"null\": \"quux\", \n"
513 " \"undefined\": \"oops\", \n"
516 EXPECT_TRUE(expected
->Equals(actual
.get()));
519 TEST_F(V8ValueConverterImplTest
, ArrayGetters
) {
520 v8::HandleScope
handle_scope(isolate_
);
521 v8::Local
<v8::Context
> context
=
522 v8::Local
<v8::Context
>::New(isolate_
, context_
);
523 v8::Context::Scope
context_scope(context
);
525 const char* source
= "(function() {"
527 "a.__defineGetter__(1, function() { return 'bar'; });"
531 v8::Local
<v8::Script
> script(
532 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
533 v8::Local
<v8::Array
> array
= script
->Run().As
<v8::Array
>();
534 ASSERT_FALSE(array
.IsEmpty());
536 V8ValueConverterImpl converter
;
537 scoped_ptr
<base::ListValue
> result(
538 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
539 ASSERT_TRUE(result
.get());
540 EXPECT_EQ(2u, result
->GetSize());
543 TEST_F(V8ValueConverterImplTest
, UndefinedValueBehavior
) {
544 v8::HandleScope
handle_scope(isolate_
);
545 v8::Local
<v8::Context
> context
=
546 v8::Local
<v8::Context
>::New(isolate_
, context_
);
547 v8::Context::Scope
context_scope(context
);
549 v8::Local
<v8::Object
> object
;
551 const char* source
= "(function() {"
552 "return { foo: undefined, bar: null, baz: function(){} };"
554 v8::Local
<v8::Script
> script(
555 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
556 object
= script
->Run().As
<v8::Object
>();
557 ASSERT_FALSE(object
.IsEmpty());
560 v8::Local
<v8::Array
> array
;
562 const char* source
= "(function() {"
563 "return [ undefined, null, function(){} ];"
565 v8::Local
<v8::Script
> script(
566 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
567 array
= script
->Run().As
<v8::Array
>();
568 ASSERT_FALSE(array
.IsEmpty());
571 v8::Local
<v8::Array
> sparse_array
;
573 const char* source
= "(function() {"
574 "return new Array(3);"
576 v8::Local
<v8::Script
> script(
577 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
578 sparse_array
= script
->Run().As
<v8::Array
>();
579 ASSERT_FALSE(sparse_array
.IsEmpty());
582 V8ValueConverterImpl converter
;
584 scoped_ptr
<base::Value
> actual_object(
585 converter
.FromV8Value(object
, context
));
586 EXPECT_TRUE(base::Value::Equals(
587 base::test::ParseJson("{ \"bar\": null }").get(), actual_object
.get()));
589 // Everything is null because JSON stringification preserves array length.
590 scoped_ptr
<base::Value
> actual_array(converter
.FromV8Value(array
, context
));
591 EXPECT_TRUE(base::Value::Equals(
592 base::test::ParseJson("[ null, null, null ]").get(), actual_array
.get()));
594 scoped_ptr
<base::Value
> actual_sparse_array(
595 converter
.FromV8Value(sparse_array
, context
));
597 base::Value::Equals(base::test::ParseJson("[ null, null, null ]").get(),
598 actual_sparse_array
.get()));
601 TEST_F(V8ValueConverterImplTest
, ObjectsWithClashingIdentityHash
) {
602 v8::HandleScope
handle_scope(isolate_
);
603 v8::Local
<v8::Context
> context
=
604 v8::Local
<v8::Context
>::New(isolate_
, context_
);
605 v8::Context::Scope
context_scope(context
);
606 V8ValueConverterImpl converter
;
608 // We check that the converter checks identity correctly by disabling the
609 // optimization of using identity hashes.
610 ScopedAvoidIdentityHashForTesting
scoped_hash_avoider(&converter
);
612 // Create the v8::Object to be converted.
613 v8::Local
<v8::Array
> root(v8::Array::New(isolate_
, 4));
614 root
->Set(0, v8::Local
<v8::Object
>(v8::Object::New(isolate_
)));
615 root
->Set(1, v8::Local
<v8::Object
>(v8::Object::New(isolate_
)));
616 root
->Set(2, v8::Local
<v8::Object
>(v8::Array::New(isolate_
, 0)));
617 root
->Set(3, v8::Local
<v8::Object
>(v8::Array::New(isolate_
, 0)));
619 // The expected base::Value result.
620 scoped_ptr
<base::Value
> expected
= base::test::ParseJson("[{},{},[],[]]");
621 ASSERT_TRUE(expected
.get());
623 // The actual result.
624 scoped_ptr
<base::Value
> value(converter
.FromV8Value(root
, context
));
625 ASSERT_TRUE(value
.get());
627 EXPECT_TRUE(expected
->Equals(value
.get()));
630 TEST_F(V8ValueConverterImplTest
, DetectCycles
) {
631 v8::HandleScope
handle_scope(isolate_
);
632 v8::Local
<v8::Context
> context
=
633 v8::Local
<v8::Context
>::New(isolate_
, context_
);
634 v8::Context::Scope
context_scope(context
);
635 V8ValueConverterImpl converter
;
637 // Create a recursive array.
638 v8::Local
<v8::Array
> recursive_array(v8::Array::New(isolate_
, 1));
639 recursive_array
->Set(0, recursive_array
);
641 // The first repetition should be trimmed and replaced by a null value.
642 base::ListValue expected_list
;
643 expected_list
.Append(base::Value::CreateNullValue());
645 // The actual result.
646 scoped_ptr
<base::Value
> actual_list(
647 converter
.FromV8Value(recursive_array
, context
));
648 ASSERT_TRUE(actual_list
.get());
650 EXPECT_TRUE(expected_list
.Equals(actual_list
.get()));
652 // Now create a recursive object
653 const std::string
key("key");
654 v8::Local
<v8::Object
> recursive_object(v8::Object::New(isolate_
));
655 v8::TryCatch try_catch
;
656 recursive_object
->Set(
657 v8::String::NewFromUtf8(
658 isolate_
, key
.c_str(), v8::String::kNormalString
, key
.length()),
660 ASSERT_FALSE(try_catch
.HasCaught());
662 // The first repetition should be trimmed and replaced by a null value.
663 base::DictionaryValue expected_dictionary
;
664 expected_dictionary
.Set(key
, base::Value::CreateNullValue());
666 // The actual result.
667 scoped_ptr
<base::Value
> actual_dictionary(
668 converter
.FromV8Value(recursive_object
, context
));
669 ASSERT_TRUE(actual_dictionary
.get());
671 EXPECT_TRUE(expected_dictionary
.Equals(actual_dictionary
.get()));
674 TEST_F(V8ValueConverterImplTest
, MaxRecursionDepth
) {
675 v8::HandleScope
handle_scope(isolate_
);
676 v8::Local
<v8::Context
> context
=
677 v8::Local
<v8::Context
>::New(isolate_
, context_
);
678 v8::Context::Scope
context_scope(context
);
680 // Must larger than kMaxRecursionDepth in v8_value_converter_impl.cc.
682 const char kKey
[] = "key";
684 v8::Local
<v8::Object
> deep_object
= v8::Object::New(isolate_
);
686 v8::Local
<v8::Object
> leaf
= deep_object
;
687 for (int i
= 0; i
< kDepth
; ++i
) {
688 v8::Local
<v8::Object
> new_object
= v8::Object::New(isolate_
);
689 leaf
->Set(v8::String::NewFromUtf8(isolate_
, kKey
), new_object
);
693 V8ValueConverterImpl converter
;
694 scoped_ptr
<base::Value
> value(converter
.FromV8Value(deep_object
, context
));
697 // Expected depth is kMaxRecursionDepth in v8_value_converter_impl.cc.
698 int kExpectedDepth
= 100;
700 base::Value
* current
= value
.get();
701 for (int i
= 1; i
< kExpectedDepth
; ++i
) {
702 base::DictionaryValue
* current_as_object
= NULL
;
703 ASSERT_TRUE(current
->GetAsDictionary(¤t_as_object
)) << i
;
704 ASSERT_TRUE(current_as_object
->Get(kKey
, ¤t
)) << i
;
707 // The leaf node shouldn't have any properties.
708 base::DictionaryValue empty
;
709 EXPECT_TRUE(base::Value::Equals(&empty
, current
)) << *current
;
712 class V8ValueConverterOverridingStrategyForTesting
713 : public V8ValueConverter::Strategy
{
715 V8ValueConverterOverridingStrategyForTesting()
716 : reference_value_(NewReferenceValue()) {}
717 bool FromV8Object(v8::Local
<v8::Object
> value
,
719 v8::Isolate
* isolate
,
720 const FromV8ValueCallback
& callback
) const override
{
721 *out
= NewReferenceValue();
724 bool FromV8Array(v8::Local
<v8::Array
> value
,
726 v8::Isolate
* isolate
,
727 const FromV8ValueCallback
& callback
) const override
{
728 *out
= NewReferenceValue();
731 bool FromV8ArrayBuffer(v8::Local
<v8::Object
> value
,
733 v8::Isolate
* isolate
) const override
{
734 *out
= NewReferenceValue();
737 bool FromV8Number(v8::Local
<v8::Number
> value
,
738 base::Value
** out
) const override
{
739 *out
= NewReferenceValue();
742 bool FromV8Undefined(base::Value
** out
) const override
{
743 *out
= NewReferenceValue();
746 base::Value
* reference_value() const { return reference_value_
.get(); }
749 static base::Value
* NewReferenceValue() {
750 return new base::StringValue("strategy");
752 scoped_ptr
<base::Value
> reference_value_
;
755 TEST_F(V8ValueConverterImplTest
, StrategyOverrides
) {
756 v8::HandleScope
handle_scope(isolate_
);
757 v8::Local
<v8::Context
> context
=
758 v8::Local
<v8::Context
>::New(isolate_
, context_
);
759 v8::Context::Scope
context_scope(context
);
761 V8ValueConverterImpl converter
;
762 V8ValueConverterOverridingStrategyForTesting strategy
;
763 converter
.SetStrategy(&strategy
);
765 v8::Local
<v8::Object
> object(v8::Object::New(isolate_
));
766 scoped_ptr
<base::Value
> object_value(converter
.FromV8Value(object
, context
));
767 ASSERT_TRUE(object_value
);
769 base::Value::Equals(strategy
.reference_value(), object_value
.get()));
771 v8::Local
<v8::Array
> array(v8::Array::New(isolate_
));
772 scoped_ptr
<base::Value
> array_value(converter
.FromV8Value(array
, context
));
773 ASSERT_TRUE(array_value
);
775 base::Value::Equals(strategy
.reference_value(), array_value
.get()));
777 v8::Local
<v8::ArrayBuffer
> array_buffer(v8::ArrayBuffer::New(isolate_
, 0));
778 scoped_ptr
<base::Value
> array_buffer_value(
779 converter
.FromV8Value(array_buffer
, context
));
780 ASSERT_TRUE(array_buffer_value
);
781 EXPECT_TRUE(base::Value::Equals(strategy
.reference_value(),
782 array_buffer_value
.get()));
784 v8::Local
<v8::ArrayBufferView
> array_buffer_view(
785 v8::Uint8Array::New(array_buffer
, 0, 0));
786 scoped_ptr
<base::Value
> array_buffer_view_value(
787 converter
.FromV8Value(array_buffer_view
, context
));
788 ASSERT_TRUE(array_buffer_view_value
);
789 EXPECT_TRUE(base::Value::Equals(strategy
.reference_value(),
790 array_buffer_view_value
.get()));
792 v8::Local
<v8::Number
> number(v8::Number::New(isolate_
, 0.0));
793 scoped_ptr
<base::Value
> number_value(converter
.FromV8Value(number
, context
));
794 ASSERT_TRUE(number_value
);
796 base::Value::Equals(strategy
.reference_value(), number_value
.get()));
798 v8::Local
<v8::Primitive
> undefined(v8::Undefined(isolate_
));
799 scoped_ptr
<base::Value
> undefined_value(
800 converter
.FromV8Value(undefined
, context
));
801 ASSERT_TRUE(undefined_value
);
803 base::Value::Equals(strategy
.reference_value(), undefined_value
.get()));
806 class V8ValueConverterBypassStrategyForTesting
807 : public V8ValueConverter::Strategy
{
809 bool FromV8Object(v8::Local
<v8::Object
> value
,
811 v8::Isolate
* isolate
,
812 const FromV8ValueCallback
& callback
) const override
{
815 bool FromV8Array(v8::Local
<v8::Array
> value
,
817 v8::Isolate
* isolate
,
818 const FromV8ValueCallback
& callback
) const override
{
821 bool FromV8ArrayBuffer(v8::Local
<v8::Object
> value
,
823 v8::Isolate
* isolate
) const override
{
826 bool FromV8Number(v8::Local
<v8::Number
> value
,
827 base::Value
** out
) const override
{
830 bool FromV8Undefined(base::Value
** out
) const override
{ return false; }
833 // Verify that having a strategy that fallbacks to default behaviour
834 // actually preserves it.
835 TEST_F(V8ValueConverterImplTest
, StrategyBypass
) {
836 v8::HandleScope
handle_scope(isolate_
);
837 v8::Local
<v8::Context
> context
=
838 v8::Local
<v8::Context
>::New(isolate_
, context_
);
839 v8::Context::Scope
context_scope(context
);
841 V8ValueConverterImpl converter
;
842 V8ValueConverterBypassStrategyForTesting strategy
;
843 converter
.SetStrategy(&strategy
);
845 v8::Local
<v8::Object
> object(v8::Object::New(isolate_
));
846 scoped_ptr
<base::Value
> object_value(converter
.FromV8Value(object
, context
));
847 ASSERT_TRUE(object_value
);
848 scoped_ptr
<base::Value
> reference_object_value(base::test::ParseJson("{}"));
850 base::Value::Equals(reference_object_value
.get(), object_value
.get()));
852 v8::Local
<v8::Array
> array(v8::Array::New(isolate_
));
853 scoped_ptr
<base::Value
> array_value(converter
.FromV8Value(array
, context
));
854 ASSERT_TRUE(array_value
);
855 scoped_ptr
<base::Value
> reference_array_value(base::test::ParseJson("[]"));
857 base::Value::Equals(reference_array_value
.get(), array_value
.get()));
859 // Not testing ArrayBuffers as V8ValueConverter uses blink helpers and
860 // this requires having blink to be initialized.
862 v8::Local
<v8::Number
> number(v8::Number::New(isolate_
, 0.0));
863 scoped_ptr
<base::Value
> number_value(converter
.FromV8Value(number
, context
));
864 ASSERT_TRUE(number_value
);
865 scoped_ptr
<base::Value
> reference_number_value(base::test::ParseJson("0"));
867 base::Value::Equals(reference_number_value
.get(), number_value
.get()));
869 v8::Local
<v8::Primitive
> undefined(v8::Undefined(isolate_
));
870 scoped_ptr
<base::Value
> undefined_value(
871 converter
.FromV8Value(undefined
, context
));
872 EXPECT_FALSE(undefined_value
);
875 } // namespace content