| Index: content/child/v8_value_converter_impl_unittest.cc
 | 
| diff --git a/content/child/v8_value_converter_impl_unittest.cc b/content/child/v8_value_converter_impl_unittest.cc
 | 
| index bcd5eeb02ebd9dfab22f0c7917dab606edff1a6d..3387f4fc4cdaf8d3ed706798ad6b62319b8c01cb 100644
 | 
| --- a/content/child/v8_value_converter_impl_unittest.cc
 | 
| +++ b/content/child/v8_value_converter_impl_unittest.cc
 | 
| @@ -104,6 +104,26 @@ class V8ValueConverterImplTest : public testing::Test {
 | 
|      return std::string(*utf8, utf8.length());
 | 
|    }
 | 
|  
 | 
| +  int32_t GetInt(v8::Local<v8::Object> value, const std::string& key) {
 | 
| +    v8::Local<v8::Int32> temp =
 | 
| +        value->Get(v8::String::NewFromUtf8(isolate_, key.c_str()))
 | 
| +            .As<v8::Int32>();
 | 
| +    if (temp.IsEmpty()) {
 | 
| +      ADD_FAILURE();
 | 
| +      return -1;
 | 
| +    }
 | 
| +    return temp->Value();
 | 
| +  }
 | 
| +
 | 
| +  int32_t GetInt(v8::Local<v8::Object> value, uint32_t index) {
 | 
| +    v8::Local<v8::Int32> temp = value->Get(index).As<v8::Int32>();
 | 
| +    if (temp.IsEmpty()) {
 | 
| +      ADD_FAILURE();
 | 
| +      return -1;
 | 
| +    }
 | 
| +    return temp->Value();
 | 
| +  }
 | 
| +
 | 
