blob: df68e802781da586ac8eb1c5472caacafa0ed9a5 [file] [log] [blame]
[email protected]3de391e82012-05-16 17:50:511// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]b1cf3372011-04-20 21:28:102// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <cmath>
6
[email protected]e57a7162011-06-15 04:14:237#include "base/memory/scoped_ptr.h"
[email protected]6a9b7372013-03-20 15:35:208#include "base/stl_util.h"
[email protected]06d19d82012-12-17 21:45:379#include "base/test/values_test_util.h"
[email protected]b1cf3372011-04-20 21:28:1010#include "base/values.h"
[email protected]8d86f13d2011-10-04 17:01:1911#include "content/renderer/v8_value_converter_impl.h"
[email protected]b1cf3372011-04-20 21:28:1012#include "testing/gtest/include/gtest/gtest.h"
13#include "v8/include/v8.h"
14
[email protected]9a292e22012-10-22 15:19:3115namespace content {
[email protected]e0658bc2012-09-17 04:05:2416
[email protected]6a9b7372013-03-20 15:35:2017// To improve the performance of
18// V8ValueConverterImpl::UpdateAndCheckUniqueness, identity hashes of objects
19// are used during checking for duplicates. For testing purposes we need to
20// ignore the hash sometimes. Create this helper object to avoid using identity
21// hashes for the lifetime of the helper.
22class ScopedAvoidIdentityHashForTesting {
23 public:
24 // The hashes will be ignored in |converter|, which must not be NULL and it
25 // must outlive the created instance of this helper.
26 explicit ScopedAvoidIdentityHashForTesting(
27 content::V8ValueConverterImpl* converter);
28 ~ScopedAvoidIdentityHashForTesting();
29
30 private:
31 content::V8ValueConverterImpl* converter_;
32
33 DISALLOW_COPY_AND_ASSIGN(ScopedAvoidIdentityHashForTesting);
34};
35
36ScopedAvoidIdentityHashForTesting::ScopedAvoidIdentityHashForTesting(
37 content::V8ValueConverterImpl* converter)
38 : converter_(converter) {
39 CHECK(converter_);
40 converter_->avoid_identity_hash_for_testing_ = true;
41}
42
43ScopedAvoidIdentityHashForTesting::~ScopedAvoidIdentityHashForTesting() {
44 converter_->avoid_identity_hash_for_testing_ = false;
45}
46
[email protected]c3e08892012-08-05 00:30:1247namespace {
48
49// A dumb getter for an object's named callback.
50v8::Handle<v8::Value> NamedCallbackGetter(v8::Local<v8::String> name,
51 const v8::AccessorInfo& info) {
52 return v8::String::New("bar");
53}
54
55} // namespace
56
[email protected]8d86f13d2011-10-04 17:01:1957class V8ValueConverterImplTest : public testing::Test {
[email protected]b1cf3372011-04-20 21:28:1058 protected:
59 virtual void SetUp() {
60 v8::HandleScope handle_scope;
61 v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
62 context_ = v8::Context::New(NULL, global);
63 }
64
65 virtual void TearDown() {
[email protected]1f3fff42013-02-12 19:17:2066 context_.Dispose(context_->GetIsolate());
[email protected]b1cf3372011-04-20 21:28:1067 }
68
[email protected]0cd50aa2013-02-12 22:28:0169 std::string GetString(base::DictionaryValue* value, const std::string& key) {
[email protected]786a7fa2011-04-22 21:27:1270 std::string temp;
71 if (!value->GetString(key, &temp)) {
72 ADD_FAILURE();
73 return "";
74 }
75 return temp;
76 }
77
78 std::string GetString(v8::Handle<v8::Object> value, const std::string& key) {
79 v8::Handle<v8::String> temp =
80 value->Get(v8::String::New(key.c_str())).As<v8::String>();
81 if (temp.IsEmpty()) {
82 ADD_FAILURE();
83 return "";
84 }
85 v8::String::Utf8Value utf8(temp);
86 return std::string(*utf8, utf8.length());
87 }
88
[email protected]0cd50aa2013-02-12 22:28:0189 std::string GetString(base::ListValue* value, uint32 index) {
[email protected]786a7fa2011-04-22 21:27:1290 std::string temp;
91 if (!value->GetString(static_cast<size_t>(index), &temp)) {
92 ADD_FAILURE();
93 return "";
94 }
95 return temp;
96 }
97
98 std::string GetString(v8::Handle<v8::Array> value, uint32 index) {
99 v8::Handle<v8::String> temp = value->Get(index).As<v8::String>();
100 if (temp.IsEmpty()) {
101 ADD_FAILURE();
102 return "";
103 }
104 v8::String::Utf8Value utf8(temp);
105 return std::string(*utf8, utf8.length());
106 }
107
[email protected]0cd50aa2013-02-12 22:28:01108 bool IsNull(base::DictionaryValue* value, const std::string& key) {
109 base::Value* child = NULL;
[email protected]786a7fa2011-04-22 21:27:12110 if (!value->Get(key, &child)) {
111 ADD_FAILURE();
112 return false;
113 }
[email protected]0cd50aa2013-02-12 22:28:01114 return child->GetType() == base::Value::TYPE_NULL;
[email protected]786a7fa2011-04-22 21:27:12115 }
116
117 bool IsNull(v8::Handle<v8::Object> value, const std::string& key) {
118 v8::Handle<v8::Value> child = value->Get(v8::String::New(key.c_str()));
119 if (child.IsEmpty()) {
120 ADD_FAILURE();
121 return false;
122 }
123 return child->IsNull();
124 }
125
[email protected]0cd50aa2013-02-12 22:28:01126 bool IsNull(base::ListValue* value, uint32 index) {
127 base::Value* child = NULL;
[email protected]786a7fa2011-04-22 21:27:12128 if (!value->Get(static_cast<size_t>(index), &child)) {
129 ADD_FAILURE();
130 return false;
131 }
[email protected]0cd50aa2013-02-12 22:28:01132 return child->GetType() == base::Value::TYPE_NULL;
[email protected]786a7fa2011-04-22 21:27:12133 }
134
135 bool IsNull(v8::Handle<v8::Array> value, uint32 index) {
136 v8::Handle<v8::Value> child = value->Get(index);
137 if (child.IsEmpty()) {
138 ADD_FAILURE();
139 return false;
140 }
141 return child->IsNull();
142 }
143
[email protected]8d86f13d2011-10-04 17:01:19144 void TestWeirdType(const V8ValueConverterImpl& converter,
[email protected]786a7fa2011-04-22 21:27:12145 v8::Handle<v8::Value> val,
[email protected]bab1c13f2011-08-12 20:59:02146 base::Value::Type expected_type,
[email protected]0cd50aa2013-02-12 22:28:01147 scoped_ptr<base::Value> expected_value) {
148 scoped_ptr<base::Value> raw(converter.FromV8Value(val, context_));
[email protected]e0658bc2012-09-17 04:05:24149
150 if (expected_value.get()) {
151 ASSERT_TRUE(raw.get());
[email protected]786a7fa2011-04-22 21:27:12152 EXPECT_TRUE(expected_value->Equals(raw.get()));
[email protected]e0658bc2012-09-17 04:05:24153 EXPECT_EQ(expected_type, raw->GetType());
154 } else {
155 EXPECT_FALSE(raw.get());
156 }
[email protected]786a7fa2011-04-22 21:27:12157
158 v8::Handle<v8::Object> object(v8::Object::New());
159 object->Set(v8::String::New("test"), val);
[email protected]0cd50aa2013-02-12 22:28:01160 scoped_ptr<base::DictionaryValue> dictionary(
161 static_cast<base::DictionaryValue*>(
[email protected]786a7fa2011-04-22 21:27:12162 converter.FromV8Value(object, context_)));
163 ASSERT_TRUE(dictionary.get());
[email protected]217d9562012-07-16 21:57:37164
[email protected]e0658bc2012-09-17 04:05:24165 if (expected_value.get()) {
[email protected]0cd50aa2013-02-12 22:28:01166 base::Value* temp = NULL;
[email protected]e0658bc2012-09-17 04:05:24167 ASSERT_TRUE(dictionary->Get("test", &temp));
168 EXPECT_EQ(expected_type, temp->GetType());
[email protected]786a7fa2011-04-22 21:27:12169 EXPECT_TRUE(expected_value->Equals(temp));
[email protected]e0658bc2012-09-17 04:05:24170 } else {
171 EXPECT_FALSE(dictionary->HasKey("test"));
172 }
[email protected]786a7fa2011-04-22 21:27:12173
174 v8::Handle<v8::Array> array(v8::Array::New());
175 array->Set(0, val);
[email protected]0cd50aa2013-02-12 22:28:01176 scoped_ptr<base::ListValue> list(
177 static_cast<base::ListValue*>(converter.FromV8Value(array, context_)));
[email protected]786a7fa2011-04-22 21:27:12178 ASSERT_TRUE(list.get());
[email protected]e0658bc2012-09-17 04:05:24179 if (expected_value.get()) {
[email protected]0cd50aa2013-02-12 22:28:01180 base::Value* temp = NULL;
[email protected]e0658bc2012-09-17 04:05:24181 ASSERT_TRUE(list->Get(0, &temp));
182 EXPECT_EQ(expected_type, temp->GetType());
[email protected]786a7fa2011-04-22 21:27:12183 EXPECT_TRUE(expected_value->Equals(temp));
[email protected]e0658bc2012-09-17 04:05:24184 } else {
185 // Arrays should preserve their length, and convert unconvertible
186 // types into null.
[email protected]0cd50aa2013-02-12 22:28:01187 base::Value* temp = NULL;
[email protected]e0658bc2012-09-17 04:05:24188 ASSERT_TRUE(list->Get(0, &temp));
[email protected]0cd50aa2013-02-12 22:28:01189 EXPECT_EQ(base::Value::TYPE_NULL, temp->GetType());
[email protected]e0658bc2012-09-17 04:05:24190 }
[email protected]786a7fa2011-04-22 21:27:12191 }
192
[email protected]b1cf3372011-04-20 21:28:10193 // Context for the JavaScript in the test.
194 v8::Persistent<v8::Context> context_;
195};
196
[email protected]8d86f13d2011-10-04 17:01:19197TEST_F(V8ValueConverterImplTest, BasicRoundTrip) {
[email protected]0cd50aa2013-02-12 22:28:01198 scoped_ptr<base::Value> original_root = base::test::ParseJson(
[email protected]06d19d82012-12-17 21:45:37199 "{ \n"
200 " \"null\": null, \n"
201 " \"true\": true, \n"
202 " \"false\": false, \n"
203 " \"positive-int\": 42, \n"
204 " \"negative-int\": -42, \n"
205 " \"zero\": 0, \n"
206 " \"double\": 88.8, \n"
[email protected]0cd50aa2013-02-12 22:28:01207 " \"big-integral-double\": 9007199254740992.0, \n" // 2.0^53
[email protected]06d19d82012-12-17 21:45:37208 " \"string\": \"foobar\", \n"
209 " \"empty-string\": \"\", \n"
210 " \"dictionary\": { \n"
211 " \"foo\": \"bar\",\n"
212 " \"hot\": \"dog\",\n"
213 " }, \n"
214 " \"empty-dictionary\": {}, \n"
215 " \"list\": [ \"monkey\", \"balls\" ], \n"
216 " \"empty-list\": [], \n"
217 "}");
[email protected]b1cf3372011-04-20 21:28:10218
219 v8::Context::Scope context_scope(context_);
220 v8::HandleScope handle_scope;
221
[email protected]8d86f13d2011-10-04 17:01:19222 V8ValueConverterImpl converter;
[email protected]786a7fa2011-04-22 21:27:12223 v8::Handle<v8::Object> v8_object =
[email protected]06d19d82012-12-17 21:45:37224 converter.ToV8Value(original_root.get(), context_).As<v8::Object>();
[email protected]b1cf3372011-04-20 21:28:10225 ASSERT_FALSE(v8_object.IsEmpty());
226
[email protected]0cd50aa2013-02-12 22:28:01227 EXPECT_EQ(static_cast<const base::DictionaryValue&>(*original_root).size(),
[email protected]06d19d82012-12-17 21:45:37228 v8_object->GetPropertyNames()->Length());
[email protected]b1cf3372011-04-20 21:28:10229 EXPECT_TRUE(v8_object->Get(v8::String::New("null"))->IsNull());
230 EXPECT_TRUE(v8_object->Get(v8::String::New("true"))->IsTrue());
231 EXPECT_TRUE(v8_object->Get(v8::String::New("false"))->IsFalse());
232 EXPECT_TRUE(v8_object->Get(v8::String::New("positive-int"))->IsInt32());
233 EXPECT_TRUE(v8_object->Get(v8::String::New("negative-int"))->IsInt32());
234 EXPECT_TRUE(v8_object->Get(v8::String::New("zero"))->IsInt32());
235 EXPECT_TRUE(v8_object->Get(v8::String::New("double"))->IsNumber());
236 EXPECT_TRUE(
237 v8_object->Get(v8::String::New("big-integral-double"))->IsNumber());
238 EXPECT_TRUE(v8_object->Get(v8::String::New("string"))->IsString());
239 EXPECT_TRUE(v8_object->Get(v8::String::New("empty-string"))->IsString());
240 EXPECT_TRUE(v8_object->Get(v8::String::New("dictionary"))->IsObject());
241 EXPECT_TRUE(v8_object->Get(v8::String::New("empty-dictionary"))->IsObject());
242 EXPECT_TRUE(v8_object->Get(v8::String::New("list"))->IsArray());
243 EXPECT_TRUE(v8_object->Get(v8::String::New("empty-list"))->IsArray());
244
[email protected]0cd50aa2013-02-12 22:28:01245 scoped_ptr<base::Value> new_root(converter.FromV8Value(v8_object, context_));
[email protected]06d19d82012-12-17 21:45:37246 EXPECT_NE(original_root.get(), new_root.get());
247 EXPECT_TRUE(original_root->Equals(new_root.get()));
[email protected]b1cf3372011-04-20 21:28:10248}
[email protected]735e70e32011-04-20 22:04:57249
[email protected]8d86f13d2011-10-04 17:01:19250TEST_F(V8ValueConverterImplTest, KeysWithDots) {
[email protected]0cd50aa2013-02-12 22:28:01251 scoped_ptr<base::Value> original =
[email protected]06d19d82012-12-17 21:45:37252 base::test::ParseJson("{ \"foo.bar\": \"baz\" }");
[email protected]735e70e32011-04-20 22:04:57253
254 v8::Context::Scope context_scope(context_);
255 v8::HandleScope handle_scope;
256
[email protected]8d86f13d2011-10-04 17:01:19257 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01258 scoped_ptr<base::Value> copy(
[email protected]735e70e32011-04-20 22:04:57259 converter.FromV8Value(
[email protected]06d19d82012-12-17 21:45:37260 converter.ToV8Value(original.get(), context_), context_));
[email protected]735e70e32011-04-20 22:04:57261
[email protected]06d19d82012-12-17 21:45:37262 EXPECT_TRUE(original->Equals(copy.get()));
[email protected]735e70e32011-04-20 22:04:57263}
[email protected]786a7fa2011-04-22 21:27:12264
[email protected]8d86f13d2011-10-04 17:01:19265TEST_F(V8ValueConverterImplTest, ObjectExceptions) {
[email protected]786a7fa2011-04-22 21:27:12266 v8::Context::Scope context_scope(context_);
267 v8::HandleScope handle_scope;
268
269 // Set up objects to throw when reading or writing 'foo'.
270 const char* source =
271 "Object.prototype.__defineSetter__('foo', "
272 " function() { throw new Error('muah!'); });"
273 "Object.prototype.__defineGetter__('foo', "
274 " function() { throw new Error('muah!'); });";
275
276 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
277 script->Run();
278
279 v8::Handle<v8::Object> object(v8::Object::New());
280 object->Set(v8::String::New("bar"), v8::String::New("bar"));
281
282 // Converting from v8 value should replace the foo property with null.
[email protected]8d86f13d2011-10-04 17:01:19283 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01284 scoped_ptr<base::DictionaryValue> converted(
285 static_cast<base::DictionaryValue*>(
286 converter.FromV8Value(object, context_)));
[email protected]786a7fa2011-04-22 21:27:12287 EXPECT_TRUE(converted.get());
288 // http://code.google.com/p/v8/issues/detail?id=1342
289 // EXPECT_EQ(2u, converted->size());
290 // EXPECT_TRUE(IsNull(converted.get(), "foo"));
291 EXPECT_EQ(1u, converted->size());
292 EXPECT_EQ("bar", GetString(converted.get(), "bar"));
293
294 // Converting to v8 value should drop the foo property.
295 converted->SetString("foo", "foo");
296 v8::Handle<v8::Object> copy =
297 converter.ToV8Value(converted.get(), context_).As<v8::Object>();
298 EXPECT_FALSE(copy.IsEmpty());
299 EXPECT_EQ(2u, copy->GetPropertyNames()->Length());
300 EXPECT_EQ("bar", GetString(copy, "bar"));
301}
302
[email protected]8d86f13d2011-10-04 17:01:19303TEST_F(V8ValueConverterImplTest, ArrayExceptions) {
[email protected]786a7fa2011-04-22 21:27:12304 v8::Context::Scope context_scope(context_);
305 v8::HandleScope handle_scope;
306
307 const char* source = "(function() {"
308 "var arr = [];"
309 "arr.__defineSetter__(0, "
310 " function() { throw new Error('muah!'); });"
311 "arr.__defineGetter__(0, "
312 " function() { throw new Error('muah!'); });"
313 "arr[1] = 'bar';"
314 "return arr;"
315 "})();";
316
317 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
318 v8::Handle<v8::Array> array = script->Run().As<v8::Array>();
319 ASSERT_FALSE(array.IsEmpty());
320
321 // Converting from v8 value should replace the first item with null.
[email protected]8d86f13d2011-10-04 17:01:19322 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01323 scoped_ptr<base::ListValue> converted(static_cast<base::ListValue*>(
[email protected]786a7fa2011-04-22 21:27:12324 converter.FromV8Value(array, context_)));
325 ASSERT_TRUE(converted.get());
326 // http://code.google.com/p/v8/issues/detail?id=1342
327 EXPECT_EQ(2u, converted->GetSize());
328 EXPECT_TRUE(IsNull(converted.get(), 0));
329
330 // Converting to v8 value should drop the first item and leave a hole.
[email protected]0cd50aa2013-02-12 22:28:01331 converted.reset(static_cast<base::ListValue*>(
[email protected]06d19d82012-12-17 21:45:37332 base::test::ParseJson("[ \"foo\", \"bar\" ]").release()));
[email protected]786a7fa2011-04-22 21:27:12333 v8::Handle<v8::Array> copy =
334 converter.ToV8Value(converted.get(), context_).As<v8::Array>();
335 ASSERT_FALSE(copy.IsEmpty());
336 EXPECT_EQ(2u, copy->Length());
337 EXPECT_EQ("bar", GetString(copy, 1));
338}
339
[email protected]8d86f13d2011-10-04 17:01:19340TEST_F(V8ValueConverterImplTest, WeirdTypes) {
[email protected]786a7fa2011-04-22 21:27:12341 v8::Context::Scope context_scope(context_);
342 v8::HandleScope handle_scope;
343
344 v8::Handle<v8::RegExp> regex(
345 v8::RegExp::New(v8::String::New("."), v8::RegExp::kNone));
346
[email protected]8d86f13d2011-10-04 17:01:19347 V8ValueConverterImpl converter;
[email protected]e0658bc2012-09-17 04:05:24348 TestWeirdType(converter,
349 v8::Undefined(),
[email protected]0cd50aa2013-02-12 22:28:01350 base::Value::TYPE_NULL, // Arbitrary type, result is NULL.
351 scoped_ptr<base::Value>(NULL));
[email protected]e0658bc2012-09-17 04:05:24352 TestWeirdType(converter,
353 v8::Date::New(1000),
[email protected]0cd50aa2013-02-12 22:28:01354 base::Value::TYPE_DICTIONARY,
355 scoped_ptr<base::Value>(new base::DictionaryValue()));
[email protected]e0658bc2012-09-17 04:05:24356 TestWeirdType(converter,
357 regex,
[email protected]0cd50aa2013-02-12 22:28:01358 base::Value::TYPE_DICTIONARY,
359 scoped_ptr<base::Value>(new base::DictionaryValue()));
[email protected]786a7fa2011-04-22 21:27:12360
[email protected]3de391e82012-05-16 17:50:51361 converter.SetDateAllowed(true);
[email protected]e0658bc2012-09-17 04:05:24362 TestWeirdType(converter,
363 v8::Date::New(1000),
[email protected]0cd50aa2013-02-12 22:28:01364 base::Value::TYPE_DOUBLE,
365 scoped_ptr<base::Value>(new base::FundamentalValue(1.0)));
[email protected]786a7fa2011-04-22 21:27:12366
[email protected]e0658bc2012-09-17 04:05:24367 converter.SetRegExpAllowed(true);
368 TestWeirdType(converter,
369 regex,
[email protected]0cd50aa2013-02-12 22:28:01370 base::Value::TYPE_STRING,
371 scoped_ptr<base::Value>(new base::StringValue("/./")));
[email protected]786a7fa2011-04-22 21:27:12372}
373
[email protected]8d86f13d2011-10-04 17:01:19374TEST_F(V8ValueConverterImplTest, Prototype) {
[email protected]786a7fa2011-04-22 21:27:12375 v8::Context::Scope context_scope(context_);
376 v8::HandleScope handle_scope;
377
378 const char* source = "(function() {"
379 "Object.prototype.foo = 'foo';"
380 "return {};"
381 "})();";
382
383 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
384 v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
385 ASSERT_FALSE(object.IsEmpty());
386
[email protected]8d86f13d2011-10-04 17:01:19387 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01388 scoped_ptr<base::DictionaryValue> result(
389 static_cast<base::DictionaryValue*>(
390 converter.FromV8Value(object, context_)));
[email protected]786a7fa2011-04-22 21:27:12391 ASSERT_TRUE(result.get());
392 EXPECT_EQ(0u, result->size());
393}
[email protected]2a521842012-05-30 09:17:06394
395TEST_F(V8ValueConverterImplTest, StripNullFromObjects) {
396 v8::Context::Scope context_scope(context_);
397 v8::HandleScope handle_scope;
398
399 const char* source = "(function() {"
400 "return { foo: undefined, bar: null };"
401 "})();";
402
403 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
404 v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
405 ASSERT_FALSE(object.IsEmpty());
406
407 V8ValueConverterImpl converter;
[email protected]2a521842012-05-30 09:17:06408 converter.SetStripNullFromObjects(true);
409
[email protected]0cd50aa2013-02-12 22:28:01410 scoped_ptr<base::DictionaryValue> result(
411 static_cast<base::DictionaryValue*>(
412 converter.FromV8Value(object, context_)));
[email protected]2a521842012-05-30 09:17:06413 ASSERT_TRUE(result.get());
414 EXPECT_EQ(0u, result->size());
415}
[email protected]217d9562012-07-16 21:57:37416
417TEST_F(V8ValueConverterImplTest, RecursiveObjects) {
418 v8::Context::Scope context_scope(context_);
419 v8::HandleScope handle_scope;
420
421 V8ValueConverterImpl converter;
422
423 v8::Handle<v8::Object> object = v8::Object::New().As<v8::Object>();
424 ASSERT_FALSE(object.IsEmpty());
425 object->Set(v8::String::New("foo"), v8::String::New("bar"));
426 object->Set(v8::String::New("obj"), object);
427
[email protected]0cd50aa2013-02-12 22:28:01428 scoped_ptr<base::DictionaryValue> object_result(
429 static_cast<base::DictionaryValue*>(
430 converter.FromV8Value(object, context_)));
[email protected]217d9562012-07-16 21:57:37431 ASSERT_TRUE(object_result.get());
432 EXPECT_EQ(2u, object_result->size());
433 EXPECT_TRUE(IsNull(object_result.get(), "obj"));
434
435 v8::Handle<v8::Array> array = v8::Array::New().As<v8::Array>();
436 ASSERT_FALSE(array.IsEmpty());
437 array->Set(0, v8::String::New("1"));
438 array->Set(1, array);
439
[email protected]0cd50aa2013-02-12 22:28:01440 scoped_ptr<base::ListValue> list_result(
441 static_cast<base::ListValue*>(converter.FromV8Value(array, context_)));
[email protected]217d9562012-07-16 21:57:37442 ASSERT_TRUE(list_result.get());
443 EXPECT_EQ(2u, list_result->GetSize());
444 EXPECT_TRUE(IsNull(list_result.get(), 1));
445}
446
[email protected]c3e08892012-08-05 00:30:12447// Do not try and convert any named callbacks including getters.
[email protected]217d9562012-07-16 21:57:37448TEST_F(V8ValueConverterImplTest, ObjectGetters) {
449 v8::Context::Scope context_scope(context_);
450 v8::HandleScope handle_scope;
451
452 const char* source = "(function() {"
453 "var a = {};"
454 "a.__defineGetter__('foo', function() { return 'bar'; });"
455 "return a;"
456 "})();";
457
458 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
459 v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
460 ASSERT_FALSE(object.IsEmpty());
461
462 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01463 scoped_ptr<base::DictionaryValue> result(
464 static_cast<base::DictionaryValue*>(
465 converter.FromV8Value(object, context_)));
[email protected]217d9562012-07-16 21:57:37466 ASSERT_TRUE(result.get());
[email protected]c3e08892012-08-05 00:30:12467 EXPECT_EQ(0u, result->size());
468}
469
470// Do not try and convert any named callbacks including getters.
471TEST_F(V8ValueConverterImplTest, ObjectWithInternalFieldsGetters) {
472 v8::Context::Scope context_scope(context_);
473 v8::HandleScope handle_scope;
474
475 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
476 object_template->SetInternalFieldCount(1);
477 object_template->SetAccessor(v8::String::New("foo"), NamedCallbackGetter);
478 v8::Handle<v8::Object> object = object_template->NewInstance();
479 ASSERT_FALSE(object.IsEmpty());
480 object->Set(v8::String::New("a"), v8::String::New("b"));
481
482 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01483 scoped_ptr<base::DictionaryValue> result(
484 static_cast<base::DictionaryValue*>(
485 converter.FromV8Value(object, context_)));
[email protected]c3e08892012-08-05 00:30:12486 ASSERT_TRUE(result.get());
[email protected]217d9562012-07-16 21:57:37487 EXPECT_EQ(1u, result->size());
488}
489
[email protected]74baf972012-09-13 02:46:39490TEST_F(V8ValueConverterImplTest, WeirdProperties) {
491 v8::Context::Scope context_scope(context_);
492 v8::HandleScope handle_scope;
493
494 const char* source = "(function() {"
495 "return {"
496 "1: 'foo',"
497 "'2': 'bar',"
498 "true: 'baz',"
499 "false: 'qux',"
500 "null: 'quux',"
501 "undefined: 'oops'"
502 "};"
503 "})();";
504
505 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
506 v8::Handle<v8::Object> object = script->Run().As<v8::Object>();
507 ASSERT_FALSE(object.IsEmpty());
508
509 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01510 scoped_ptr<base::Value> actual(converter.FromV8Value(object, context_));
[email protected]74baf972012-09-13 02:46:39511
[email protected]0cd50aa2013-02-12 22:28:01512 scoped_ptr<base::Value> expected = base::test::ParseJson(
[email protected]06d19d82012-12-17 21:45:37513 "{ \n"
514 " \"1\": \"foo\", \n"
515 " \"2\": \"bar\", \n"
516 " \"true\": \"baz\", \n"
517 " \"false\": \"qux\", \n"
518 " \"null\": \"quux\", \n"
519 " \"undefined\": \"oops\", \n"
520 "}");
[email protected]74baf972012-09-13 02:46:39521
[email protected]06d19d82012-12-17 21:45:37522 EXPECT_TRUE(expected->Equals(actual.get()));
[email protected]74baf972012-09-13 02:46:39523}
524
[email protected]217d9562012-07-16 21:57:37525TEST_F(V8ValueConverterImplTest, ArrayGetters) {
526 v8::Context::Scope context_scope(context_);
527 v8::HandleScope handle_scope;
528
529 const char* source = "(function() {"
530 "var a = [0];"
531 "a.__defineGetter__(1, function() { return 'bar'; });"
532 "return a;"
533 "})();";
534
535 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
536 v8::Handle<v8::Array> array = script->Run().As<v8::Array>();
537 ASSERT_FALSE(array.IsEmpty());
538
539 V8ValueConverterImpl converter;
[email protected]0cd50aa2013-02-12 22:28:01540 scoped_ptr<base::ListValue> result(
541 static_cast<base::ListValue*>(converter.FromV8Value(array, context_)));
[email protected]217d9562012-07-16 21:57:37542 ASSERT_TRUE(result.get());
543 EXPECT_EQ(2u, result->GetSize());
544}
[email protected]e0658bc2012-09-17 04:05:24545
546TEST_F(V8ValueConverterImplTest, UndefinedValueBehavior) {
547 v8::Context::Scope context_scope(context_);
548 v8::HandleScope handle_scope;
549
550 v8::Handle<v8::Object> object;
551 {
552 const char* source = "(function() {"
553 "return { foo: undefined, bar: null, baz: function(){} };"
554 "})();";
555 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
556 object = script->Run().As<v8::Object>();
557 ASSERT_FALSE(object.IsEmpty());
558 }
559
560 v8::Handle<v8::Array> array;
561 {
562 const char* source = "(function() {"
563 "return [ undefined, null, function(){} ];"
564 "})();";
565 v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source)));
566 array = script->Run().As<v8::Array>();
567 ASSERT_FALSE(array.IsEmpty());
568 }
569
570 V8ValueConverterImpl converter;
571
[email protected]0cd50aa2013-02-12 22:28:01572 scoped_ptr<base::Value> actual_object(
573 converter.FromV8Value(object, context_));
574 EXPECT_TRUE(base::Value::Equals(
575 base::test::ParseJson("{ \"bar\": null }").get(), actual_object.get()));
[email protected]e0658bc2012-09-17 04:05:24576
[email protected]e0658bc2012-09-17 04:05:24577 // Everything is null because JSON stringification preserves array length.
[email protected]e0658bc2012-09-17 04:05:24578 scoped_ptr<Value> actual_array(converter.FromV8Value(array, context_));
[email protected]0cd50aa2013-02-12 22:28:01579 EXPECT_TRUE(base::Value::Equals(
580 base::test::ParseJson("[ null, null, null ]").get(), actual_array.get()));
[email protected]e0658bc2012-09-17 04:05:24581}
[email protected]9a292e22012-10-22 15:19:31582
[email protected]6a9b7372013-03-20 15:35:20583TEST_F(V8ValueConverterImplTest, ObjectsWithClashingIdentityHash) {
584 v8::Context::Scope context_scope(context_);
585 v8::HandleScope handle_scope;
586 V8ValueConverterImpl converter;
587
588 // We check that the converter checks identity correctly by disabling the
589 // optimization of using identity hashes.
590 ScopedAvoidIdentityHashForTesting scoped_hash_avoider(&converter);
591
592 // Create the v8::Object to be converted.
593 v8::Handle<v8::Array> root(v8::Array::New(4));
594 root->Set(0, v8::Handle<v8::Object>(v8::Object::New()));
595 root->Set(1, v8::Handle<v8::Object>(v8::Object::New()));
596 root->Set(2, v8::Handle<v8::Object>(v8::Array::New(0)));
597 root->Set(3, v8::Handle<v8::Object>(v8::Array::New(0)));
598
599 // The expected base::Value result.
600 scoped_ptr<base::Value> expected = base::test::ParseJson("[{},{},[],[]]");
601 ASSERT_TRUE(expected.get());
602
603 // The actual result.
604 scoped_ptr<base::Value> value(converter.FromV8Value(root, context_));
605 ASSERT_TRUE(value.get());
606
607 EXPECT_TRUE(expected->Equals(value.get()));
608}
609
610TEST_F(V8ValueConverterImplTest, DetectCycles) {
611 v8::Context::Scope context_scope(context_);
612 v8::HandleScope handle_scope;
613 V8ValueConverterImpl converter;
614
615 // Create a recursive array.
616 v8::Handle<v8::Array> recursive_array(v8::Array::New(1));
617 recursive_array->Set(0, recursive_array);
618
619 // The first repetition should be trimmed and replaced by a null value.
620 base::ListValue expected_list;
621 expected_list.Append(base::Value::CreateNullValue());
622
623 // The actual result.
624 scoped_ptr<base::Value> actual_list(
625 converter.FromV8Value(recursive_array, context_));
626 ASSERT_TRUE(actual_list.get());
627
628 EXPECT_TRUE(expected_list.Equals(actual_list.get()));
629
630 // Now create a recursive object
631 const std::string key("key");
632 v8::Handle<v8::Object> recursive_object(v8::Object::New());
633 v8::TryCatch try_catch;
634 recursive_object->Set(v8::String::New(key.c_str(), key.length()),
635 recursive_object);
636 ASSERT_FALSE(try_catch.HasCaught());
637
638 // The first repetition should be trimmed and replaced by a null value.
639 base::DictionaryValue expected_dictionary;
640 expected_dictionary.Set(key, base::Value::CreateNullValue());
641
642 // The actual result.
643 scoped_ptr<base::Value> actual_dictionary(
644 converter.FromV8Value(recursive_object, context_));
645 ASSERT_TRUE(actual_dictionary.get());
646
647 EXPECT_TRUE(expected_dictionary.Equals(actual_dictionary.get()));
648}
649
[email protected]9a292e22012-10-22 15:19:31650} // namespace content