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 "content/renderer/v8_value_converter_impl.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <cmath>
11 #include <memory>
12 
13 #include "base/containers/span.h"
14 #include "base/macros.h"
15 #include "base/stl_util.h"
16 #include "base/test/task_environment.h"
17 #include "base/test/values_test_util.h"
18 #include "base/values.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "v8/include/v8.h"
21 
22 namespace content {
23 
24 // To improve the performance of
25 // V8ValueConverterImpl::UpdateAndCheckUniqueness, identity hashes of objects
26 // are used during checking for duplicates. For testing purposes we need to
27 // ignore the hash sometimes. Create this helper object to avoid using identity
28 // hashes for the lifetime of the helper.
29 class ScopedAvoidIdentityHashForTesting {
30  public:
31   // The hashes will be ignored in |converter|, which must not be NULL and it
32   // must outlive the created instance of this helper.
33   explicit ScopedAvoidIdentityHashForTesting(
34       content::V8ValueConverterImpl* converter);
35   ~ScopedAvoidIdentityHashForTesting();
36 
37  private:
38   content::V8ValueConverterImpl* converter_;
39 
40   DISALLOW_COPY_AND_ASSIGN(ScopedAvoidIdentityHashForTesting);
41 };
42 
ScopedAvoidIdentityHashForTesting(content::V8ValueConverterImpl * converter)43 ScopedAvoidIdentityHashForTesting::ScopedAvoidIdentityHashForTesting(
44     content::V8ValueConverterImpl* converter)
45     : converter_(converter) {
46   CHECK(converter_);
47   converter_->avoid_identity_hash_for_testing_ = true;
48 }
49 
~ScopedAvoidIdentityHashForTesting()50 ScopedAvoidIdentityHashForTesting::~ScopedAvoidIdentityHashForTesting() {
51   converter_->avoid_identity_hash_for_testing_ = false;
52 }
53 
54 class V8ValueConverterImplTest : public testing::Test {
55  public:
V8ValueConverterImplTest()56   V8ValueConverterImplTest()
57       : isolate_(v8::Isolate::GetCurrent()) {
58   }
59 
60  protected:
SetUp()61   void SetUp() override {
62     v8::HandleScope handle_scope(isolate_);
63     v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
64     context_.Reset(isolate_, v8::Context::New(isolate_, nullptr, global));
65   }
66 
TearDown()67   void TearDown() override { context_.Reset(); }
68 
GetString(base::DictionaryValue * value,const std::string & key)69   std::string GetString(base::DictionaryValue* value, const std::string& key) {
70     std::string temp;
71     if (!value->GetString(key, &temp)) {
72       ADD_FAILURE();
73       return std::string();
74     }
75     return temp;
76   }
77 
GetString(v8::Local<v8::Object> value,const std::string & key)78   std::string GetString(v8::Local<v8::Object> value, const std::string& key) {
79     v8::Local<v8::Value> temp;
80     if (!value
81              ->Get(isolate_->GetCurrentContext(),
82                    v8::String::NewFromUtf8(isolate_, key.c_str(),
83                                            v8::NewStringType::kInternalized)
84                        .ToLocalChecked())
85              .ToLocal(&temp)) {
86       ADD_FAILURE();
87       return std::string();
88     }
89     v8::String::Utf8Value utf8(isolate_, temp.As<v8::String>());
90     return std::string(*utf8, utf8.length());
91   }
92 
GetString(base::ListValue * value,uint32_t index)93   std::string GetString(base::ListValue* value, uint32_t index) {
94     std::string temp;
95     if (!value->GetString(static_cast<size_t>(index), &temp)) {
96       ADD_FAILURE();
97       return std::string();
98     }
99     return temp;
100   }
101 
GetString(v8::Local<v8::Array> value,uint32_t index)102   std::string GetString(v8::Local<v8::Array> value, uint32_t index) {
103     v8::Local<v8::Value> temp;
104     if (!value->Get(isolate_->GetCurrentContext(), index).ToLocal(&temp)) {
105       ADD_FAILURE();
106       return std::string();
107     }
108     v8::String::Utf8Value utf8(isolate_, temp.As<v8::String>());
109     return std::string(*utf8, utf8.length());
110   }
111 
GetInt(v8::Local<v8::Object> value,const std::string & key)112   int32_t GetInt(v8::Local<v8::Object> value, const std::string& key) {
113     v8::Local<v8::Value> temp;
114     if (!value
115              ->Get(isolate_->GetCurrentContext(),
116                    v8::String::NewFromUtf8(isolate_, key.c_str(),
117                                            v8::NewStringType::kInternalized)
118                        .ToLocalChecked())
119              .ToLocal(&temp)) {
120       ADD_FAILURE();
121       return -1;
122     }
123     return temp.As<v8::Int32>()->Value();
124   }
125 
GetInt(v8::Local<v8::Object> value,uint32_t index)126   int32_t GetInt(v8::Local<v8::Object> value, uint32_t index) {
127     v8::Local<v8::Value> temp;
128     if (!value->Get(isolate_->GetCurrentContext(), index).ToLocal(&temp)) {
129       ADD_FAILURE();
130       return -1;
131     }
132     return temp.As<v8::Int32>()->Value();
133   }
134 
IsNull(base::DictionaryValue * value,const std::string & key)135   bool IsNull(base::DictionaryValue* value, const std::string& key) {
136     base::Value* child = nullptr;
137     if (!value->Get(key, &child)) {
138       ADD_FAILURE();
139       return false;
140     }
141     return child->type() == base::Value::Type::NONE;
142   }
143 
IsNull(v8::Local<v8::Object> value,const std::string & key)144   bool IsNull(v8::Local<v8::Object> value, const std::string& key) {
145     v8::Local<v8::Value> child;
146     if (!value
147              ->Get(isolate_->GetCurrentContext(),
148                    v8::String::NewFromUtf8(isolate_, key.c_str(),
149                                            v8::NewStringType::kInternalized)
150                        .ToLocalChecked())
151              .ToLocal(&child)) {
152       ADD_FAILURE();
153       return false;
154     }
155     return child->IsNull();
156   }
157 
IsNull(base::ListValue * value,uint32_t index)158   bool IsNull(base::ListValue* value, uint32_t index) {
159     base::Value* child = nullptr;
160     if (!value->Get(static_cast<size_t>(index), &child)) {
161       ADD_FAILURE();
162       return false;
163     }
164     return child->type() == base::Value::Type::NONE;
165   }
166 
IsNull(v8::Local<v8::Array> value,uint32_t index)167   bool IsNull(v8::Local<v8::Array> value, uint32_t index) {
168     v8::Local<v8::Value> child;
169     if (!value->Get(isolate_->GetCurrentContext(), index).ToLocal(&child)) {
170       ADD_FAILURE();
171       return false;
172     }
173     return child->IsNull();
174   }
175 
TestWeirdType(V8ValueConverterImpl & converter,v8::Local<v8::Value> val,base::Value::Type expected_type,std::unique_ptr<base::Value> expected_value)176   void TestWeirdType(V8ValueConverterImpl& converter,
177                      v8::Local<v8::Value> val,
178                      base::Value::Type expected_type,
179                      std::unique_ptr<base::Value> expected_value) {
180     v8::Local<v8::Context> context =
181         v8::Local<v8::Context>::New(isolate_, context_);
182     std::unique_ptr<base::Value> raw(converter.FromV8Value(val, context));
183 
184     if (expected_value) {
185       ASSERT_TRUE(raw.get());
186       EXPECT_TRUE(expected_value->Equals(raw.get()));
187       EXPECT_EQ(expected_type, raw->type());
188     } else {
189       EXPECT_FALSE(raw.get());
190     }
191 
192     v8::Local<v8::Object> object(v8::Object::New(isolate_));
193     object
194         ->Set(context,
195               v8::String::NewFromUtf8(isolate_, "test",
196                                       v8::NewStringType::kInternalized)
197                   .ToLocalChecked(),
198               val)
199         .Check();
200     std::unique_ptr<base::DictionaryValue> dictionary(
201         base::DictionaryValue::From(converter.FromV8Value(object, context)));
202     ASSERT_TRUE(dictionary.get());
203 
204     if (expected_value) {
205       base::Value* temp = nullptr;
206       ASSERT_TRUE(dictionary->Get("test", &temp));
207       EXPECT_EQ(expected_type, temp->type());
208       EXPECT_TRUE(expected_value->Equals(temp));
209     } else {
210       EXPECT_FALSE(dictionary->HasKey("test"));
211     }
212 
213     v8::Local<v8::Array> array(v8::Array::New(isolate_));
214     array->Set(context, 0, val).Check();
215     std::unique_ptr<base::ListValue> list(
216         base::ListValue::From(converter.FromV8Value(array, context)));
217     ASSERT_TRUE(list.get());
218     if (expected_value) {
219       base::Value* temp = nullptr;
220       ASSERT_TRUE(list->Get(0, &temp));
221       EXPECT_EQ(expected_type, temp->type());
222       EXPECT_TRUE(expected_value->Equals(temp));
223     } else {
224       // Arrays should preserve their length, and convert unconvertible
225       // types into null.
226       base::Value* temp = nullptr;
227       ASSERT_TRUE(list->Get(0, &temp));
228       EXPECT_EQ(base::Value::Type::NONE, temp->type());
229     }
230   }
231 
232   template <typename T>
CompileRun(v8::Local<v8::Context> context,const char * source)233   v8::Local<T> CompileRun(v8::Local<v8::Context> context, const char* source) {
234     return v8::Script::Compile(
235                context,
236                v8::String::NewFromUtf8(isolate_, source).ToLocalChecked())
237         .ToLocalChecked()
238         ->Run(context)
239         .ToLocalChecked()
240         .As<T>();
241   }
242 
243   base::test::TaskEnvironment task_environment_;
244 
245   v8::Isolate* isolate_;
246 
247   // Context for the JavaScript in the test.
248   v8::Persistent<v8::Context> context_;
249 };
250 
TEST_F(V8ValueConverterImplTest,BasicRoundTrip)251 TEST_F(V8ValueConverterImplTest, BasicRoundTrip) {
252   std::unique_ptr<base::Value> original_root = base::test::ParseJsonDeprecated(
253       "{ \n"
254       "  \"null\": null, \n"
255       "  \"true\": true, \n"
256       "  \"false\": false, \n"
257       "  \"positive-int\": 42, \n"
258       "  \"negative-int\": -42, \n"
259       "  \"zero\": 0, \n"
260       "  \"double\": 88.8, \n"
261       "  \"big-integral-double\": 9007199254740992.0, \n"  // 2.0^53
262       "  \"string\": \"foobar\", \n"
263       "  \"empty-string\": \"\", \n"
264       "  \"dictionary\": { \n"
265       "    \"foo\": \"bar\",\n"
266       "    \"hot\": \"dog\",\n"
267       "  }, \n"
268       "  \"empty-dictionary\": {}, \n"
269       "  \"list\": [ \"bar\", \"foo\" ], \n"
270       "  \"empty-list\": [], \n"
271       "}");
272 
273   v8::HandleScope handle_scope(isolate_);
274   v8::Local<v8::Context> context =
275       v8::Local<v8::Context>::New(isolate_, context_);
276   v8::Context::Scope context_scope(context);
277 
278   V8ValueConverterImpl converter;
279   v8::Local<v8::Object> v8_object =
280       converter.ToV8Value(original_root.get(), context).As<v8::Object>();
281   ASSERT_FALSE(v8_object.IsEmpty());
282 
283   EXPECT_EQ(static_cast<const base::DictionaryValue&>(*original_root).size(),
284             v8_object->GetPropertyNames(context).ToLocalChecked()->Length());
285   EXPECT_TRUE(
286       v8_object
287           ->Get(context, v8::String::NewFromUtf8(
288                              isolate_, "null", v8::NewStringType::kInternalized)
289                              .ToLocalChecked())
290           .ToLocalChecked()
291           ->IsNull());
292   EXPECT_TRUE(
293       v8_object
294           ->Get(context, v8::String::NewFromUtf8(
295                              isolate_, "true", v8::NewStringType::kInternalized)
296                              .ToLocalChecked())
297           .ToLocalChecked()
298           ->IsTrue());
299   EXPECT_TRUE(v8_object
300                   ->Get(context,
301                         v8::String::NewFromUtf8(
302                             isolate_, "false", v8::NewStringType::kInternalized)
303                             .ToLocalChecked())
304                   .ToLocalChecked()
305                   ->IsFalse());
306   EXPECT_TRUE(v8_object
307                   ->Get(context, v8::String::NewFromUtf8(
308                                      isolate_, "positive-int",
309                                      v8::NewStringType::kInternalized)
310                                      .ToLocalChecked())
311                   .ToLocalChecked()
312                   ->IsInt32());
313   EXPECT_TRUE(v8_object
314                   ->Get(context, v8::String::NewFromUtf8(
315                                      isolate_, "negative-int",
316                                      v8::NewStringType::kInternalized)
317                                      .ToLocalChecked())
318                   .ToLocalChecked()
319                   ->IsInt32());
320   EXPECT_TRUE(
321       v8_object
322           ->Get(context, v8::String::NewFromUtf8(
323                              isolate_, "zero", v8::NewStringType::kInternalized)
324                              .ToLocalChecked())
325           .ToLocalChecked()
326           ->IsInt32());
327   EXPECT_TRUE(v8_object
328                   ->Get(context, v8::String::NewFromUtf8(
329                                      isolate_, "double",
330                                      v8::NewStringType::kInternalized)
331                                      .ToLocalChecked())
332                   .ToLocalChecked()
333                   ->IsNumber());
334   EXPECT_TRUE(v8_object
335                   ->Get(context, v8::String::NewFromUtf8(
336                                      isolate_, "big-integral-double",
337                                      v8::NewStringType::kInternalized)
338                                      .ToLocalChecked())
339                   .ToLocalChecked()
340                   ->IsNumber());
341   EXPECT_TRUE(v8_object
342                   ->Get(context, v8::String::NewFromUtf8(
343                                      isolate_, "string",
344                                      v8::NewStringType::kInternalized)
345                                      .ToLocalChecked())
346                   .ToLocalChecked()
347                   ->IsString());
348   EXPECT_TRUE(v8_object
349                   ->Get(context, v8::String::NewFromUtf8(
350                                      isolate_, "empty-string",
351                                      v8::NewStringType::kInternalized)
352                                      .ToLocalChecked())
353                   .ToLocalChecked()
354                   ->IsString());
355   EXPECT_TRUE(v8_object
356                   ->Get(context, v8::String::NewFromUtf8(
357                                      isolate_, "dictionary",
358                                      v8::NewStringType::kInternalized)
359                                      .ToLocalChecked())
360                   .ToLocalChecked()
361                   ->IsObject());
362   EXPECT_TRUE(v8_object
363                   ->Get(context, v8::String::NewFromUtf8(
364                                      isolate_, "empty-dictionary",
365                                      v8::NewStringType::kInternalized)
366                                      .ToLocalChecked())
367                   .ToLocalChecked()
368                   ->IsObject());
369   EXPECT_TRUE(
370       v8_object
371           ->Get(context, v8::String::NewFromUtf8(
372                              isolate_, "list", v8::NewStringType::kInternalized)
373                              .ToLocalChecked())
374           .ToLocalChecked()
375           ->IsArray());
376   EXPECT_TRUE(v8_object
377                   ->Get(context, v8::String::NewFromUtf8(
378                                      isolate_, "empty-list",
379                                      v8::NewStringType::kInternalized)
380                                      .ToLocalChecked())
381                   .ToLocalChecked()
382                   ->IsArray());
383 
384   std::unique_ptr<base::Value> new_root(
385       converter.FromV8Value(v8_object, context));
386   EXPECT_NE(original_root.get(), new_root.get());
387   EXPECT_TRUE(original_root->Equals(new_root.get()));
388 }
389 
TEST_F(V8ValueConverterImplTest,KeysWithDots)390 TEST_F(V8ValueConverterImplTest, KeysWithDots) {
391   std::unique_ptr<base::Value> original =
392       base::test::ParseJsonDeprecated("{ \"foo.bar\": \"baz\" }");
393 
394   v8::HandleScope handle_scope(isolate_);
395   v8::Local<v8::Context> context =
396       v8::Local<v8::Context>::New(isolate_, context_);
397   v8::Context::Scope context_scope(context);
398 
399   V8ValueConverterImpl converter;
400   std::unique_ptr<base::Value> copy(converter.FromV8Value(
401       converter.ToV8Value(original.get(), context), context));
402 
403   EXPECT_TRUE(original->Equals(copy.get()));
404 }
405 
TEST_F(V8ValueConverterImplTest,ObjectExceptions)406 TEST_F(V8ValueConverterImplTest, ObjectExceptions) {
407   v8::HandleScope handle_scope(isolate_);
408   v8::Local<v8::Context> context =
409       v8::Local<v8::Context>::New(isolate_, context_);
410   v8::Context::Scope context_scope(context);
411   v8::MicrotasksScope microtasks(
412       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
413 
414   // Set up objects to throw when reading or writing 'foo'.
415   const char* source =
416       "Object.prototype.__defineSetter__('foo', "
417       "    function() { throw new Error('muah!'); });"
418       "Object.prototype.__defineGetter__('foo', "
419       "    function() { throw new Error('muah!'); });";
420 
421   CompileRun<v8::Value>(context, source);
422 
423   v8::Local<v8::Object> object(v8::Object::New(isolate_));
424   v8::Local<v8::String> bar =
425       v8::String::NewFromUtf8(isolate_, "bar", v8::NewStringType::kInternalized)
426           .ToLocalChecked();
427   object->Set(context, bar, bar).Check();
428 
429   // Converting from v8 value should replace the foo property with null.
430   V8ValueConverterImpl converter;
431   std::unique_ptr<base::DictionaryValue> converted(
432       base::DictionaryValue::From(converter.FromV8Value(object, context)));
433   EXPECT_TRUE(converted.get());
434   // http://code.google.com/p/v8/issues/detail?id=1342
435   // EXPECT_EQ(2u, converted->size());
436   // EXPECT_TRUE(IsNull(converted.get(), "foo"));
437   EXPECT_EQ(1u, converted->size());
438   EXPECT_EQ("bar", GetString(converted.get(), "bar"));
439 
440   // Converting to v8 value should not trigger the setter.
441   converted->SetString("foo", "foo");
442   v8::Local<v8::Object> copy =
443       converter.ToV8Value(converted.get(), context).As<v8::Object>();
444   EXPECT_FALSE(copy.IsEmpty());
445   EXPECT_EQ(2u, copy->GetPropertyNames(context).ToLocalChecked()->Length());
446   EXPECT_EQ("foo", GetString(copy, "foo"));
447   EXPECT_EQ("bar", GetString(copy, "bar"));
448 }
449 
TEST_F(V8ValueConverterImplTest,ArrayExceptions)450 TEST_F(V8ValueConverterImplTest, ArrayExceptions) {
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   v8::MicrotasksScope microtasks(
456       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
457 
458   const char* source = "(function() {"
459       "var arr = [];"
460       "arr.__defineSetter__(0, "
461       "    function() { throw new Error('muah!'); });"
462       "arr.__defineGetter__(0, "
463       "    function() { throw new Error('muah!'); });"
464       "arr[1] = 'bar';"
465       "return arr;"
466       "})();";
467 
468   v8::Local<v8::Array> array = CompileRun<v8::Array>(context, source);
469 
470   // Converting from v8 value should replace the first item with null.
471   V8ValueConverterImpl converter;
472   std::unique_ptr<base::ListValue> converted(
473       base::ListValue::From(converter.FromV8Value(array, context)));
474   ASSERT_TRUE(converted.get());
475   // http://code.google.com/p/v8/issues/detail?id=1342
476   EXPECT_EQ(2u, converted->GetSize());
477   EXPECT_TRUE(IsNull(converted.get(), 0));
478 
479   // Converting to v8 value should not be affected by the getter/setter
480   // because the setters/getters are defined on the array instance, not
481   // on the Array's prototype.
482   converted.reset(static_cast<base::ListValue*>(
483       base::test::ParseJsonDeprecated("[ \"foo\", \"bar\" ]").release()));
484   v8::Local<v8::Array> copy =
485       converter.ToV8Value(converted.get(), context).As<v8::Array>();
486   ASSERT_FALSE(copy.IsEmpty());
487   EXPECT_EQ(2u, copy->Length());
488   EXPECT_EQ("foo", GetString(copy, 0));
489   EXPECT_EQ("bar", GetString(copy, 1));
490 }
491 
TEST_F(V8ValueConverterImplTest,WeirdTypes)492 TEST_F(V8ValueConverterImplTest, WeirdTypes) {
493   v8::HandleScope handle_scope(isolate_);
494   v8::Local<v8::Context> context =
495       v8::Local<v8::Context>::New(isolate_, context_);
496   v8::Context::Scope context_scope(context);
497 
498   v8::Local<v8::RegExp> regex(
499       v8::RegExp::New(context, v8::String::NewFromUtf8Literal(isolate_, "."),
500                       v8::RegExp::kNone)
501           .ToLocalChecked());
502 
503   V8ValueConverterImpl converter;
504   TestWeirdType(converter, v8::Undefined(isolate_),
505                 base::Value::Type::NONE,  // Arbitrary type, result is NULL.
506                 std::unique_ptr<base::Value>());
507   TestWeirdType(converter, v8::Date::New(context, 1000).ToLocalChecked(),
508                 base::Value::Type::DICTIONARY,
509                 std::unique_ptr<base::Value>(new base::DictionaryValue()));
510   TestWeirdType(converter, regex, base::Value::Type::DICTIONARY,
511                 std::unique_ptr<base::Value>(new base::DictionaryValue()));
512 
513   converter.SetDateAllowed(true);
514   TestWeirdType(converter, v8::Date::New(context, 1000).ToLocalChecked(),
515                 base::Value::Type::DOUBLE,
516                 std::unique_ptr<base::Value>(new base::Value(1.0)));
517 
518   converter.SetRegExpAllowed(true);
519   TestWeirdType(converter, regex, base::Value::Type::STRING,
520                 std::unique_ptr<base::Value>(new base::Value("/./")));
521 }
522 
TEST_F(V8ValueConverterImplTest,Prototype)523 TEST_F(V8ValueConverterImplTest, Prototype) {
524   v8::HandleScope handle_scope(isolate_);
525   v8::Local<v8::Context> context =
526       v8::Local<v8::Context>::New(isolate_, context_);
527   v8::Context::Scope context_scope(context);
528   v8::MicrotasksScope microtasks(
529       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
530 
531   const char* source = "(function() {"
532       "Object.prototype.foo = 'foo';"
533       "return {};"
534       "})();";
535 
536   v8::Local<v8::Object> object = CompileRun<v8::Object>(context, source);
537 
538   V8ValueConverterImpl converter;
539   std::unique_ptr<base::DictionaryValue> result(
540       base::DictionaryValue::From(converter.FromV8Value(object, context)));
541   ASSERT_TRUE(result.get());
542   EXPECT_EQ(0u, result->size());
543 }
544 
TEST_F(V8ValueConverterImplTest,ObjectPrototypeSetter)545 TEST_F(V8ValueConverterImplTest, ObjectPrototypeSetter) {
546   std::unique_ptr<base::Value> original =
547       base::test::ParseJsonDeprecated("{ \"foo\": \"good value\" }");
548 
549   v8::HandleScope handle_scope(isolate_);
550   v8::Local<v8::Context> context =
551       v8::Local<v8::Context>::New(isolate_, context_);
552   v8::Context::Scope context_scope(context);
553   v8::MicrotasksScope microtasks(isolate_,
554                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
555 
556   const char* source =
557       "var result = { getters: 0, setters: 0 };"
558       "Object.defineProperty(Object.prototype, 'foo', {"
559       "  get() { ++result.getters; return 'bogus'; },"
560       "  set() { ++result.setters; },"
561       "});"
562       "result;";
563 
564   const char* source_sanity =
565       "({}).foo = 'Trigger setter';"
566       "({}).foo;";
567 
568   v8::Local<v8::Object> result = CompileRun<v8::Object>(context, source);
569 
570   // Sanity checks: the getters/setters are normally triggered.
571   CompileRun<v8::Value>(context, source_sanity);
572   EXPECT_EQ(1, GetInt(result, "getters"));
573   EXPECT_EQ(1, GetInt(result, "setters"));
574 
575   V8ValueConverterImpl converter;
576   v8::Local<v8::Object> converted =
577       converter.ToV8Value(original.get(), context).As<v8::Object>();
578   EXPECT_FALSE(converted.IsEmpty());
579 
580   // Getters/setters shouldn't be triggered.
581   EXPECT_EQ(1, GetInt(result, "getters"));
582   EXPECT_EQ(1, GetInt(result, "setters"));
583 
584   EXPECT_EQ(1u,
585             converted->GetPropertyNames(context).ToLocalChecked()->Length());
586   EXPECT_EQ("good value", GetString(converted, "foo"));
587 
588   // Getters/setters shouldn't be triggered while accessing existing values.
589   EXPECT_EQ(1, GetInt(result, "getters"));
590   EXPECT_EQ(1, GetInt(result, "setters"));
591 
592   // Repeat the same exercise with a dictionary without the key.
593   base::DictionaryValue missing_key_dict;
594   missing_key_dict.SetString("otherkey", "hello");
595   v8::Local<v8::Object> converted2 =
596       converter.ToV8Value(&missing_key_dict, context).As<v8::Object>();
597   EXPECT_FALSE(converted2.IsEmpty());
598 
599   // Getters/setters shouldn't be triggered.
600   EXPECT_EQ(1, GetInt(result, "getters"));
601   EXPECT_EQ(1, GetInt(result, "setters"));
602 
603   EXPECT_EQ(1u,
604             converted2->GetPropertyNames(context).ToLocalChecked()->Length());
605   EXPECT_EQ("hello", GetString(converted2, "otherkey"));
606 
607   // Missing key = should trigger getter upon access.
608   EXPECT_EQ("bogus", GetString(converted2, "foo"));
609   EXPECT_EQ(2, GetInt(result, "getters"));
610   EXPECT_EQ(1, GetInt(result, "setters"));
611 }
612 
TEST_F(V8ValueConverterImplTest,ArrayPrototypeSetter)613 TEST_F(V8ValueConverterImplTest, ArrayPrototypeSetter) {
614   std::unique_ptr<base::Value> original =
615       base::test::ParseJsonDeprecated("[100, 200, 300]");
616 
617   v8::HandleScope handle_scope(isolate_);
618   v8::Local<v8::Context> context =
619       v8::Local<v8::Context>::New(isolate_, context_);
620   v8::Context::Scope context_scope(context);
621   v8::MicrotasksScope microtasks(isolate_,
622                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
623 
624   const char* source =
625       "var result = { getters: 0, setters: 0 };"
626       "Object.defineProperty(Array.prototype, '1', {"
627       "  get() { ++result.getters; return 1337; },"
628       "  set() { ++result.setters; },"
629       "});"
630       "result;";
631 
632   const char* source_sanity =
633       "[][1] = 'Trigger setter';"
634       "[][1];";
635 
636   v8::Local<v8::Object> result = CompileRun<v8::Object>(context, source);
637 
638   // Sanity checks: the getters/setters are normally triggered.
639   CompileRun<v8::Value>(context, source_sanity);
640   EXPECT_EQ(1, GetInt(result, "getters"));
641   EXPECT_EQ(1, GetInt(result, "setters"));
642 
643   V8ValueConverterImpl converter;
644   v8::Local<v8::Array> converted =
645       converter.ToV8Value(original.get(), context).As<v8::Array>();
646   EXPECT_FALSE(converted.IsEmpty());
647 
648   // Getters/setters shouldn't be triggered during the conversion.
649   EXPECT_EQ(1, GetInt(result, "getters"));
650   EXPECT_EQ(1, GetInt(result, "setters"));
651 
652   EXPECT_EQ(3u, converted->Length());
653   EXPECT_EQ(100, GetInt(converted, 0));
654   EXPECT_EQ(200, GetInt(converted, 1));
655   EXPECT_EQ(300, GetInt(converted, 2));
656 
657   // Getters/setters shouldn't be triggered while accessing existing values.
658   EXPECT_EQ(1, GetInt(result, "getters"));
659   EXPECT_EQ(1, GetInt(result, "setters"));
660 
661   // Try again, using an array without the index.
662   base::ListValue one_item_list;
663   one_item_list.AppendInteger(123456);
664   v8::Local<v8::Array> converted2 =
665       converter.ToV8Value(&one_item_list, context).As<v8::Array>();
666   EXPECT_FALSE(converted2.IsEmpty());
667 
668   // Getters/setters shouldn't be triggered during the conversion.
669   EXPECT_EQ(1, GetInt(result, "getters"));
670   EXPECT_EQ(1, GetInt(result, "setters"));
671 
672   EXPECT_EQ(1u, converted2->Length());
673   EXPECT_EQ(123456, GetInt(converted2, 0));
674 
675   // Accessing missing index 1 triggers the getter.
676   EXPECT_EQ(1337, GetInt(converted2, 1));
677   EXPECT_EQ(2, GetInt(result, "getters"));
678   EXPECT_EQ(1, GetInt(result, "setters"));
679 }
680 
TEST_F(V8ValueConverterImplTest,StripNullFromObjects)681 TEST_F(V8ValueConverterImplTest, StripNullFromObjects) {
682   v8::HandleScope handle_scope(isolate_);
683   v8::Local<v8::Context> context =
684       v8::Local<v8::Context>::New(isolate_, context_);
685   v8::Context::Scope context_scope(context);
686   v8::MicrotasksScope microtasks(
687       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
688 
689   const char* source = "(function() {"
690       "return { foo: undefined, bar: null };"
691       "})();";
692 
693   v8::Local<v8::Object> object = CompileRun<v8::Object>(context, source);
694 
695   V8ValueConverterImpl converter;
696   converter.SetStripNullFromObjects(true);
697 
698   std::unique_ptr<base::DictionaryValue> result(
699       base::DictionaryValue::From(converter.FromV8Value(object, context)));
700   ASSERT_TRUE(result.get());
701   EXPECT_EQ(0u, result->size());
702 }
703 
TEST_F(V8ValueConverterImplTest,RecursiveObjects)704 TEST_F(V8ValueConverterImplTest, RecursiveObjects) {
705   v8::HandleScope handle_scope(isolate_);
706   v8::Local<v8::Context> context =
707       v8::Local<v8::Context>::New(isolate_, context_);
708   v8::Context::Scope context_scope(context);
709 
710   V8ValueConverterImpl converter;
711 
712   v8::Local<v8::Object> object = v8::Object::New(isolate_).As<v8::Object>();
713   ASSERT_FALSE(object.IsEmpty());
714   object
715       ->Set(context,
716             v8::String::NewFromUtf8(isolate_, "foo",
717                                     v8::NewStringType::kInternalized)
718                 .ToLocalChecked(),
719             v8::String::NewFromUtf8Literal(isolate_, "bar"))
720       .Check();
721   object
722       ->Set(context,
723             v8::String::NewFromUtf8(isolate_, "obj",
724                                     v8::NewStringType::kInternalized)
725                 .ToLocalChecked(),
726             object)
727       .Check();
728 
729   std::unique_ptr<base::DictionaryValue> object_result(
730       base::DictionaryValue::From(converter.FromV8Value(object, context)));
731   ASSERT_TRUE(object_result.get());
732   EXPECT_EQ(2u, object_result->size());
733   EXPECT_TRUE(IsNull(object_result.get(), "obj"));
734 
735   v8::Local<v8::Array> array = v8::Array::New(isolate_).As<v8::Array>();
736   ASSERT_FALSE(array.IsEmpty());
737   array->Set(context, 0, v8::String::NewFromUtf8Literal(isolate_, "1")).Check();
738   array->Set(context, 1, array).Check();
739 
740   std::unique_ptr<base::ListValue> list_result(
741       base::ListValue::From(converter.FromV8Value(array, context)));
742   ASSERT_TRUE(list_result.get());
743   EXPECT_EQ(2u, list_result->GetSize());
744   EXPECT_TRUE(IsNull(list_result.get(), 1));
745 }
746 
TEST_F(V8ValueConverterImplTest,WeirdProperties)747 TEST_F(V8ValueConverterImplTest, WeirdProperties) {
748   v8::HandleScope handle_scope(isolate_);
749   v8::Local<v8::Context> context =
750       v8::Local<v8::Context>::New(isolate_, context_);
751   v8::Context::Scope context_scope(context);
752   v8::MicrotasksScope microtasks(
753       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
754 
755   const char* source = "(function() {"
756       "return {"
757         "1: 'foo',"
758         "'2': 'bar',"
759         "true: 'baz',"
760         "false: 'qux',"
761         "null: 'quux',"
762         "undefined: 'oops'"
763       "};"
764       "})();";
765 
766   v8::Local<v8::Object> object = CompileRun<v8::Object>(context, source);
767 
768   V8ValueConverterImpl converter;
769   std::unique_ptr<base::Value> actual(converter.FromV8Value(object, context));
770 
771   std::unique_ptr<base::Value> expected = base::test::ParseJsonDeprecated(
772       "{ \n"
773       "  \"1\": \"foo\", \n"
774       "  \"2\": \"bar\", \n"
775       "  \"true\": \"baz\", \n"
776       "  \"false\": \"qux\", \n"
777       "  \"null\": \"quux\", \n"
778       "  \"undefined\": \"oops\", \n"
779       "}");
780 
781   EXPECT_TRUE(expected->Equals(actual.get()));
782 }
783 
TEST_F(V8ValueConverterImplTest,ArrayGetters)784 TEST_F(V8ValueConverterImplTest, ArrayGetters) {
785   v8::HandleScope handle_scope(isolate_);
786   v8::Local<v8::Context> context =
787       v8::Local<v8::Context>::New(isolate_, context_);
788   v8::Context::Scope context_scope(context);
789   v8::MicrotasksScope microtasks(
790       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
791 
792   const char* source = "(function() {"
793       "var a = [0];"
794       "a.__defineGetter__(1, function() { return 'bar'; });"
795       "return a;"
796       "})();";
797 
798   v8::Local<v8::Array> array = CompileRun<v8::Array>(context, source);
799 
800   V8ValueConverterImpl converter;
801   std::unique_ptr<base::ListValue> result(
802       base::ListValue::From(converter.FromV8Value(array, context)));
803   ASSERT_TRUE(result.get());
804   EXPECT_EQ(2u, result->GetSize());
805 }
806 
TEST_F(V8ValueConverterImplTest,UndefinedValueBehavior)807 TEST_F(V8ValueConverterImplTest, UndefinedValueBehavior) {
808   v8::HandleScope handle_scope(isolate_);
809   v8::Local<v8::Context> context =
810       v8::Local<v8::Context>::New(isolate_, context_);
811   v8::Context::Scope context_scope(context);
812   v8::MicrotasksScope microtasks(
813       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
814 
815   v8::Local<v8::Object> object;
816   {
817     const char* source = "(function() {"
818         "return { foo: undefined, bar: null, baz: function(){} };"
819         "})();";
820     object = CompileRun<v8::Object>(context, source);
821   }
822 
823   v8::Local<v8::Array> array;
824   {
825     const char* source = "(function() {"
826         "return [ undefined, null, function(){} ];"
827         "})();";
828     array = CompileRun<v8::Array>(context, source);
829   }
830 
831   v8::Local<v8::Array> sparse_array;
832   {
833     const char* source = "(function() {"
834         "return new Array(3);"
835         "})();";
836     sparse_array = CompileRun<v8::Array>(context, source);
837   }
838 
839   V8ValueConverterImpl converter;
840 
841   std::unique_ptr<base::Value> actual_object(
842       converter.FromV8Value(object, context));
843   EXPECT_EQ(*base::test::ParseJsonDeprecated("{ \"bar\": null }"),
844             *actual_object);
845 
846   // Everything is null because JSON stringification preserves array length.
847   std::unique_ptr<base::Value> actual_array(
848       converter.FromV8Value(array, context));
849   EXPECT_EQ(*base::test::ParseJsonDeprecated("[ null, null, null ]"),
850             *actual_array);
851 
852   std::unique_ptr<base::Value> actual_sparse_array(
853       converter.FromV8Value(sparse_array, context));
854   EXPECT_EQ(*base::test::ParseJsonDeprecated("[ null, null, null ]"),
855             *actual_sparse_array);
856 }
857 
TEST_F(V8ValueConverterImplTest,ObjectsWithClashingIdentityHash)858 TEST_F(V8ValueConverterImplTest, ObjectsWithClashingIdentityHash) {
859   v8::HandleScope handle_scope(isolate_);
860   v8::Local<v8::Context> context =
861       v8::Local<v8::Context>::New(isolate_, context_);
862   v8::Context::Scope context_scope(context);
863   V8ValueConverterImpl converter;
864 
865   // We check that the converter checks identity correctly by disabling the
866   // optimization of using identity hashes.
867   ScopedAvoidIdentityHashForTesting scoped_hash_avoider(&converter);
868 
869   // Create the v8::Object to be converted.
870   v8::Local<v8::Array> root(v8::Array::New(isolate_, 4));
871   root->Set(context, 0, v8::Local<v8::Object>(v8::Object::New(isolate_)))
872       .Check();
873   root->Set(context, 1, v8::Local<v8::Object>(v8::Object::New(isolate_)))
874       .Check();
875   root->Set(context, 2, v8::Local<v8::Object>(v8::Array::New(isolate_, 0)))
876       .Check();
877   root->Set(context, 3, v8::Local<v8::Object>(v8::Array::New(isolate_, 0)))
878       .Check();
879 
880   // The expected base::Value result.
881   std::unique_ptr<base::Value> expected =
882       base::test::ParseJsonDeprecated("[{},{},[],[]]");
883   ASSERT_TRUE(expected.get());
884 
885   // The actual result.
886   std::unique_ptr<base::Value> value(converter.FromV8Value(root, context));
887   ASSERT_TRUE(value.get());
888 
889   EXPECT_TRUE(expected->Equals(value.get()));
890 }
891 
TEST_F(V8ValueConverterImplTest,DetectCycles)892 TEST_F(V8ValueConverterImplTest, DetectCycles) {
893   v8::HandleScope handle_scope(isolate_);
894   v8::Local<v8::Context> context =
895       v8::Local<v8::Context>::New(isolate_, context_);
896   v8::Context::Scope context_scope(context);
897   V8ValueConverterImpl converter;
898 
899   // Create a recursive array.
900   v8::Local<v8::Array> recursive_array(v8::Array::New(isolate_, 1));
901   recursive_array->Set(context, 0, recursive_array).Check();
902 
903   // The first repetition should be trimmed and replaced by a null value.
904   base::ListValue expected_list;
905   expected_list.Append(std::make_unique<base::Value>());
906 
907   // The actual result.
908   std::unique_ptr<base::Value> actual_list(
909       converter.FromV8Value(recursive_array, context));
910   ASSERT_TRUE(actual_list.get());
911 
912   EXPECT_TRUE(expected_list.Equals(actual_list.get()));
913 
914   // Now create a recursive object
915   const std::string key("key");
916   v8::Local<v8::Object> recursive_object(v8::Object::New(isolate_));
917   recursive_object
918       ->Set(context,
919             v8::String::NewFromUtf8(isolate_, key.c_str(),
920                                     v8::NewStringType::kInternalized,
921                                     key.length())
922                 .ToLocalChecked(),
923             recursive_object)
924       .Check();
925 
926   // The first repetition should be trimmed and replaced by a null value.
927   base::DictionaryValue expected_dictionary;
928   expected_dictionary.Set(key, std::make_unique<base::Value>());
929 
930   // The actual result.
931   std::unique_ptr<base::Value> actual_dictionary(
932       converter.FromV8Value(recursive_object, context));
933   ASSERT_TRUE(actual_dictionary.get());
934 
935   EXPECT_TRUE(expected_dictionary.Equals(actual_dictionary.get()));
936 }
937 
938 // Tests that reused object values with no cycles do not get nullified.
TEST_F(V8ValueConverterImplTest,ReuseObjects)939 TEST_F(V8ValueConverterImplTest, ReuseObjects) {
940   v8::HandleScope handle_scope(isolate_);
941   v8::Local<v8::Context> context =
942       v8::Local<v8::Context>::New(isolate_, context_);
943   v8::Context::Scope context_scope(context);
944   v8::MicrotasksScope microtasks(
945       isolate_, v8::MicrotasksScope::kDoNotRunMicrotasks);
946   V8ValueConverterImpl converter;
947 
948   // Object with reused values in different keys.
949   {
950     const char* source = "(function() {"
951         "var objA = {key: 'another same value'};"
952         "var obj = {one: objA, two: objA};"
953         "return obj;"
954         "})();";
955     v8::Local<v8::Object> object = CompileRun<v8::Object>(context, source);
956 
957     // The actual result.
958     std::unique_ptr<base::DictionaryValue> result(
959         base::DictionaryValue::From(converter.FromV8Value(object, context)));
960     ASSERT_TRUE(result.get());
961     EXPECT_EQ(2u, result->size());
962 
963     {
964       base::DictionaryValue* one_dict = nullptr;
965       const char* key1 = "one";
966       ASSERT_TRUE(result->GetDictionary(key1, &one_dict));
967       EXPECT_EQ("another same value", GetString(one_dict, "key"));
968     }
969     {
970       base::DictionaryValue* two_dict = nullptr;
971       const char* key2 = "two";
972       ASSERT_TRUE(result->GetDictionary(key2, &two_dict));
973       EXPECT_EQ("another same value", GetString(two_dict, "key"));
974     }
975   }
976 
977   // Array with reused values.
978   {
979     const char* source = "(function() {"
980         "var objA = {key: 'same value'};"
981         "var arr = [objA, objA];"
982         "return arr;"
983         "})();";
984     v8::Local<v8::Array> array = CompileRun<v8::Array>(context, source);
985 
986     // The actual result.
987     std::unique_ptr<base::ListValue> list_result(
988         base::ListValue::From(converter.FromV8Value(array, context)));
989     ASSERT_TRUE(list_result.get());
990     ASSERT_EQ(2u, list_result->GetSize());
991     for (size_t i = 0; i < list_result->GetSize(); ++i) {
992       ASSERT_FALSE(IsNull(list_result.get(), i));
993       base::DictionaryValue* dict_value = nullptr;
994       ASSERT_TRUE(list_result->GetDictionary(0u, &dict_value));
995       EXPECT_EQ("same value", GetString(dict_value, "key"));
996     }
997   }
998 }
999 
TEST_F(V8ValueConverterImplTest,MaxRecursionDepth)1000 TEST_F(V8ValueConverterImplTest, MaxRecursionDepth) {
1001   v8::HandleScope handle_scope(isolate_);
1002   v8::Local<v8::Context> context =
1003       v8::Local<v8::Context>::New(isolate_, context_);
1004   v8::Context::Scope context_scope(context);
1005 
1006   // Must larger than kMaxRecursionDepth in v8_value_converter_impl.cc.
1007   int kDepth = 1000;
1008   const char kKey[] = "key";
1009 
1010   v8::Local<v8::Object> deep_object = v8::Object::New(isolate_);
1011 
1012   v8::Local<v8::Object> leaf = deep_object;
1013   for (int i = 0; i < kDepth; ++i) {
1014     v8::Local<v8::Object> new_object = v8::Object::New(isolate_);
1015     leaf->Set(context,
1016               v8::String::NewFromUtf8(isolate_, kKey,
1017                                       v8::NewStringType::kInternalized)
1018                   .ToLocalChecked(),
1019               new_object)
1020         .Check();
1021     leaf = new_object;
1022   }
1023 
1024   V8ValueConverterImpl converter;
1025   std::unique_ptr<base::Value> value(
1026       converter.FromV8Value(deep_object, context));
1027   ASSERT_TRUE(value);
1028 
1029   // Expected depth is kMaxRecursionDepth in v8_value_converter_impl.cc.
1030   int kExpectedDepth = 100;
1031 
1032   base::Value* current = value.get();
1033   for (int i = 1; i < kExpectedDepth; ++i) {
1034     base::DictionaryValue* current_as_object = nullptr;
1035     ASSERT_TRUE(current->GetAsDictionary(&current_as_object)) << i;
1036     ASSERT_TRUE(current_as_object->Get(kKey, &current)) << i;
1037   }
1038 
1039   // The leaf node shouldn't have any properties.
1040   base::DictionaryValue empty;
1041   EXPECT_EQ(empty, *current) << *current;
1042 }
1043 
TEST_F(V8ValueConverterImplTest,NegativeZero)1044 TEST_F(V8ValueConverterImplTest, NegativeZero) {
1045   v8::HandleScope handle_scope(isolate_);
1046   v8::Local<v8::Context> context =
1047       v8::Local<v8::Context>::New(isolate_, context_);
1048   v8::MicrotasksScope microtasks(isolate_,
1049                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
1050 
1051   v8::Context::Scope context_scope(context);
1052   const char* source = "(function() { return -0; })();";
1053 
1054   v8::Local<v8::Value> value = CompileRun<v8::Value>(context, source);
1055 
1056   {
1057     V8ValueConverterImpl converter;
1058     std::unique_ptr<base::Value> result = converter.FromV8Value(value, context);
1059     ASSERT_TRUE(result->is_double())
1060         << base::Value::GetTypeName(result->type());
1061     EXPECT_EQ(0, result->GetDouble());
1062   }
1063   {
1064     V8ValueConverterImpl converter;
1065     converter.SetConvertNegativeZeroToInt(true);
1066     std::unique_ptr<base::Value> result = converter.FromV8Value(value, context);
1067     ASSERT_TRUE(result->is_int()) << base::Value::GetTypeName(result->type());
1068     EXPECT_EQ(0, result->GetInt());
1069   }
1070 }
1071 
1072 class V8ValueConverterOverridingStrategyForTesting
1073     : public V8ValueConverter::Strategy {
1074  public:
V8ValueConverterOverridingStrategyForTesting()1075   V8ValueConverterOverridingStrategyForTesting()
1076       : reference_value_(NewReferenceValue()) {}
FromV8Object(v8::Local<v8::Object> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1077   bool FromV8Object(v8::Local<v8::Object> value,
1078                     std::unique_ptr<base::Value>* out,
1079                     v8::Isolate* isolate) override {
1080     *out = NewReferenceValue();
1081     return true;
1082   }
FromV8Array(v8::Local<v8::Array> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1083   bool FromV8Array(v8::Local<v8::Array> value,
1084                    std::unique_ptr<base::Value>* out,
1085                    v8::Isolate* isolate) override {
1086     *out = NewReferenceValue();
1087     return true;
1088   }
FromV8ArrayBuffer(v8::Local<v8::Object> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1089   bool FromV8ArrayBuffer(v8::Local<v8::Object> value,
1090                          std::unique_ptr<base::Value>* out,
1091                          v8::Isolate* isolate) override {
1092     *out = NewReferenceValue();
1093     return true;
1094   }
FromV8Number(v8::Local<v8::Number> value,std::unique_ptr<base::Value> * out)1095   bool FromV8Number(v8::Local<v8::Number> value,
1096                     std::unique_ptr<base::Value>* out) override {
1097     *out = NewReferenceValue();
1098     return true;
1099   }
FromV8Undefined(std::unique_ptr<base::Value> * out)1100   bool FromV8Undefined(std::unique_ptr<base::Value>* out) override {
1101     *out = NewReferenceValue();
1102     return true;
1103   }
reference_value() const1104   base::Value* reference_value() const { return reference_value_.get(); }
1105 
1106  private:
NewReferenceValue()1107   static std::unique_ptr<base::Value> NewReferenceValue() {
1108     return std::make_unique<base::Value>("strategy");
1109   }
1110   std::unique_ptr<base::Value> reference_value_;
1111 };
1112 
TEST_F(V8ValueConverterImplTest,StrategyOverrides)1113 TEST_F(V8ValueConverterImplTest, StrategyOverrides) {
1114   v8::HandleScope handle_scope(isolate_);
1115   v8::Local<v8::Context> context =
1116       v8::Local<v8::Context>::New(isolate_, context_);
1117   v8::Context::Scope context_scope(context);
1118 
1119   V8ValueConverterImpl converter;
1120   V8ValueConverterOverridingStrategyForTesting strategy;
1121   converter.SetStrategy(&strategy);
1122 
1123   v8::Local<v8::Object> object(v8::Object::New(isolate_));
1124   std::unique_ptr<base::Value> object_value(
1125       converter.FromV8Value(object, context));
1126   ASSERT_TRUE(object_value);
1127   EXPECT_EQ(*strategy.reference_value(), *object_value);
1128 
1129   v8::Local<v8::Array> array(v8::Array::New(isolate_));
1130   std::unique_ptr<base::Value> array_value(
1131       converter.FromV8Value(array, context));
1132   ASSERT_TRUE(array_value);
1133   EXPECT_EQ(*strategy.reference_value(), *array_value);
1134 
1135   v8::Local<v8::ArrayBuffer> array_buffer(v8::ArrayBuffer::New(isolate_, 0));
1136   std::unique_ptr<base::Value> array_buffer_value(
1137       converter.FromV8Value(array_buffer, context));
1138   ASSERT_TRUE(array_buffer_value);
1139   EXPECT_EQ(*strategy.reference_value(), *array_buffer_value);
1140 
1141   v8::Local<v8::ArrayBufferView> array_buffer_view(
1142       v8::Uint8Array::New(array_buffer, 0, 0));
1143   std::unique_ptr<base::Value> array_buffer_view_value(
1144       converter.FromV8Value(array_buffer_view, context));
1145   ASSERT_TRUE(array_buffer_view_value);
1146   EXPECT_EQ(*strategy.reference_value(), *array_buffer_view_value);
1147 
1148   v8::Local<v8::Number> number(v8::Number::New(isolate_, 0.0));
1149   std::unique_ptr<base::Value> number_value(
1150       converter.FromV8Value(number, context));
1151   ASSERT_TRUE(number_value);
1152   EXPECT_EQ(*strategy.reference_value(), *number_value);
1153 
1154   v8::Local<v8::Primitive> undefined(v8::Undefined(isolate_));
1155   std::unique_ptr<base::Value> undefined_value(
1156       converter.FromV8Value(undefined, context));
1157   ASSERT_TRUE(undefined_value);
1158   EXPECT_EQ(*strategy.reference_value(), *undefined_value);
1159 }
1160 
1161 class V8ValueConverterBypassStrategyForTesting
1162     : public V8ValueConverter::Strategy {
1163  public:
FromV8Object(v8::Local<v8::Object> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1164   bool FromV8Object(v8::Local<v8::Object> value,
1165                     std::unique_ptr<base::Value>* out,
1166                     v8::Isolate* isolate) override {
1167     return false;
1168   }
FromV8Array(v8::Local<v8::Array> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1169   bool FromV8Array(v8::Local<v8::Array> value,
1170                    std::unique_ptr<base::Value>* out,
1171                    v8::Isolate* isolate) override {
1172     return false;
1173   }
FromV8ArrayBuffer(v8::Local<v8::Object> value,std::unique_ptr<base::Value> * out,v8::Isolate * isolate)1174   bool FromV8ArrayBuffer(v8::Local<v8::Object> value,
1175                          std::unique_ptr<base::Value>* out,
1176                          v8::Isolate* isolate) override {
1177     return false;
1178   }
FromV8Number(v8::Local<v8::Number> value,std::unique_ptr<base::Value> * out)1179   bool FromV8Number(v8::Local<v8::Number> value,
1180                     std::unique_ptr<base::Value>* out) override {
1181     return false;
1182   }
FromV8Undefined(std::unique_ptr<base::Value> * out)1183   bool FromV8Undefined(std::unique_ptr<base::Value>* out) override {
1184     return false;
1185   }
1186 };
1187 
1188 // Verify that having a strategy that fallbacks to default behaviour
1189 // actually preserves it.
TEST_F(V8ValueConverterImplTest,StrategyBypass)1190 TEST_F(V8ValueConverterImplTest, StrategyBypass) {
1191   v8::HandleScope handle_scope(isolate_);
1192   v8::Local<v8::Context> context =
1193       v8::Local<v8::Context>::New(isolate_, context_);
1194   v8::Context::Scope context_scope(context);
1195 
1196   V8ValueConverterImpl converter;
1197   V8ValueConverterBypassStrategyForTesting strategy;
1198   converter.SetStrategy(&strategy);
1199 
1200   v8::Local<v8::Object> object(v8::Object::New(isolate_));
1201   std::unique_ptr<base::Value> object_value(
1202       converter.FromV8Value(object, context));
1203   ASSERT_TRUE(object_value);
1204   std::unique_ptr<base::Value> reference_object_value(
1205       base::test::ParseJsonDeprecated("{}"));
1206   EXPECT_EQ(*reference_object_value, *object_value);
1207 
1208   v8::Local<v8::Array> array(v8::Array::New(isolate_));
1209   std::unique_ptr<base::Value> array_value(
1210       converter.FromV8Value(array, context));
1211   ASSERT_TRUE(array_value);
1212   std::unique_ptr<base::Value> reference_array_value(
1213       base::test::ParseJsonDeprecated("[]"));
1214   EXPECT_EQ(*reference_array_value, *array_value);
1215 
1216   const char kExampleData[] = {1, 2, 3, 4, 5};
1217   v8::Local<v8::ArrayBuffer> array_buffer(
1218       v8::ArrayBuffer::New(isolate_, sizeof(kExampleData)));
1219   memcpy(array_buffer->GetContents().Data(), kExampleData,
1220          sizeof(kExampleData));
1221   std::unique_ptr<base::Value> binary_value(
1222       converter.FromV8Value(array_buffer, context));
1223   ASSERT_TRUE(binary_value);
1224   base::Value reference_binary_value(
1225       base::as_bytes(base::make_span(kExampleData)));
1226   EXPECT_EQ(reference_binary_value, *binary_value);
1227 
1228   v8::Local<v8::ArrayBufferView> array_buffer_view(
1229       v8::Uint8Array::New(array_buffer, 1, 3));
1230   std::unique_ptr<base::Value> binary_view_value(
1231       converter.FromV8Value(array_buffer_view, context));
1232   ASSERT_TRUE(binary_view_value);
1233   base::Value reference_binary_view_value(
1234       base::as_bytes(base::make_span(kExampleData).subspan(1, 3)));
1235   EXPECT_EQ(reference_binary_view_value, *binary_view_value);
1236 
1237   v8::Local<v8::Number> number(v8::Number::New(isolate_, 0.0));
1238   std::unique_ptr<base::Value> number_value(
1239       converter.FromV8Value(number, context));
1240   ASSERT_TRUE(number_value);
1241   std::unique_ptr<base::Value> reference_number_value(
1242       base::test::ParseJsonDeprecated("0"));
1243   EXPECT_EQ(*reference_number_value, *number_value);
1244 
1245   v8::Local<v8::Primitive> undefined(v8::Undefined(isolate_));
1246   std::unique_ptr<base::Value> undefined_value(
1247       converter.FromV8Value(undefined, context));
1248   EXPECT_FALSE(undefined_value);
1249 }
1250 
1251 }  // namespace content
1252