|    bool IsNull(base::DictionaryValue* value, const std::string& key) {
 | 
|      base::Value* child = NULL;
 | 
|      if (!value->Get(key, &child)) {
 | 
| @@ -318,12 +338,13 @@ TEST_F(V8ValueConverterImplTest, ObjectExceptions) {
 | 
|    EXPECT_EQ(1u, converted->size());
 | 
|    EXPECT_EQ("bar", GetString(converted.get(), "bar"));
 | 
|  
 | 
| -  // Converting to v8 value should drop the foo property.
 | 
| +  // Converting to v8 value should not trigger the setter.
 | 
|    converted->SetString("foo", "foo");
 | 
|    v8::Local<v8::Object> copy =
 | 
|        converter.ToV8Value(converted.get(), context).As<v8::Object>();
 | 
|    EXPECT_FALSE(copy.IsEmpty());
 | 
|    EXPECT_EQ(2u, copy->GetPropertyNames()->Length());
 | 
| +  EXPECT_EQ("foo", GetString(copy, "foo"));
 | 
|    EXPECT_EQ("bar", GetString(copy, "bar"));
 | 
|  }
 | 
|  
 | 
| @@ -359,13 +380,16 @@ TEST_F(V8ValueConverterImplTest, ArrayExceptions) {
 | 
|    EXPECT_EQ(2u, converted->GetSize());
 | 
|    EXPECT_TRUE(IsNull(converted.get(), 0));
 | 
|  
 | 
| -  // Converting to v8 value should drop the first item and leave a hole.
 | 
| +  // Converting to v8 value should not be affected by the getter/setter
 | 
| +  // because the setters/getters are defined on the array instance, not
 | 
| +  // on the Array's prototype.
 | 
|    converted.reset(static_cast<base::ListValue*>(
 | 
|        base::test::ParseJson("[ \"foo\", \"bar\" ]").release()));
 | 
|    v8::Local<v8::Array> copy =
 | 
|        converter.ToV8Value(converted.get(), context).As<v8::Array>();
 | 
|    ASSERT_FALSE(copy.IsEmpty());
 | 
|    EXPECT_EQ(2u, copy->Length());
 | 
| +  EXPECT_EQ("foo", GetString(copy, 0));
 | 
|    EXPECT_EQ("bar", GetString(copy, 1));
 | 
|  }
 | 
|  
 | 
| @@ -424,6 +448,146 @@ TEST_F(V8ValueConverterImplTest, Prototype) {
 | 
|    EXPECT_EQ(0u, result->size());
 | 
|  }
 | 
|  
 | 
| +TEST_F(V8ValueConverterImplTest, ObjectPrototypeSetter) {
 | 
| +  std::unique_ptr<base::Value> original =
 | 
| +      base::test::ParseJson("{ \"foo\": \"good value\" }");
 | 
| +
 | 
| +  v8::HandleScope handle_scope(isolate_);
 | 
| +  v8::Local<v8::Context> context =
 | 
| +      v8::Local<v8::Context>::New(isolate_, context_);
 | 
| +  v8::Context::Scope context_scope(context);
 | 
| +  v8::MicrotasksScope microtasks(isolate_,
 | 
| +                                 v8::MicrotasksScope::kDoNotRunMicrotasks);
 | 
| +
 | 
| +  const char* source =
 | 
| +      "var result = { getters: 0, setters: 0 };"
 | 
| +      "Object.defineProperty(Object.prototype, 'foo', {"
 | 
| +      "  get() { ++result.getters; return 'bogus'; },"
 | 
| +      "  set() { ++result.setters; },"
 | 
| +      "});"
 | 
| +      "result;";
 | 
| +
 | 
| +  const char* source_sanity =
 | 
| +      "({}).foo = 'Trigger setter';"
 | 
| +      "({}).foo;";
 | 
| +
 | 
| +  v8::Local<v8::Script> script(
 | 
| +      v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
 | 
| +  v8::Local<v8::Object> result = script->Run().As<v8::Object>();
 | 
| +  ASSERT_FALSE(result.IsEmpty());
 | 
| +
 | 
| +  // Sanity checks: the getters/setters are normally triggered.
 | 
| +  v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source_sanity))->Run();
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  V8ValueConverterImpl converter;
 | 
| +  v8::Local<v8::Object> converted =
 | 
| +      converter.ToV8Value(original.get(), context).As<v8::Object>();
 | 
| +  EXPECT_FALSE(converted.IsEmpty());
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  EXPECT_EQ(1u, converted->GetPropertyNames()->Length());
 | 
| +  EXPECT_EQ("good value", GetString(converted, "foo"));
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered while accessing existing values.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  // Repeat the same exercise with a dictionary without the key.
 | 
| +  base::DictionaryValue missing_key_dict;
 | 
| +  missing_key_dict.SetString("otherkey", "hello");
 | 
| +  v8::Local<v8::Object> converted2 =
 | 
| +      converter.ToV8Value(&missing_key_dict, context).As<v8::Object>();
 | 
| +  EXPECT_FALSE(converted2.IsEmpty());
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  EXPECT_EQ(1u, converted2->GetPropertyNames()->Length());
 | 
| +  EXPECT_EQ("hello", GetString(converted2, "otherkey"));
 | 
| +
 | 
| +  // Missing key = should trigger getter upon access.
 | 
| +  EXPECT_EQ("bogus", GetString(converted2, "foo"));
 | 
| +  EXPECT_EQ(2, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +}
 | 
| +
 | 
| +TEST_F(V8ValueConverterImplTest, ArrayPrototypeSetter) {
 | 
| +  std::unique_ptr<base::Value> original =
 | 
| +      base::test::ParseJson("[100, 200, 300]");
 | 
| +
 | 
| +  v8::HandleScope handle_scope(isolate_);
 | 
| +  v8::Local<v8::Context> context =
 | 
| +      v8::Local<v8::Context>::New(isolate_, context_);
 | 
| +  v8::Context::Scope context_scope(context);
 | 
| +  v8::MicrotasksScope microtasks(isolate_,
 | 
| +                                 v8::MicrotasksScope::kDoNotRunMicrotasks);
 | 
| +
 | 
| +  const char* source =
 | 
| +      "var result = { getters: 0, setters: 0 };"
 | 
| +      "Object.defineProperty(Array.prototype, '1', {"
 | 
| +      "  get() { ++result.getters; return 1337; },"
 | 
| +      "  set() { ++result.setters; },"
 | 
| +      "});"
 | 
| +      "result;";
 | 
| +
 | 
| +  const char* source_sanity =
 | 
| +      "[][1] = 'Trigger setter';"
 | 
| +      "[][1];";
 | 
| +
 | 
| +  v8::Local<v8::Script> script(
 | 
| +      v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source)));
 | 
| +  v8::Local<v8::Object> result = script->Run().As<v8::Object>();
 | 
| +  ASSERT_FALSE(result.IsEmpty());
 | 
| +
 | 
| +  // Sanity checks: the getters/setters are normally triggered.
 | 
| +  v8::Script::Compile(v8::String::NewFromUtf8(isolate_, source_sanity))->Run();
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  V8ValueConverterImpl converter;
 | 
| +  v8::Local<v8::Array> converted =
 | 
| +      converter.ToV8Value(original.get(), context).As<v8::Array>();
 | 
| +  EXPECT_FALSE(converted.IsEmpty());
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered during the conversion.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  EXPECT_EQ(3u, converted->Length());
 | 
| +  EXPECT_EQ(100, GetInt(converted, 0));
 | 
| +  EXPECT_EQ(200, GetInt(converted, 1));
 | 
| +  EXPECT_EQ(300, GetInt(converted, 2));
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered while accessing existing values.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  // Try again, using an array without the index.
 | 
| +  base::ListValue one_item_list;
 | 
| +  one_item_list.Append(new base::FundamentalValue(123456));
 | 
| +  v8::Local<v8::Array> converted2 =
 | 
| +      converter.ToV8Value(&one_item_list, context).As<v8::Array>();
 | 
| +  EXPECT_FALSE(converted2.IsEmpty());
 | 
| +
 | 
| +  // Getters/setters shouldn't be triggered during the conversion.
 | 
| +  EXPECT_EQ(1, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +
 | 
| +  EXPECT_EQ(1u, converted2->Length());
 | 
| +  EXPECT_EQ(123456, GetInt(converted2, 0));
 | 
| +
 | 
| +  // Accessing missing index 1 triggers the getter.
 | 
| +  EXPECT_EQ(1337, GetInt(converted2, 1));
 | 
| +  EXPECT_EQ(2, GetInt(result, "getters"));
 | 
| +  EXPECT_EQ(1, GetInt(result, "setters"));
 | 
| +}
 | 
| +
 | 
|  TEST_F(V8ValueConverterImplTest, StripNullFromObjects) {
 | 
|    v8::HandleScope handle_scope(isolate_);
 | 
|    v8::Local<v8::Context> context =
 | 
| 
 |