Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / renderer / v8_value_converter_impl_unittest.cc
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.
4
5 #include <cmath>
6
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"
14
15 namespace content {
16
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();
29
30  private:
31   content::V8ValueConverterImpl* converter_;
32
33   DISALLOW_COPY_AND_ASSIGN(ScopedAvoidIdentityHashForTesting);
34 };
35
36 ScopedAvoidIdentityHashForTesting::ScopedAvoidIdentityHashForTesting(
37     content::V8ValueConverterImpl* converter)
38     : converter_(converter) {
39   CHECK(converter_);
40   converter_->avoid_identity_hash_for_testing_ = true;
41 }
42
43 ScopedAvoidIdentityHashForTesting::~ScopedAvoidIdentityHashForTesting() {
44   converter_->avoid_identity_hash_for_testing_ = false;
45 }
46
47 class V8ValueConverterImplTest : public testing::Test {
48  public:
49   V8ValueConverterImplTest()
50       : isolate_(v8::Isolate::GetCurrent()) {
51   }
52
53  protected:
54   void SetUp() override {
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));
58   }
59
60   void TearDown() override { context_.Reset(); }
61
62   std::string GetString(base::DictionaryValue* value, const std::string& key) {
63     std::string temp;
64     if (!value->GetString(key, &temp)) {
65       ADD_FAILURE();
66       return std::string();
67     }
68     return temp;
69   }
70
71   std::string GetString(v8::Handle<v8::Object> value, const std::string& key) {
72     v8::Handle<v8::String> temp =
73         value->Get(v8::String::NewFromUtf8(isolate_, key.c_str()))
74             .As<v8::String>();
75     if (temp.IsEmpty()) {
76       ADD_FAILURE();
77       return std::string();
78     }
79     v8::String::Utf8Value utf8(temp);
80     return std::string(*utf8, utf8.length());
81   }
82
83   std::string GetString(base::ListValue* value, uint32 index) {
84     std::string temp;
85     if (!value->GetString(static_cast<size_t>(index), &temp)) {
86       ADD_FAILURE();
87       return std::string();
88     }
89     return temp;
90   }
91
92   std::string GetString(v8::Handle<v8::Array> value, uint32 index) {
93     v8::Handle<v8::String> temp = value->Get(index).As<v8::String>();
94     if (temp.IsEmpty()) {
95       ADD_FAILURE();
96       return std::string();
97     }
98     v8::String::Utf8Value utf8(temp);
99     return std::string(*utf8, utf8.length());
100   }
101
102   bool IsNull(base::DictionaryValue* value, const std::string& key) {
103     base::Value* child = NULL;
104     if (!value->Get(key, &child)) {
105       ADD_FAILURE();
106       return false;
107     }
108     return child->GetType() == base::Value::TYPE_NULL;
109   }
110
111   bool IsNull(v8::Handle<v8::Object> value, const std::string& key) {
112     v8::Handle<v8::Value> child =
113         value->Get(v8::String::NewFromUtf8(isolate_, key.c_str()));
114     if (child.IsEmpty()) {
115       ADD_FAILURE();
116       return false;
117     }
118     return child->IsNull();
119   }
120
121   bool IsNull(base::ListValue* value, uint32 index) {
122     base::Value* child = NULL;
123     if (!value->Get(static_cast<size_t>(index), &child)) {
124       ADD_FAILURE();
125       return false;
126     }
127     return child->GetType() == base::Value::TYPE_NULL;
128   }
129
130   bool IsNull(v8::Handle<v8::Array> value, uint32 index) {
131     v8::Handle<v8::Value> child = value->Get(index);
132     if (child.IsEmpty()) {
133       ADD_FAILURE();
134       return false;
135     }
136     return child->IsNull();
137   }
138
139   void TestWeirdType(const V8ValueConverterImpl& converter,
140                      v8::Handle<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));
146
147     if (expected_value) {
148       ASSERT_TRUE(raw.get());
149       EXPECT_TRUE(expected_value->Equals(raw.get()));
150       EXPECT_EQ(expected_type, raw->GetType());
151     } else {
152       EXPECT_FALSE(raw.get());
153     }
154
155     v8::Handle<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());
161
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));
167     } else {
168       EXPECT_FALSE(dictionary->HasKey("test"));
169     }
170
171     v8::Handle<v8::Array> array(v8::Array::New(isolate_));
172     array->Set(0, val);
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));
181     } else {
182       // Arrays should preserve their length, and convert unconvertible
183       // types into null.
184       base::Value* temp = NULL;
185       ASSERT_TRUE(list->Get(0, &temp));
186       EXPECT_EQ(base::Value::TYPE_NULL, temp->GetType());
187     }
188   }
189
190   v8::Isolate* isolate_;
191
192   // Context for the JavaScript in the test.
193   v8::Persistent<v8::Context> context_;
194 };
195
196 TEST_F(V8ValueConverterImplTest, BasicRoundTrip) {
197   scoped_ptr<base::Value> original_root = base::test::ParseJson(
198       "{ \n"
199       "  \"null\": null, \n"
200       "  \"true\": true, \n"
201       "  \"false\": false, \n"
202       "  \"positive-int\": 42, \n"
203       "  \"negative-int\": -42, \n"
204       "  \"zero\": 0, \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"
212       "  }, \n"
213       "  \"empty-dictionary\": {}, \n"
214       "  \"list\": [ \"monkey\", \"balls\" ], \n"
215       "  \"empty-list\": [], \n"
216       "}");
217
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);
222
223   V8ValueConverterImpl converter;
224   v8::Handle<v8::Object> v8_object =
225       converter.ToV8Value(original_root.get(), context).As<v8::Object>();
226   ASSERT_FALSE(v8_object.IsEmpty());
227
228   EXPECT_EQ(static_cast<const base::DictionaryValue&>(*original_root).size(),
229             v8_object->GetPropertyNames()->Length());
230   EXPECT_TRUE(
231       v8_object->Get(v8::String::NewFromUtf8(isolate_, "null"))->IsNull());
232   EXPECT_TRUE(
233       v8_object->Get(v8::String::NewFromUtf8(isolate_, "true"))->IsTrue());
234   EXPECT_TRUE(
235       v8_object->Get(v8::String::NewFromUtf8(isolate_, "false"))->IsFalse());
236   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "positive-int"))
237                   ->IsInt32());
238   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "negative-int"))
239                   ->IsInt32());
240   EXPECT_TRUE(
241       v8_object->Get(v8::String::NewFromUtf8(isolate_, "zero"))->IsInt32());
242   EXPECT_TRUE(
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());
246   EXPECT_TRUE(
247       v8_object->Get(v8::String::NewFromUtf8(isolate_, "string"))->IsString());
248   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "empty-string"))
249                   ->IsString());
250   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "dictionary"))
251                   ->IsObject());
252   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(
253                                  isolate_, "empty-dictionary"))->IsObject());
254   EXPECT_TRUE(
255       v8_object->Get(v8::String::NewFromUtf8(isolate_, "list"))->IsArray());
256   EXPECT_TRUE(v8_object->Get(v8::String::NewFromUtf8(isolate_, "empty-list"))
257                   ->IsArray());
258
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()));
262 }
263
264 TEST_F(V8ValueConverterImplTest, KeysWithDots) {
265   scoped_ptr<base::Value> original =
266       base::test::ParseJson("{ \"foo.bar\": \"baz\" }");
267
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);
272
273   V8ValueConverterImpl converter;
274   scoped_ptr<base::Value> copy(
275       converter.FromV8Value(
276           converter.ToV8Value(original.get(), context), context));
277
278   EXPECT_TRUE(original->Equals(copy.get()));
279 }
280
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);
286
287   // Set up objects to throw when reading or writing 'foo'.
288   const char* source =
289       "Object.prototype.__defineSetter__('foo', "
290       "    function() { throw new Error('muah!'); });"
291       "Object.prototype.__defineGetter__('foo', "
292       "    function() { throw new Error('muah!'); });";
293
294   v8::Handle<v8::Script> script(
295       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
296   script->Run();
297
298   v8::Handle<v8::Object> object(v8::Object::New(isolate_));
299   object->Set(v8::String::NewFromUtf8(isolate_, "bar"),
300               v8::String::NewFromUtf8(isolate_, "bar"));
301
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"));
313
314   // Converting to v8 value should drop the foo property.
315   converted->SetString("foo", "foo");
316   v8::Handle<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"));
321 }
322
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);
328
329   const char* source = "(function() {"
330       "var arr = [];"
331       "arr.__defineSetter__(0, "
332       "    function() { throw new Error('muah!'); });"
333       "arr.__defineGetter__(0, "
334       "    function() { throw new Error('muah!'); });"
335       "arr[1] = 'bar';"
336       "return arr;"
337       "})();";
338
339   v8::Handle<v8::Script> script(
340       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
341   v8::Handle<v8::Array> array = script->Run().As<v8::Array>();
342   ASSERT_FALSE(array.IsEmpty());
343
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));
352
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::Handle<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));
361 }
362
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);
368
369   v8::Handle<v8::RegExp> regex(v8::RegExp::New(
370       v8::String::NewFromUtf8(isolate_, "."), v8::RegExp::kNone));
371
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,
382                 regex,
383                 base::Value::TYPE_DICTIONARY,
384                 scoped_ptr<base::Value>(new base::DictionaryValue()));
385
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)));
391
392   converter.SetRegExpAllowed(true);
393   TestWeirdType(converter,
394                 regex,
395                 base::Value::TYPE_STRING,
396                 scoped_ptr<base::Value>(new base::StringValue("/./")));
397 }
398
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);
404
405   const char* source = "(function() {"
406       "Object.prototype.foo = 'foo';"
407       "return {};"
408       "})();";
409
410   v8::Handle<v8::Script> script(
411       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
412   v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
413   ASSERT_FALSE(object.IsEmpty());
414
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());
421 }
422
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);
428
429   const char* source = "(function() {"
430       "return { foo: undefined, bar: null };"
431       "})();";
432
433   v8::Handle<v8::Script> script(
434       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
435   v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
436   ASSERT_FALSE(object.IsEmpty());
437
438   V8ValueConverterImpl converter;
439   converter.SetStripNullFromObjects(true);
440
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());
446 }
447
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);
453
454   V8ValueConverterImpl converter;
455
456   v8::Handle<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);
461
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"));
468
469   v8::Handle<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);
473
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));
479 }
480
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);
486
487   const char* source = "(function() {"
488       "return {"
489         "1: 'foo',"
490         "'2': 'bar',"
491         "true: 'baz',"
492         "false: 'qux',"
493         "null: 'quux',"
494         "undefined: 'oops'"
495       "};"
496       "})();";
497
498   v8::Handle<v8::Script> script(
499       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
500   v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
501   ASSERT_FALSE(object.IsEmpty());
502
503   V8ValueConverterImpl converter;
504   scoped_ptr<base::Value> actual(converter.FromV8Value(object, context));
505
506   scoped_ptr<base::Value> expected = base::test::ParseJson(
507       "{ \n"
508       "  \"1\": \"foo\", \n"
509       "  \"2\": \"bar\", \n"
510       "  \"true\": \"baz\", \n"
511       "  \"false\": \"qux\", \n"
512       "  \"null\": \"quux\", \n"
513       "  \"undefined\": \"oops\", \n"
514       "}");
515
516   EXPECT_TRUE(expected->Equals(actual.get()));
517 }
518
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);
524
525   const char* source = "(function() {"
526       "var a = [0];"
527       "a.__defineGetter__(1, function() { return 'bar'; });"
528       "return a;"
529       "})();";
530
531   v8::Handle<v8::Script> script(
532       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
533   v8::Handle<v8::Array> array = script->Run().As<v8::Array>();
534   ASSERT_FALSE(array.IsEmpty());
535
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());
541 }
542
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);
548
549   v8::Handle<v8::Object> object;
550   {
551     const char* source = "(function() {"
552         "return { foo: undefined, bar: null, baz: function(){} };"
553         "})();";
554     v8::Handle<v8::Script> script(
555         v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
556     object = script->Run().As<v8::Object>();
557     ASSERT_FALSE(object.IsEmpty());
558   }
559
560   v8::Handle<v8::Array> array;
561   {
562     const char* source = "(function() {"
563         "return [ undefined, null, function(){} ];"
564         "})();";
565     v8::Handle<v8::Script> script(
566         v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
567     array = script->Run().As<v8::Array>();
568     ASSERT_FALSE(array.IsEmpty());
569   }
570
571   v8::Handle<v8::Array> sparse_array;
572   {
573     const char* source = "(function() {"
574         "return new Array(3);"
575         "})();";
576     v8::Handle<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());
580   }
581
582   V8ValueConverterImpl converter;
583
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()));
588
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()));
593
594   scoped_ptr<base::Value> actual_sparse_array(
595       converter.FromV8Value(sparse_array, context));
596   EXPECT_TRUE(
597       base::Value::Equals(base::test::ParseJson("[ null, null, null ]").get(),
598                           actual_sparse_array.get()));
599 }
600
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;
607
608   // We check that the converter checks identity correctly by disabling the
609   // optimization of using identity hashes.
610   ScopedAvoidIdentityHashForTesting scoped_hash_avoider(&converter);
611
612   // Create the v8::Object to be converted.
613   v8::Handle<v8::Array> root(v8::Array::New(isolate_, 4));
614   root->Set(0, v8::Handle<v8::Object>(v8::Object::New(isolate_)));
615   root->Set(1, v8::Handle<v8::Object>(v8::Object::New(isolate_)));
616   root->Set(2, v8::Handle<v8::Object>(v8::Array::New(isolate_, 0)));
617   root->Set(3, v8::Handle<v8::Object>(v8::Array::New(isolate_, 0)));
618
619   // The expected base::Value result.
620   scoped_ptr<base::Value> expected = base::test::ParseJson("[{},{},[],[]]");
621   ASSERT_TRUE(expected.get());
622
623   // The actual result.
624   scoped_ptr<base::Value> value(converter.FromV8Value(root, context));
625   ASSERT_TRUE(value.get());
626
627   EXPECT_TRUE(expected->Equals(value.get()));
628 }
629
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;
636
637   // Create a recursive array.
638   v8::Handle<v8::Array> recursive_array(v8::Array::New(isolate_, 1));
639   recursive_array->Set(0, recursive_array);
640
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());
644
645   // The actual result.
646   scoped_ptr<base::Value> actual_list(
647       converter.FromV8Value(recursive_array, context));
648   ASSERT_TRUE(actual_list.get());
649
650   EXPECT_TRUE(expected_list.Equals(actual_list.get()));
651
652   // Now create a recursive object
653   const std::string key("key");
654   v8::Handle<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()),
659       recursive_object);
660   ASSERT_FALSE(try_catch.HasCaught());
661
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());
665
666   // The actual result.
667   scoped_ptr<base::Value> actual_dictionary(
668       converter.FromV8Value(recursive_object, context));
669   ASSERT_TRUE(actual_dictionary.get());
670
671   EXPECT_TRUE(expected_dictionary.Equals(actual_dictionary.get()));
672 }
673
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);
679
680   // Must larger than kMaxRecursionDepth in v8_value_converter_impl.cc.
681   int kDepth = 1000;
682   const char kKey[] = "key";
683
684   v8::Local<v8::Object> deep_object = v8::Object::New(isolate_);
685
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);
690     leaf = new_object;
691   }
692
693   V8ValueConverterImpl converter;
694   scoped_ptr<base::Value> value(converter.FromV8Value(deep_object, context));
695   ASSERT_TRUE(value);
696
697   // Expected depth is kMaxRecursionDepth in v8_value_converter_impl.cc.
698   int kExpectedDepth = 100;
699
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(&current_as_object)) << i;
704     ASSERT_TRUE(current_as_object->Get(kKey, &current)) << i;
705   }
706
707   // The leaf node shouldn't have any properties.
708   base::DictionaryValue empty;
709   EXPECT_TRUE(base::Value::Equals(&empty, current)) << *current;
710 }
711
712 class V8ValueConverterOverridingStrategyForTesting
713     : public V8ValueConverter::Strategy {
714  public:
715   V8ValueConverterOverridingStrategyForTesting()
716       : reference_value_(NewReferenceValue()) {}
717   bool FromV8Object(v8::Handle<v8::Object> value,
718                     base::Value** out,
719                     v8::Isolate* isolate,
720                     const FromV8ValueCallback& callback) const override {
721     *out = NewReferenceValue();
722     return true;
723   }
724   bool FromV8Array(v8::Handle<v8::Array> value,
725                    base::Value** out,
726                    v8::Isolate* isolate,
727                    const FromV8ValueCallback& callback) const override {
728     *out = NewReferenceValue();
729     return true;
730   }
731   bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
732                          base::Value** out,
733                          v8::Isolate* isolate) const override {
734     *out = NewReferenceValue();
735     return true;
736   }
737   bool FromV8Number(v8::Handle<v8::Number> value,
738                     base::Value** out) const override {
739     *out = NewReferenceValue();
740     return true;
741   }
742   bool FromV8Undefined(base::Value** out) const override {
743     *out = NewReferenceValue();
744     return true;
745   }
746   base::Value* reference_value() const { return reference_value_.get(); }
747
748  private:
749   static base::Value* NewReferenceValue() {
750     return new base::StringValue("strategy");
751   }
752   scoped_ptr<base::Value> reference_value_;
753 };
754
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);
760
761   V8ValueConverterImpl converter;
762   V8ValueConverterOverridingStrategyForTesting strategy;
763   converter.SetStrategy(&strategy);
764
765   v8::Handle<v8::Object> object(v8::Object::New(isolate_));
766   scoped_ptr<base::Value> object_value(converter.FromV8Value(object, context));
767   ASSERT_TRUE(object_value);
768   EXPECT_TRUE(
769       base::Value::Equals(strategy.reference_value(), object_value.get()));
770
771   v8::Handle<v8::Array> array(v8::Array::New(isolate_));
772   scoped_ptr<base::Value> array_value(converter.FromV8Value(array, context));
773   ASSERT_TRUE(array_value);
774   EXPECT_TRUE(
775       base::Value::Equals(strategy.reference_value(), array_value.get()));
776
777   v8::Handle<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()));
783
784   v8::Handle<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()));
791
792   v8::Handle<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);
795   EXPECT_TRUE(
796       base::Value::Equals(strategy.reference_value(), number_value.get()));
797
798   v8::Handle<v8::Primitive> undefined(v8::Undefined(isolate_));
799   scoped_ptr<base::Value> undefined_value(
800       converter.FromV8Value(undefined, context));
801   ASSERT_TRUE(undefined_value);
802   EXPECT_TRUE(
803       base::Value::Equals(strategy.reference_value(), undefined_value.get()));
804 }
805
806 class V8ValueConverterBypassStrategyForTesting
807     : public V8ValueConverter::Strategy {
808  public:
809   bool FromV8Object(v8::Handle<v8::Object> value,
810                     base::Value** out,
811                     v8::Isolate* isolate,
812                     const FromV8ValueCallback& callback) const override {
813     return false;
814   }
815   bool FromV8Array(v8::Handle<v8::Array> value,
816                    base::Value** out,
817                    v8::Isolate* isolate,
818                    const FromV8ValueCallback& callback) const override {
819     return false;
820   }
821   bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
822                          base::Value** out,
823                          v8::Isolate* isolate) const override {
824     return false;
825   }
826   bool FromV8Number(v8::Handle<v8::Number> value,
827                     base::Value** out) const override {
828     return false;
829   }
830   bool FromV8Undefined(base::Value** out) const override { return false; }
831 };
832
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);
840
841   V8ValueConverterImpl converter;
842   V8ValueConverterBypassStrategyForTesting strategy;
843   converter.SetStrategy(&strategy);
844
845   v8::Handle<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("{}"));
849   EXPECT_TRUE(
850       base::Value::Equals(reference_object_value.get(), object_value.get()));
851
852   v8::Handle<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("[]"));
856   EXPECT_TRUE(
857       base::Value::Equals(reference_array_value.get(), array_value.get()));
858
859   // Not testing ArrayBuffers as V8ValueConverter uses blink helpers and
860   // this requires having blink to be initialized.
861
862   v8::Handle<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"));
866   EXPECT_TRUE(
867       base::Value::Equals(reference_number_value.get(), number_value.get()));
868
869   v8::Handle<v8::Primitive> undefined(v8::Undefined(isolate_));
870   scoped_ptr<base::Value> undefined_value(
871       converter.FromV8Value(undefined, context));
872   EXPECT_FALSE(undefined_value);
873 }
874
875 }  // namespace content