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/renderer/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 virtual void SetUp() {
55 v8::HandleScope
handle_scope(isolate_
);
56 v8::Handle
<v8::ObjectTemplate
> global
= v8::ObjectTemplate::New(isolate_
);
57 context_
.Reset(isolate_
, v8::Context::New(isolate_
, NULL
, global
));
60 virtual void TearDown() {
64 std::string
GetString(base::DictionaryValue
* value
, const std::string
& key
) {
66 if (!value
->GetString(key
, &temp
)) {
73 std::string
GetString(v8::Handle
<v8::Object
> value
, const std::string
& key
) {
74 v8::Handle
<v8::String
> temp
=
75 value
->Get(v8::String::NewFromUtf8(isolate_
, key
.c_str()))
81 v8::String::Utf8Value
utf8(temp
);
82 return std::string(*utf8
, utf8
.length());
85 std::string
GetString(base::ListValue
* value
, uint32 index
) {
87 if (!value
->GetString(static_cast<size_t>(index
), &temp
)) {
94 std::string
GetString(v8::Handle
<v8::Array
> value
, uint32 index
) {
95 v8::Handle
<v8::String
> temp
= value
->Get(index
).As
<v8::String
>();
100 v8::String::Utf8Value
utf8(temp
);
101 return std::string(*utf8
, utf8
.length());
104 bool IsNull(base::DictionaryValue
* value
, const std::string
& key
) {
105 base::Value
* child
= NULL
;
106 if (!value
->Get(key
, &child
)) {
110 return child
->GetType() == base::Value::TYPE_NULL
;
113 bool IsNull(v8::Handle
<v8::Object
> value
, const std::string
& key
) {
114 v8::Handle
<v8::Value
> child
=
115 value
->Get(v8::String::NewFromUtf8(isolate_
, key
.c_str()));
116 if (child
.IsEmpty()) {
120 return child
->IsNull();
123 bool IsNull(base::ListValue
* value
, uint32 index
) {
124 base::Value
* child
= NULL
;
125 if (!value
->Get(static_cast<size_t>(index
), &child
)) {
129 return child
->GetType() == base::Value::TYPE_NULL
;
132 bool IsNull(v8::Handle
<v8::Array
> value
, uint32 index
) {
133 v8::Handle
<v8::Value
> child
= value
->Get(index
);
134 if (child
.IsEmpty()) {
138 return child
->IsNull();
141 void TestWeirdType(const V8ValueConverterImpl
& converter
,
142 v8::Handle
<v8::Value
> val
,
143 base::Value::Type expected_type
,
144 scoped_ptr
<base::Value
> expected_value
) {
145 v8::Local
<v8::Context
> context
=
146 v8::Local
<v8::Context
>::New(isolate_
, context_
);
147 scoped_ptr
<base::Value
> raw(converter
.FromV8Value(val
, context
));
149 if (expected_value
) {
150 ASSERT_TRUE(raw
.get());
151 EXPECT_TRUE(expected_value
->Equals(raw
.get()));
152 EXPECT_EQ(expected_type
, raw
->GetType());
154 EXPECT_FALSE(raw
.get());
157 v8::Handle
<v8::Object
> object(v8::Object::New(isolate_
));
158 object
->Set(v8::String::NewFromUtf8(isolate_
, "test"), val
);
159 scoped_ptr
<base::DictionaryValue
> dictionary(
160 static_cast<base::DictionaryValue
*>(
161 converter
.FromV8Value(object
, context
)));
162 ASSERT_TRUE(dictionary
.get());
164 if (expected_value
) {
165 base::Value
* temp
= NULL
;
166 ASSERT_TRUE(dictionary
->Get("test", &temp
));
167 EXPECT_EQ(expected_type
, temp
->GetType());
168 EXPECT_TRUE(expected_value
->Equals(temp
));
170 EXPECT_FALSE(dictionary
->HasKey("test"));
173 v8::Handle
<v8::Array
> array(v8::Array::New(isolate_
));
175 scoped_ptr
<base::ListValue
> list(
176 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
177 ASSERT_TRUE(list
.get());
178 if (expected_value
) {
179 base::Value
* temp
= NULL
;
180 ASSERT_TRUE(list
->Get(0, &temp
));
181 EXPECT_EQ(expected_type
, temp
->GetType());
182 EXPECT_TRUE(expected_value
->Equals(temp
));
184 // Arrays should preserve their length, and convert unconvertible
186 base::Value
* temp
= NULL
;
187 ASSERT_TRUE(list
->Get(0, &temp
));
188 EXPECT_EQ(base::Value::TYPE_NULL
, temp
->GetType());
192 v8::Isolate
* isolate_
;
194 // Context for the JavaScript in the test.
195 v8::Persistent
<v8::Context
> context_
;
198 TEST_F(V8ValueConverterImplTest
, BasicRoundTrip
) {
199 scoped_ptr
<base::Value
> original_root
= base::test::ParseJson(
201 " \"null\": null, \n"
202 " \"true\": true, \n"
203 " \"false\": false, \n"
204 " \"positive-int\": 42, \n"
205 " \"negative-int\": -42, \n"
207 " \"double\": 88.8, \n"
208 " \"big-integral-double\": 9007199254740992.0, \n" // 2.0^53
209 " \"string\": \"foobar\", \n"
210 " \"empty-string\": \"\", \n"
211 " \"dictionary\": { \n"
212 " \"foo\": \"bar\",\n"
213 " \"hot\": \"dog\",\n"
215 " \"empty-dictionary\": {}, \n"
216 " \"list\": [ \"monkey\", \"balls\" ], \n"
217 " \"empty-list\": [], \n"
220 v8::HandleScope
handle_scope(isolate_
);
221 v8::Local
<v8::Context
> context
=
222 v8::Local
<v8::Context
>::New(isolate_
, context_
);
223 v8::Context::Scope
context_scope(context
);
225 V8ValueConverterImpl converter
;
226 v8::Handle
<v8::Object
> v8_object
=
227 converter
.ToV8Value(original_root
.get(), context
).As
<v8::Object
>();
228 ASSERT_FALSE(v8_object
.IsEmpty());
230 EXPECT_EQ(static_cast<const base::DictionaryValue
&>(*original_root
).size(),
231 v8_object
->GetPropertyNames()->Length());
233 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "null"))->IsNull());
235 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "true"))->IsTrue());
237 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "false"))->IsFalse());
238 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "positive-int"))
240 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "negative-int"))
243 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "zero"))->IsInt32());
245 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "double"))->IsNumber());
246 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(
247 isolate_
, "big-integral-double"))->IsNumber());
249 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "string"))->IsString());
250 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "empty-string"))
252 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "dictionary"))
254 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(
255 isolate_
, "empty-dictionary"))->IsObject());
257 v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "list"))->IsArray());
258 EXPECT_TRUE(v8_object
->Get(v8::String::NewFromUtf8(isolate_
, "empty-list"))
261 scoped_ptr
<base::Value
> new_root(converter
.FromV8Value(v8_object
, context
));
262 EXPECT_NE(original_root
.get(), new_root
.get());
263 EXPECT_TRUE(original_root
->Equals(new_root
.get()));
266 TEST_F(V8ValueConverterImplTest
, KeysWithDots
) {
267 scoped_ptr
<base::Value
> original
=
268 base::test::ParseJson("{ \"foo.bar\": \"baz\" }");
270 v8::HandleScope
handle_scope(isolate_
);
271 v8::Local
<v8::Context
> context
=
272 v8::Local
<v8::Context
>::New(isolate_
, context_
);
273 v8::Context::Scope
context_scope(context
);
275 V8ValueConverterImpl converter
;
276 scoped_ptr
<base::Value
> copy(
277 converter
.FromV8Value(
278 converter
.ToV8Value(original
.get(), context
), context
));
280 EXPECT_TRUE(original
->Equals(copy
.get()));
283 TEST_F(V8ValueConverterImplTest
, ObjectExceptions
) {
284 v8::HandleScope
handle_scope(isolate_
);
285 v8::Local
<v8::Context
> context
=
286 v8::Local
<v8::Context
>::New(isolate_
, context_
);
287 v8::Context::Scope
context_scope(context
);
289 // Set up objects to throw when reading or writing 'foo'.
291 "Object.prototype.__defineSetter__('foo', "
292 " function() { throw new Error('muah!'); });"
293 "Object.prototype.__defineGetter__('foo', "
294 " function() { throw new Error('muah!'); });";
296 v8::Handle
<v8::Script
> script(
297 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
300 v8::Handle
<v8::Object
> object(v8::Object::New(isolate_
));
301 object
->Set(v8::String::NewFromUtf8(isolate_
, "bar"),
302 v8::String::NewFromUtf8(isolate_
, "bar"));
304 // Converting from v8 value should replace the foo property with null.
305 V8ValueConverterImpl converter
;
306 scoped_ptr
<base::DictionaryValue
> converted(
307 static_cast<base::DictionaryValue
*>(
308 converter
.FromV8Value(object
, context
)));
309 EXPECT_TRUE(converted
.get());
310 // http://code.google.com/p/v8/issues/detail?id=1342
311 // EXPECT_EQ(2u, converted->size());
312 // EXPECT_TRUE(IsNull(converted.get(), "foo"));
313 EXPECT_EQ(1u, converted
->size());
314 EXPECT_EQ("bar", GetString(converted
.get(), "bar"));
316 // Converting to v8 value should drop the foo property.
317 converted
->SetString("foo", "foo");
318 v8::Handle
<v8::Object
> copy
=
319 converter
.ToV8Value(converted
.get(), context
).As
<v8::Object
>();
320 EXPECT_FALSE(copy
.IsEmpty());
321 EXPECT_EQ(2u, copy
->GetPropertyNames()->Length());
322 EXPECT_EQ("bar", GetString(copy
, "bar"));
325 TEST_F(V8ValueConverterImplTest
, ArrayExceptions
) {
326 v8::HandleScope
handle_scope(isolate_
);
327 v8::Local
<v8::Context
> context
=
328 v8::Local
<v8::Context
>::New(isolate_
, context_
);
329 v8::Context::Scope
context_scope(context
);
331 const char* source
= "(function() {"
333 "arr.__defineSetter__(0, "
334 " function() { throw new Error('muah!'); });"
335 "arr.__defineGetter__(0, "
336 " function() { throw new Error('muah!'); });"
341 v8::Handle
<v8::Script
> script(
342 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
343 v8::Handle
<v8::Array
> array
= script
->Run().As
<v8::Array
>();
344 ASSERT_FALSE(array
.IsEmpty());
346 // Converting from v8 value should replace the first item with null.
347 V8ValueConverterImpl converter
;
348 scoped_ptr
<base::ListValue
> converted(static_cast<base::ListValue
*>(
349 converter
.FromV8Value(array
, context
)));
350 ASSERT_TRUE(converted
.get());
351 // http://code.google.com/p/v8/issues/detail?id=1342
352 EXPECT_EQ(2u, converted
->GetSize());
353 EXPECT_TRUE(IsNull(converted
.get(), 0));
355 // Converting to v8 value should drop the first item and leave a hole.
356 converted
.reset(static_cast<base::ListValue
*>(
357 base::test::ParseJson("[ \"foo\", \"bar\" ]").release()));
358 v8::Handle
<v8::Array
> copy
=
359 converter
.ToV8Value(converted
.get(), context
).As
<v8::Array
>();
360 ASSERT_FALSE(copy
.IsEmpty());
361 EXPECT_EQ(2u, copy
->Length());
362 EXPECT_EQ("bar", GetString(copy
, 1));
365 TEST_F(V8ValueConverterImplTest
, WeirdTypes
) {
366 v8::HandleScope
handle_scope(isolate_
);
367 v8::Local
<v8::Context
> context
=
368 v8::Local
<v8::Context
>::New(isolate_
, context_
);
369 v8::Context::Scope
context_scope(context
);
371 v8::Handle
<v8::RegExp
> regex(v8::RegExp::New(
372 v8::String::NewFromUtf8(isolate_
, "."), v8::RegExp::kNone
));
374 V8ValueConverterImpl converter
;
375 TestWeirdType(converter
,
376 v8::Undefined(isolate_
),
377 base::Value::TYPE_NULL
, // Arbitrary type, result is NULL.
378 scoped_ptr
<base::Value
>());
379 TestWeirdType(converter
,
380 v8::Date::New(isolate_
, 1000),
381 base::Value::TYPE_DICTIONARY
,
382 scoped_ptr
<base::Value
>(new base::DictionaryValue()));
383 TestWeirdType(converter
,
385 base::Value::TYPE_DICTIONARY
,
386 scoped_ptr
<base::Value
>(new base::DictionaryValue()));
388 converter
.SetDateAllowed(true);
389 TestWeirdType(converter
,
390 v8::Date::New(isolate_
, 1000),
391 base::Value::TYPE_DOUBLE
,
392 scoped_ptr
<base::Value
>(new base::FundamentalValue(1.0)));
394 converter
.SetRegExpAllowed(true);
395 TestWeirdType(converter
,
397 base::Value::TYPE_STRING
,
398 scoped_ptr
<base::Value
>(new base::StringValue("/./")));
401 TEST_F(V8ValueConverterImplTest
, Prototype
) {
402 v8::HandleScope
handle_scope(isolate_
);
403 v8::Local
<v8::Context
> context
=
404 v8::Local
<v8::Context
>::New(isolate_
, context_
);
405 v8::Context::Scope
context_scope(context
);
407 const char* source
= "(function() {"
408 "Object.prototype.foo = 'foo';"
412 v8::Handle
<v8::Script
> script(
413 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
414 v8::Handle
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
415 ASSERT_FALSE(object
.IsEmpty());
417 V8ValueConverterImpl converter
;
418 scoped_ptr
<base::DictionaryValue
> result(
419 static_cast<base::DictionaryValue
*>(
420 converter
.FromV8Value(object
, context
)));
421 ASSERT_TRUE(result
.get());
422 EXPECT_EQ(0u, result
->size());
425 TEST_F(V8ValueConverterImplTest
, StripNullFromObjects
) {
426 v8::HandleScope
handle_scope(isolate_
);
427 v8::Local
<v8::Context
> context
=
428 v8::Local
<v8::Context
>::New(isolate_
, context_
);
429 v8::Context::Scope
context_scope(context
);
431 const char* source
= "(function() {"
432 "return { foo: undefined, bar: null };"
435 v8::Handle
<v8::Script
> script(
436 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
437 v8::Handle
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
438 ASSERT_FALSE(object
.IsEmpty());
440 V8ValueConverterImpl converter
;
441 converter
.SetStripNullFromObjects(true);
443 scoped_ptr
<base::DictionaryValue
> result(
444 static_cast<base::DictionaryValue
*>(
445 converter
.FromV8Value(object
, context
)));
446 ASSERT_TRUE(result
.get());
447 EXPECT_EQ(0u, result
->size());
450 TEST_F(V8ValueConverterImplTest
, RecursiveObjects
) {
451 v8::HandleScope
handle_scope(isolate_
);
452 v8::Local
<v8::Context
> context
=
453 v8::Local
<v8::Context
>::New(isolate_
, context_
);
454 v8::Context::Scope
context_scope(context
);
456 V8ValueConverterImpl converter
;
458 v8::Handle
<v8::Object
> object
= v8::Object::New(isolate_
).As
<v8::Object
>();
459 ASSERT_FALSE(object
.IsEmpty());
460 object
->Set(v8::String::NewFromUtf8(isolate_
, "foo"),
461 v8::String::NewFromUtf8(isolate_
, "bar"));
462 object
->Set(v8::String::NewFromUtf8(isolate_
, "obj"), object
);
464 scoped_ptr
<base::DictionaryValue
> object_result(
465 static_cast<base::DictionaryValue
*>(
466 converter
.FromV8Value(object
, context
)));
467 ASSERT_TRUE(object_result
.get());
468 EXPECT_EQ(2u, object_result
->size());
469 EXPECT_TRUE(IsNull(object_result
.get(), "obj"));
471 v8::Handle
<v8::Array
> array
= v8::Array::New(isolate_
).As
<v8::Array
>();
472 ASSERT_FALSE(array
.IsEmpty());
473 array
->Set(0, v8::String::NewFromUtf8(isolate_
, "1"));
474 array
->Set(1, array
);
476 scoped_ptr
<base::ListValue
> list_result(
477 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
478 ASSERT_TRUE(list_result
.get());
479 EXPECT_EQ(2u, list_result
->GetSize());
480 EXPECT_TRUE(IsNull(list_result
.get(), 1));
483 TEST_F(V8ValueConverterImplTest
, WeirdProperties
) {
484 v8::HandleScope
handle_scope(isolate_
);
485 v8::Local
<v8::Context
> context
=
486 v8::Local
<v8::Context
>::New(isolate_
, context_
);
487 v8::Context::Scope
context_scope(context
);
489 const char* source
= "(function() {"
500 v8::Handle
<v8::Script
> script(
501 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
502 v8::Handle
<v8::Object
> object
= script
->Run().As
<v8::Object
>();
503 ASSERT_FALSE(object
.IsEmpty());
505 V8ValueConverterImpl converter
;
506 scoped_ptr
<base::Value
> actual(converter
.FromV8Value(object
, context
));
508 scoped_ptr
<base::Value
> expected
= base::test::ParseJson(
510 " \"1\": \"foo\", \n"
511 " \"2\": \"bar\", \n"
512 " \"true\": \"baz\", \n"
513 " \"false\": \"qux\", \n"
514 " \"null\": \"quux\", \n"
515 " \"undefined\": \"oops\", \n"
518 EXPECT_TRUE(expected
->Equals(actual
.get()));
521 TEST_F(V8ValueConverterImplTest
, ArrayGetters
) {
522 v8::HandleScope
handle_scope(isolate_
);
523 v8::Local
<v8::Context
> context
=
524 v8::Local
<v8::Context
>::New(isolate_
, context_
);
525 v8::Context::Scope
context_scope(context
);
527 const char* source
= "(function() {"
529 "a.__defineGetter__(1, function() { return 'bar'; });"
533 v8::Handle
<v8::Script
> script(
534 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
535 v8::Handle
<v8::Array
> array
= script
->Run().As
<v8::Array
>();
536 ASSERT_FALSE(array
.IsEmpty());
538 V8ValueConverterImpl converter
;
539 scoped_ptr
<base::ListValue
> result(
540 static_cast<base::ListValue
*>(converter
.FromV8Value(array
, context
)));
541 ASSERT_TRUE(result
.get());
542 EXPECT_EQ(2u, result
->GetSize());
545 TEST_F(V8ValueConverterImplTest
, UndefinedValueBehavior
) {
546 v8::HandleScope
handle_scope(isolate_
);
547 v8::Local
<v8::Context
> context
=
548 v8::Local
<v8::Context
>::New(isolate_
, context_
);
549 v8::Context::Scope
context_scope(context
);
551 v8::Handle
<v8::Object
> object
;
553 const char* source
= "(function() {"
554 "return { foo: undefined, bar: null, baz: function(){} };"
556 v8::Handle
<v8::Script
> script(
557 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
558 object
= script
->Run().As
<v8::Object
>();
559 ASSERT_FALSE(object
.IsEmpty());
562 v8::Handle
<v8::Array
> array
;
564 const char* source
= "(function() {"
565 "return [ undefined, null, function(){} ];"
567 v8::Handle
<v8::Script
> script(
568 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
569 array
= script
->Run().As
<v8::Array
>();
570 ASSERT_FALSE(array
.IsEmpty());
573 v8::Handle
<v8::Array
> sparse_array
;
575 const char* source
= "(function() {"
576 "return new Array(3);"
578 v8::Handle
<v8::Script
> script(
579 v8::Script::Compile(v8::String::NewFromUtf8(isolate_
, source
)));
580 sparse_array
= script
->Run().As
<v8::Array
>();
581 ASSERT_FALSE(sparse_array
.IsEmpty());
584 V8ValueConverterImpl converter
;
586 scoped_ptr
<base::Value
> actual_object(
587 converter
.FromV8Value(object
, context
));
588 EXPECT_TRUE(base::Value::Equals(
589 base::test::ParseJson("{ \"bar\": null }").get(), actual_object
.get()));
591 // Everything is null because JSON stringification preserves array length.
592 scoped_ptr
<base::Value
> actual_array(converter
.FromV8Value(array
, context
));
593 EXPECT_TRUE(base::Value::Equals(
594 base::test::ParseJson("[ null, null, null ]").get(), actual_array
.get()));
596 scoped_ptr
<base::Value
> actual_sparse_array(
597 converter
.FromV8Value(sparse_array
, context
));
599 base::Value::Equals(base::test::ParseJson("[ null, null, null ]").get(),
600 actual_sparse_array
.get()));
603 TEST_F(V8ValueConverterImplTest
, ObjectsWithClashingIdentityHash
) {
604 v8::HandleScope
handle_scope(isolate_
);
605 v8::Local
<v8::Context
> context
=
606 v8::Local
<v8::Context
>::New(isolate_
, context_
);
607 v8::Context::Scope
context_scope(context
);
608 V8ValueConverterImpl converter
;
610 // We check that the converter checks identity correctly by disabling the
611 // optimization of using identity hashes.
612 ScopedAvoidIdentityHashForTesting
scoped_hash_avoider(&converter
);
614 // Create the v8::Object to be converted.
615 v8::Handle
<v8::Array
> root(v8::Array::New(isolate_
, 4));
616 root
->Set(0, v8::Handle
<v8::Object
>(v8::Object::New(isolate_
)));
617 root
->Set(1, v8::Handle
<v8::Object
>(v8::Object::New(isolate_
)));
618 root
->Set(2, v8::Handle
<v8::Object
>(v8::Array::New(isolate_
, 0)));
619 root
->Set(3, v8::Handle
<v8::Object
>(v8::Array::New(isolate_
, 0)));
621 // The expected base::Value result.
622 scoped_ptr
<base::Value
> expected
= base::test::ParseJson("[{},{},[],[]]");
623 ASSERT_TRUE(expected
.get());
625 // The actual result.
626 scoped_ptr
<base::Value
> value(converter
.FromV8Value(root
, context
));
627 ASSERT_TRUE(value
.get());
629 EXPECT_TRUE(expected
->Equals(value
.get()));
632 TEST_F(V8ValueConverterImplTest
, DetectCycles
) {
633 v8::HandleScope
handle_scope(isolate_
);
634 v8::Local
<v8::Context
> context
=
635 v8::Local
<v8::Context
>::New(isolate_
, context_
);
636 v8::Context::Scope
context_scope(context
);
637 V8ValueConverterImpl converter
;
639 // Create a recursive array.
640 v8::Handle
<v8::Array
> recursive_array(v8::Array::New(isolate_
, 1));
641 recursive_array
->Set(0, recursive_array
);
643 // The first repetition should be trimmed and replaced by a null value.
644 base::ListValue expected_list
;
645 expected_list
.Append(base::Value::CreateNullValue());
647 // The actual result.
648 scoped_ptr
<base::Value
> actual_list(
649 converter
.FromV8Value(recursive_array
, context
));
650 ASSERT_TRUE(actual_list
.get());
652 EXPECT_TRUE(expected_list
.Equals(actual_list
.get()));
654 // Now create a recursive object
655 const std::string
key("key");
656 v8::Handle
<v8::Object
> recursive_object(v8::Object::New(isolate_
));
657 v8::TryCatch try_catch
;
658 recursive_object
->Set(
659 v8::String::NewFromUtf8(
660 isolate_
, key
.c_str(), v8::String::kNormalString
, key
.length()),
662 ASSERT_FALSE(try_catch
.HasCaught());
664 // The first repetition should be trimmed and replaced by a null value.
665 base::DictionaryValue expected_dictionary
;
666 expected_dictionary
.Set(key
, base::Value::CreateNullValue());
668 // The actual result.
669 scoped_ptr
<base::Value
> actual_dictionary(
670 converter
.FromV8Value(recursive_object
, context
));
671 ASSERT_TRUE(actual_dictionary
.get());
673 EXPECT_TRUE(expected_dictionary
.Equals(actual_dictionary
.get()));
676 TEST_F(V8ValueConverterImplTest
, MaxRecursionDepth
) {
677 v8::HandleScope
handle_scope(isolate_
);
678 v8::Local
<v8::Context
> context
=
679 v8::Local
<v8::Context
>::New(isolate_
, context_
);
680 v8::Context::Scope
context_scope(context
);
682 // Must larger than kMaxRecursionDepth in v8_value_converter_impl.cc.
684 const char kKey
[] = "key";
686 v8::Local
<v8::Object
> deep_object
= v8::Object::New(isolate_
);
688 v8::Local
<v8::Object
> leaf
= deep_object
;
689 for (int i
= 0; i
< kDepth
; ++i
) {
690 v8::Local
<v8::Object
> new_object
= v8::Object::New(isolate_
);
691 leaf
->Set(v8::String::NewFromUtf8(isolate_
, kKey
), new_object
);
695 V8ValueConverterImpl converter
;
696 scoped_ptr
<base::Value
> value(converter
.FromV8Value(deep_object
, context
));
699 // Expected depth is kMaxRecursionDepth in v8_value_converter_impl.cc.
700 int kExpectedDepth
= 100;
702 base::Value
* current
= value
.get();
703 for (int i
= 1; i
< kExpectedDepth
; ++i
) {
704 base::DictionaryValue
* current_as_object
= NULL
;
705 ASSERT_TRUE(current
->GetAsDictionary(¤t_as_object
)) << i
;
706 ASSERT_TRUE(current_as_object
->Get(kKey
, ¤t
)) << i
;
709 // The leaf node shouldn't have any properties.
710 base::DictionaryValue empty
;
711 EXPECT_TRUE(base::Value::Equals(&empty
, current
)) << *current
;
714 class V8ValueConverterOverridingStrategyForTesting
715 : public V8ValueConverter::Strategy
{
717 V8ValueConverterOverridingStrategyForTesting()
718 : reference_value_(NewReferenceValue()) {}
719 virtual bool FromV8Object(
720 v8::Handle
<v8::Object
> value
,
722 v8::Isolate
* isolate
,
723 const FromV8ValueCallback
& callback
) const OVERRIDE
{
724 *out
= NewReferenceValue();
727 virtual bool FromV8Array(v8::Handle
<v8::Array
> value
,
729 v8::Isolate
* isolate
,
730 const FromV8ValueCallback
& callback
) const OVERRIDE
{
731 *out
= NewReferenceValue();
734 virtual bool FromV8ArrayBuffer(v8::Handle
<v8::Object
> value
,
736 v8::Isolate
* isolate
) const OVERRIDE
{
737 *out
= NewReferenceValue();
740 virtual bool FromV8Number(v8::Handle
<v8::Number
> value
,
741 base::Value
** out
) const OVERRIDE
{
742 *out
= NewReferenceValue();
745 virtual bool FromV8Undefined(base::Value
** out
) const OVERRIDE
{
746 *out
= NewReferenceValue();
749 base::Value
* reference_value() const { return reference_value_
.get(); }
752 static base::Value
* NewReferenceValue() {
753 return new base::StringValue("strategy");
755 scoped_ptr
<base::Value
> reference_value_
;
758 TEST_F(V8ValueConverterImplTest
, StrategyOverrides
) {
759 v8::HandleScope
handle_scope(isolate_
);
760 v8::Local
<v8::Context
> context
=
761 v8::Local
<v8::Context
>::New(isolate_
, context_
);
762 v8::Context::Scope
context_scope(context
);
764 V8ValueConverterImpl converter
;
765 V8ValueConverterOverridingStrategyForTesting strategy
;
766 converter
.SetStrategy(&strategy
);
768 v8::Handle
<v8::Object
> object(v8::Object::New(isolate_
));
769 scoped_ptr
<base::Value
> object_value(converter
.FromV8Value(object
, context
));
770 ASSERT_TRUE(object_value
);
772 base::Value::Equals(strategy
.reference_value(), object_value
.get()));
774 v8::Handle
<v8::Array
> array(v8::Array::New(isolate_
));
775 scoped_ptr
<base::Value
> array_value(converter
.FromV8Value(array
, context
));
776 ASSERT_TRUE(array_value
);
778 base::Value::Equals(strategy
.reference_value(), array_value
.get()));
780 v8::Handle
<v8::ArrayBuffer
> array_buffer(v8::ArrayBuffer::New(isolate_
, 0));
781 scoped_ptr
<base::Value
> array_buffer_value(
782 converter
.FromV8Value(array_buffer
, context
));
783 ASSERT_TRUE(array_buffer_value
);
784 EXPECT_TRUE(base::Value::Equals(strategy
.reference_value(),
785 array_buffer_value
.get()));
787 v8::Handle
<v8::ArrayBufferView
> array_buffer_view(
788 v8::Uint8Array::New(array_buffer
, 0, 0));
789 scoped_ptr
<base::Value
> array_buffer_view_value(
790 converter
.FromV8Value(array_buffer_view
, context
));
791 ASSERT_TRUE(array_buffer_view_value
);
792 EXPECT_TRUE(base::Value::Equals(strategy
.reference_value(),
793 array_buffer_view_value
.get()));
795 v8::Handle
<v8::Number
> number(v8::Number::New(isolate_
, 0.0));
796 scoped_ptr
<base::Value
> number_value(converter
.FromV8Value(number
, context
));
797 ASSERT_TRUE(number_value
);
799 base::Value::Equals(strategy
.reference_value(), number_value
.get()));
801 v8::Handle
<v8::Primitive
> undefined(v8::Undefined(isolate_
));
802 scoped_ptr
<base::Value
> undefined_value(
803 converter
.FromV8Value(undefined
, context
));
804 ASSERT_TRUE(undefined_value
);
806 base::Value::Equals(strategy
.reference_value(), undefined_value
.get()));
809 class V8ValueConverterBypassStrategyForTesting
810 : public V8ValueConverter::Strategy
{
812 virtual bool FromV8Object(
813 v8::Handle
<v8::Object
> value
,
815 v8::Isolate
* isolate
,
816 const FromV8ValueCallback
& callback
) const OVERRIDE
{
819 virtual bool FromV8Array(v8::Handle
<v8::Array
> value
,
821 v8::Isolate
* isolate
,
822 const FromV8ValueCallback
& callback
) const OVERRIDE
{
825 virtual bool FromV8ArrayBuffer(v8::Handle
<v8::Object
> value
,
827 v8::Isolate
* isolate
) const OVERRIDE
{
830 virtual bool FromV8Number(v8::Handle
<v8::Number
> value
,
831 base::Value
** out
) const OVERRIDE
{
834 virtual bool FromV8Undefined(base::Value
** out
) const OVERRIDE
{
839 // Verify that having a strategy that fallbacks to default behaviour
840 // actually preserves it.
841 TEST_F(V8ValueConverterImplTest
, StrategyBypass
) {
842 v8::HandleScope
handle_scope(isolate_
);
843 v8::Local
<v8::Context
> context
=
844 v8::Local
<v8::Context
>::New(isolate_
, context_
);
845 v8::Context::Scope
context_scope(context
);
847 V8ValueConverterImpl converter
;
848 V8ValueConverterBypassStrategyForTesting strategy
;
849 converter
.SetStrategy(&strategy
);
851 v8::Handle
<v8::Object
> object(v8::Object::New(isolate_
));
852 scoped_ptr
<base::Value
> object_value(converter
.FromV8Value(object
, context
));
853 ASSERT_TRUE(object_value
);
854 scoped_ptr
<base::Value
> reference_object_value(base::test::ParseJson("{}"));
856 base::Value::Equals(reference_object_value
.get(), object_value
.get()));
858 v8::Handle
<v8::Array
> array(v8::Array::New(isolate_
));
859 scoped_ptr
<base::Value
> array_value(converter
.FromV8Value(array
, context
));
860 ASSERT_TRUE(array_value
);
861 scoped_ptr
<base::Value
> reference_array_value(base::test::ParseJson("[]"));
863 base::Value::Equals(reference_array_value
.get(), array_value
.get()));
865 // Not testing ArrayBuffers as V8ValueConverter uses blink helpers and
866 // this requires having blink to be initialized.
868 v8::Handle
<v8::Number
> number(v8::Number::New(isolate_
, 0.0));
869 scoped_ptr
<base::Value
> number_value(converter
.FromV8Value(number
, context
));
870 ASSERT_TRUE(number_value
);
871 scoped_ptr
<base::Value
> reference_number_value(base::test::ParseJson("0"));
873 base::Value::Equals(reference_number_value
.get(), number_value
.get()));
875 v8::Handle
<v8::Primitive
> undefined(v8::Undefined(isolate_
));
876 scoped_ptr
<base::Value
> undefined_value(
877 converter
.FromV8Value(undefined
, context
));
878 EXPECT_FALSE(undefined_value
);
881 } // namespace content