aw: Ensure fallback tick unsets |block_invalidates_|
[chromium-blink-merge.git] / content / renderer / v8_value_converter_impl_unittest.cc
blob6f1302dd5fcd63d78adf3f85f5d41fefa0f551a0
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.
5 #include <cmath>
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"
15 namespace content {
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 {
23 public:
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();
30 private:
31 content::V8ValueConverterImpl* converter_;
33 DISALLOW_COPY_AND_ASSIGN(ScopedAvoidIdentityHashForTesting);
36 ScopedAvoidIdentityHashForTesting::ScopedAvoidIdentityHashForTesting(
37 content::V8ValueConverterImpl* converter)
38 : converter_(converter) {
39 CHECK(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 {
48 public:
49 V8ValueConverterImplTest()
50 : isolate_(v8::Isolate::GetCurrent()) {
53 protected:
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() {
61 context_.Reset();
64 std::string GetString(base::DictionaryValue* value, const std::string& key) {
65 std::string temp;
66 if (!value->GetString(key, &temp)) {
67 ADD_FAILURE();
68 return std::string();
70 return 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()))
76 .As<v8::String>();
77 if (temp.IsEmpty()) {
78 ADD_FAILURE();
79 return std::string();
81 v8::String::Utf8Value utf8(temp);
82 return std::string(*utf8, utf8.length());
85 std::string GetString(base::ListValue* value, uint32 index) {
86 std::string temp;
87 if (!value->GetString(static_cast<size_t>(index), &temp)) {
88 ADD_FAILURE();
89 return std::string();
91 return temp;
94 std::string GetString(v8::Handle<v8::Array> value, uint32 index) {
95 v8::Handle<v8::String> temp = value->Get(index).As<v8::String>();
96 if (temp.IsEmpty()) {
97 ADD_FAILURE();
98 return std::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)) {
107 ADD_FAILURE();
108 return false;
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()) {
117 ADD_FAILURE();
118 return false;
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)) {
126 ADD_FAILURE();
127 return false;
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()) {
135 ADD_FAILURE();
136 return false;
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());
153 } else {
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));
169 } else {
170 EXPECT_FALSE(dictionary->HasKey("test"));
173 v8::Handle<v8::Array> array(v8::Array::New(isolate_));
174 array->Set(0, val);
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));
183 } else {
184 // Arrays should preserve their length, and convert unconvertible
185 // types into null.
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(
200 "{ \n"
201 " \"null\": null, \n"
202 " \"true\": true, \n"
203 " \"false\": false, \n"
204 " \"positive-int\": 42, \n"
205 " \"negative-int\": -42, \n"
206 " \"zero\": 0, \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"
214 " }, \n"
215 " \"empty-dictionary\": {}, \n"
216 " \"list\": [ \"monkey\", \"balls\" ], \n"
217 " \"empty-list\": [], \n"
218 "}");
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());
232 EXPECT_TRUE(
233 v8_object->Get(v8::String::NewFromUtf8(isolate_, "null"))->IsNull());
234 EXPECT_TRUE(
235 v8_object->Get(v8::String::NewFromUtf8(isolate_, "true"))->IsTrue());
236 EXPECT_TRUE(
237 v8_object->Get(v8::String::NewFromUtf8(isolate_, "false"))->IsFalse());
238 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "positive-int"))
239 ->IsInt32());
240 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "negative-int"))
241 ->IsInt32());
242 EXPECT_TRUE(
243 v8_object->Get(v8::String::NewFromUtf8(isolate_, "zero"))->IsInt32());
244 EXPECT_TRUE(
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());
248 EXPECT_TRUE(
249 v8_object->Get(v8::String::NewFromUtf8(isolate_, "string"))->IsString());
250 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "empty-string"))
251 ->IsString());
252 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "dictionary"))
253 ->IsObject());
254 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(
255 isolate_, "empty-dictionary"))->IsObject());
256 EXPECT_TRUE(
257 v8_object->Get(v8::String::NewFromUtf8(isolate_, "list"))->IsArray());
258 EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "empty-list"))
259 ->IsArray());
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'.
290 const char* source =
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)));
298 script->Run();
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() {"
332 "var arr = [];"
333 "arr.__defineSetter__(0, "
334 " function() { throw new Error('muah!'); });"
335 "arr.__defineGetter__(0, "
336 " function() { throw new Error('muah!'); });"
337 "arr[1] = 'bar';"
338 "return arr;"
339 "})();";
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,
384 regex,
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,
396 regex,
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';"
409 "return {};"
410 "})();";
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 };"
433 "})();";
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() {"
490 "return {"
491 "1: 'foo',"
492 "'2': 'bar',"
493 "true: 'baz',"
494 "false: 'qux',"
495 "null: 'quux',"
496 "undefined: 'oops'"
497 "};"
498 "})();";
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(
509 "{ \n"
510 " \"1\": \"foo\", \n"
511 " \"2\": \"bar\", \n"
512 " \"true\": \"baz\", \n"
513 " \"false\": \"qux\", \n"
514 " \"null\": \"quux\", \n"
515 " \"undefined\": \"oops\", \n"
516 "}");
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() {"
528 "var a = [0];"
529 "a.__defineGetter__(1, function() { return 'bar'; });"
530 "return a;"
531 "})();";
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(){} };"
555 "})();";
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(){} ];"
566 "})();";
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);"
577 "})();";
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));
598 EXPECT_TRUE(
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()),
661 recursive_object);
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.
683 int kDepth = 1000;
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);
692 leaf = new_object;
695 V8ValueConverterImpl converter;
696 scoped_ptr<base::Value> value(converter.FromV8Value(deep_object, context));
697 ASSERT_TRUE(value);
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(&current_as_object)) << i;
706 ASSERT_TRUE(current_as_object->Get(kKey, &current)) << 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 {
716 public:
717 V8ValueConverterOverridingStrategyForTesting()
718 : reference_value_(NewReferenceValue()) {}
719 virtual bool FromV8Object(
720 v8::Handle<v8::Object> value,
721 base::Value** out,
722 v8::Isolate* isolate,
723 const FromV8ValueCallback& callback) const OVERRIDE {
724 *out = NewReferenceValue();
725 return true;
727 virtual bool FromV8Array(v8::Handle<v8::Array> value,
728 base::Value** out,
729 v8::Isolate* isolate,
730 const FromV8ValueCallback& callback) const OVERRIDE {
731 *out = NewReferenceValue();
732 return true;
734 virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
735 base::Value** out,
736 v8::Isolate* isolate) const OVERRIDE {
737 *out = NewReferenceValue();
738 return true;
740 virtual bool FromV8Number(v8::Handle<v8::Number> value,
741 base::Value** out) const OVERRIDE {
742 *out = NewReferenceValue();
743 return true;
745 virtual bool FromV8Undefined(base::Value** out) const OVERRIDE {
746 *out = NewReferenceValue();
747 return true;
749 base::Value* reference_value() const { return reference_value_.get(); }
751 private:
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);
771 EXPECT_TRUE(
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);
777 EXPECT_TRUE(
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);
798 EXPECT_TRUE(
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);
805 EXPECT_TRUE(
806 base::Value::Equals(strategy.reference_value(), undefined_value.get()));
809 class V8ValueConverterBypassStrategyForTesting
810 : public V8ValueConverter::Strategy {
811 public:
812 virtual bool FromV8Object(
813 v8::Handle<v8::Object> value,
814 base::Value** out,
815 v8::Isolate* isolate,
816 const FromV8ValueCallback& callback) const OVERRIDE {
817 return false;
819 virtual bool FromV8Array(v8::Handle<v8::Array> value,
820 base::Value** out,
821 v8::Isolate* isolate,
822 const FromV8ValueCallback& callback) const OVERRIDE {
823 return false;
825 virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
826 base::Value** out,
827 v8::Isolate* isolate) const OVERRIDE {
828 return false;
830 virtual bool FromV8Number(v8::Handle<v8::Number> value,
831 base::Value** out) const OVERRIDE {
832 return false;
834 virtual bool FromV8Undefined(base::Value** out) const OVERRIDE {
835 return false;
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("{}"));
855 EXPECT_TRUE(
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("[]"));
862 EXPECT_TRUE(
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"));
872 EXPECT_TRUE(
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