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(¤t_as_object)) << i;
1036 ASSERT_TRUE(current_as_object->Get(kKey, ¤t)) << 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