Home | History | Annotate | Download | only in renderer
      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   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));
     58   }
     59 
     60   virtual void TearDown() {
     61     context_.Reset();
     62   }
     63 
     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();
     69     }
     70     return temp;
     71   }
     72 
     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();
     80     }
     81     v8::String::Utf8Value utf8(temp);
     82     return std::string(*utf8, utf8.length());
     83   }
     84 
     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();
     90     }
     91     return temp;
     92   }
     93 
     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();
     99     }
    100     v8::String::Utf8Value utf8(temp);
    101     return std::string(*utf8, utf8.length());
    102   }
    103 
    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;
    109     }
    110     return child->GetType() == base::Value::TYPE_NULL;
    111   }
    112 
    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;
    119     }
    120     return child->IsNull();
    121   }
    122 
    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;
    128     }
    129     return child->GetType() == base::Value::TYPE_NULL;
    130   }
    131 
    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;
    137     }
    138     return child->IsNull();
    139   }
    140 
    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));
    148 
    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());
    155     }
    156 
    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());
    163 
    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"));
    171     }
    172 
    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());
    189     }
    190   }
    191 
    192   v8::Isolate* isolate_;
    193 
    194   // Context for the JavaScript in the test.
    195   v8::Persistent<v8::Context> context_;
    196 };
    197 
    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       "}");
    219 
    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);
    224 
    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());
    229 
    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());
    260 
    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()));
    264 }
    265 
    266 TEST_F(V8ValueConverterImplTest, KeysWithDots) {
    267   scoped_ptr<base::Value> original =
    268       base::test::ParseJson("{ \"foo.bar\": \"baz\" }");
    269 
    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);
    274 
    275   V8ValueConverterImpl converter;
    276   scoped_ptr<base::Value> copy(
    277       converter.FromV8Value(
    278           converter.ToV8Value(original.get(), context), context));
    279 
    280   EXPECT_TRUE(original->Equals(copy.get()));
    281 }
    282 
    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);
    288 
    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!'); });";
    295 
    296   v8::Handle<v8::Script> script(
    297       v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
    298   script->Run();
    299 
    300   v8::Handle<v8::Object> object(v8::Object::New(isolate_));
    301   object->Set(v8::String::NewFromUtf8(isolate_, "bar"),
    302               v8::String::NewFromUtf8(isolate_, "bar"));
    303 
    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"));
    315 
    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"));
    323 }
    324 
    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);
    330 
    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       "})();";
    340 
    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());
    345 
    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));
    354 
    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));
    363 }
    364 
    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);
    370 
    371   v8::Handle<v8::RegExp> regex(v8::RegExp::New(
    372       v8::String::NewFromUtf8(isolate_, "."), v8::RegExp::kNone));
    373 
    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()));
    387 
    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)));
    393 
    394   converter.SetRegExpAllowed(true);
    395   TestWeirdType(converter,
    396                 regex,
    397                 base::Value::TYPE_STRING,
    398                 scoped_ptr<base::Value>(new base::StringValue("/./")));
    399 }
    400 
    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);
    406 
    407   const char* source = "(function() {"
    408       "Object.prototype.foo = 'foo';"
    409       "return {};"
    410       "})();";
    411 
    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());
    416 
    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());
    423 }
    424 
    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);
    430 
    431   const char* source = "(function() {"
    432       "return { foo: undefined, bar: null };"
    433       "})();";
    434 
    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());
    439 
    440   V8ValueConverterImpl converter;
    441   converter.SetStripNullFromObjects(true);
    442 
    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());
    448 }
    449 
    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);
    455 
    456   V8ValueConverterImpl converter;
    457 
    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);
    463 
    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"));
    470 
    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);
    475 
    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));
    481 }
    482 
    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);
    488 
    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       "})();";
    499 
    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());
    504 
    505   V8ValueConverterImpl converter;
    506   scoped_ptr<base::Value> actual(converter.FromV8Value(object, context));
    507 
    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       "}");
    517 
    518   EXPECT_TRUE(expected->Equals(actual.get()));
    519 }
    520 
    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);
    526 
    527   const char* source = "(function() {"
    528       "var a = [0];"
    529       "a.__defineGetter__(1, function() { return 'bar'; });"
    530       "return a;"
    531       "})();";
    532 
    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());
    537 
    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());
    543 }
    544 
    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);
    550 
    551   v8::Handle<v8::Object> object;
    552   {
    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());
    560   }
    561 
    562   v8::Handle<v8::Array> array;
    563   {
    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());
    571   }
    572 
    573   v8::Handle<v8::Array> sparse_array;
    574   {
    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());
    582   }
    583 
    584   V8ValueConverterImpl converter;
    585 
    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()));
    590 
    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()));
    595 
    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()));
    601 }
    602 
    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;
    609 
    610   // We check that the converter checks identity correctly by disabling the
    611   // optimization of using identity hashes.
    612   ScopedAvoidIdentityHashForTesting scoped_hash_avoider(&converter);
    613 
    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)));
    620 
    621   // The expected base::Value result.
    622   scoped_ptr<base::Value> expected = base::test::ParseJson("[{},{},[],[]]");
    623   ASSERT_TRUE(expected.get());
    624 
    625   // The actual result.
    626   scoped_ptr<base::Value> value(converter.FromV8Value(root, context));
    627   ASSERT_TRUE(value.get());
    628 
    629   EXPECT_TRUE(expected->Equals(value.get()));
    630 }
    631 
    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;
    638 
    639   // Create a recursive array.
    640   v8::Handle<v8::Array> recursive_array(v8::Array::New(isolate_, 1));
    641   recursive_array->Set(0, recursive_array);
    642 
    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());
    646 
    647   // The actual result.
    648   scoped_ptr<base::Value> actual_list(
    649       converter.FromV8Value(recursive_array, context));
    650   ASSERT_TRUE(actual_list.get());
    651 
    652   EXPECT_TRUE(expected_list.Equals(actual_list.get()));
    653 
    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());
    663 
    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());
    667 
    668   // The actual result.
    669   scoped_ptr<base::Value> actual_dictionary(
    670       converter.FromV8Value(recursive_object, context));
    671   ASSERT_TRUE(actual_dictionary.get());
    672 
    673   EXPECT_TRUE(expected_dictionary.Equals(actual_dictionary.get()));
    674 }
    675 
    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);
    681 
    682   // Must larger than kMaxRecursionDepth in v8_value_converter_impl.cc.
    683   int kDepth = 1000;
    684   const char kKey[] = "key";
    685 
    686   v8::Local<v8::Object> deep_object = v8::Object::New(isolate_);
    687 
    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;
    693   }
    694 
    695   V8ValueConverterImpl converter;
    696   scoped_ptr<base::Value> value(converter.FromV8Value(deep_object, context));
    697   ASSERT_TRUE(value);
    698 
    699   // Expected depth is kMaxRecursionDepth in v8_value_converter_impl.cc.
    700   int kExpectedDepth = 100;
    701 
    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;
    707   }
    708 
    709   // The leaf node shouldn't have any properties.
    710   base::DictionaryValue empty;
    711   EXPECT_TRUE(base::Value::Equals(&empty, current)) << *current;
    712 }
    713 
    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;
    726   }
    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;
    733   }
    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;
    739   }
    740   virtual bool FromV8Number(v8::Handle<v8::Number> value,
    741                             base::Value** out) const OVERRIDE {
    742     *out = NewReferenceValue();
    743     return true;
    744   }
    745   virtual bool FromV8Undefined(base::Value** out) const OVERRIDE {
    746     *out = NewReferenceValue();
    747     return true;
    748   }
    749   base::Value* reference_value() const { return reference_value_.get(); }
    750 
    751  private:
    752   static base::Value* NewReferenceValue() {
    753     return new base::StringValue("strategy");
    754   }
    755   scoped_ptr<base::Value> reference_value_;
    756 };
    757 
    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);
    763 
    764   V8ValueConverterImpl converter;
    765   V8ValueConverterOverridingStrategyForTesting strategy;
    766   converter.SetStrategy(&strategy);
    767 
    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()));
    773 
    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()));
    779 
    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()));
    786 
    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()));
    794 
    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()));
    800 
    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()));
    807 }
    808 
    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;
    818   }
    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;
    824   }
    825   virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
    826                                  base::Value** out,
    827                                  v8::Isolate* isolate) const OVERRIDE {
    828     return false;
    829   }
    830   virtual bool FromV8Number(v8::Handle<v8::Number> value,
    831                             base::Value** out) const OVERRIDE {
    832     return false;
    833   }
    834   virtual bool FromV8Undefined(base::Value** out) const OVERRIDE {
    835     return false;
    836   }
    837 };
    838 
    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);
    846 
    847   V8ValueConverterImpl converter;
    848   V8ValueConverterBypassStrategyForTesting strategy;
    849   converter.SetStrategy(&strategy);
    850 
    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()));
    857 
    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()));
    864 
    865   // Not testing ArrayBuffers as V8ValueConverter uses blink helpers and
    866   // this requires having blink to be initialized.
    867 
    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()));
    874 
    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);
    879 }
    880 
    881 }  // namespace content
    882