OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/v8_value_converter_impl.h" | 5 #include "content/renderer/v8_value_converter_impl.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/float_util.h" | |
10 #include "base/logging.h" | 9 #include "base/logging.h" |
11 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
12 #include "base/values.h" | 11 #include "base/values.h" |
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h" | 12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h" |
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBufferView.h" | 13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBufferView.h" |
15 #include "v8/include/v8.h" | 14 #include "v8/include/v8.h" |
16 | 15 |
17 namespace content { | 16 namespace content { |
18 | 17 |
19 namespace { | |
20 const int kMaxRecursionDepth = 10; | |
21 } | |
22 | |
23 // The state of a call to FromV8Value. | |
24 class V8ValueConverterImpl::FromV8ValueState { | |
25 public: | |
26 // Level scope which updates the current depth of some FromV8ValueState. | |
27 class Level { | |
28 public: | |
29 explicit Level(FromV8ValueState* state) : state_(state) { | |
30 state_->max_recursion_depth_--; | |
31 } | |
32 ~Level() { | |
33 state_->max_recursion_depth_++; | |
34 } | |
35 | |
36 private: | |
37 FromV8ValueState* state_; | |
38 }; | |
39 | |
40 explicit FromV8ValueState(bool avoid_identity_hash_for_testing) | |
41 : max_recursion_depth_(kMaxRecursionDepth), | |
42 avoid_identity_hash_for_testing_(avoid_identity_hash_for_testing) {} | |
43 | |
44 // If |handle| is not in |unique_map_|, then add it to |unique_map_| and | |
45 // return true. | |
46 // | |
47 // Otherwise do nothing and return false. Here "A is unique" means that no | |
48 // other handle B in the map points to the same object as A. Note that A can | |
49 // be unique even if there already is another handle with the same identity | |
50 // hash (key) in the map, because two objects can have the same hash. | |
51 bool UpdateAndCheckUniqueness(v8::Handle<v8::Object> handle) { | |
52 typedef HashToHandleMap::const_iterator Iterator; | |
53 int hash = avoid_identity_hash_for_testing_ ? 0 : handle->GetIdentityHash(); | |
54 // We only compare using == with handles to objects with the same identity | |
55 // hash. Different hash obviously means different objects, but two objects | |
56 // in a couple of thousands could have the same identity hash. | |
57 std::pair<Iterator, Iterator> range = unique_map_.equal_range(hash); | |
58 for (Iterator it = range.first; it != range.second; ++it) { | |
59 // Operator == for handles actually compares the underlying objects. | |
60 if (it->second == handle) | |
61 return false; | |
62 } | |
63 unique_map_.insert(std::make_pair(hash, handle)); | |
64 return true; | |
65 } | |
66 | |
67 bool HasReachedMaxRecursionDepth() { | |
68 return max_recursion_depth_ < 0; | |
69 } | |
70 | |
71 private: | |
72 typedef std::multimap<int, v8::Handle<v8::Object> > HashToHandleMap; | |
73 HashToHandleMap unique_map_; | |
74 | |
75 int max_recursion_depth_; | |
76 | |
77 bool avoid_identity_hash_for_testing_; | |
78 }; | |
79 | |
80 V8ValueConverter* V8ValueConverter::create() { | 18 V8ValueConverter* V8ValueConverter::create() { |
81 return new V8ValueConverterImpl(); | 19 return new V8ValueConverterImpl(); |
82 } | 20 } |
83 | 21 |
84 V8ValueConverterImpl::V8ValueConverterImpl() | 22 V8ValueConverterImpl::V8ValueConverterImpl() |
85 : date_allowed_(false), | 23 : date_allowed_(false), |
86 reg_exp_allowed_(false), | 24 reg_exp_allowed_(false), |
87 function_allowed_(false), | 25 function_allowed_(false), |
88 strip_null_from_objects_(false), | 26 strip_null_from_objects_(false), |
89 avoid_identity_hash_for_testing_(false) {} | 27 avoid_identity_hash_for_testing_(false) {} |
(...skipping 19 matching lines...) Expand all Loading... |
109 v8::Context::Scope context_scope(context); | 47 v8::Context::Scope context_scope(context); |
110 v8::HandleScope handle_scope; | 48 v8::HandleScope handle_scope; |
111 return handle_scope.Close(ToV8ValueImpl(value)); | 49 return handle_scope.Close(ToV8ValueImpl(value)); |
112 } | 50 } |
113 | 51 |
114 Value* V8ValueConverterImpl::FromV8Value( | 52 Value* V8ValueConverterImpl::FromV8Value( |
115 v8::Handle<v8::Value> val, | 53 v8::Handle<v8::Value> val, |
116 v8::Handle<v8::Context> context) const { | 54 v8::Handle<v8::Context> context) const { |
117 v8::Context::Scope context_scope(context); | 55 v8::Context::Scope context_scope(context); |
118 v8::HandleScope handle_scope; | 56 v8::HandleScope handle_scope; |
119 FromV8ValueState state(avoid_identity_hash_for_testing_); | 57 HashToHandleMap unique_map; |
120 return FromV8ValueImpl(val, &state); | 58 return FromV8ValueImpl(val, &unique_map); |
121 } | 59 } |
122 | 60 |
123 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8ValueImpl( | 61 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8ValueImpl( |
124 const base::Value* value) const { | 62 const base::Value* value) const { |
125 CHECK(value); | 63 CHECK(value); |
126 switch (value->GetType()) { | 64 switch (value->GetType()) { |
127 case base::Value::TYPE_NULL: | 65 case base::Value::TYPE_NULL: |
128 return v8::Null(); | 66 return v8::Null(); |
129 | 67 |
130 case base::Value::TYPE_BOOLEAN: { | 68 case base::Value::TYPE_BOOLEAN: { |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 } | 146 } |
209 | 147 |
210 v8::Handle<v8::Value> V8ValueConverterImpl::ToArrayBuffer( | 148 v8::Handle<v8::Value> V8ValueConverterImpl::ToArrayBuffer( |
211 const base::BinaryValue* value) const { | 149 const base::BinaryValue* value) const { |
212 WebKit::WebArrayBuffer buffer = | 150 WebKit::WebArrayBuffer buffer = |
213 WebKit::WebArrayBuffer::create(value->GetSize(), 1); | 151 WebKit::WebArrayBuffer::create(value->GetSize(), 1); |
214 memcpy(buffer.data(), value->GetBuffer(), value->GetSize()); | 152 memcpy(buffer.data(), value->GetBuffer(), value->GetSize()); |
215 return buffer.toV8Value(); | 153 return buffer.toV8Value(); |
216 } | 154 } |
217 | 155 |
218 Value* V8ValueConverterImpl::FromV8ValueImpl( | 156 Value* V8ValueConverterImpl::FromV8ValueImpl(v8::Handle<v8::Value> val, |
219 v8::Handle<v8::Value> val, | 157 HashToHandleMap* unique_map) const { |
220 FromV8ValueState* state) const { | |
221 CHECK(!val.IsEmpty()); | 158 CHECK(!val.IsEmpty()); |
222 | 159 |
223 FromV8ValueState::Level state_level(state); | |
224 if (state->HasReachedMaxRecursionDepth()) | |
225 return NULL; | |
226 | |
227 if (val->IsNull()) | 160 if (val->IsNull()) |
228 return base::Value::CreateNullValue(); | 161 return base::Value::CreateNullValue(); |
229 | 162 |
230 if (val->IsBoolean()) | 163 if (val->IsBoolean()) |
231 return new base::FundamentalValue(val->ToBoolean()->Value()); | 164 return new base::FundamentalValue(val->ToBoolean()->Value()); |
232 | 165 |
233 if (val->IsInt32()) | 166 if (val->IsInt32()) |
234 return new base::FundamentalValue(val->ToInt32()->Value()); | 167 return new base::FundamentalValue(val->ToInt32()->Value()); |
235 | 168 |
236 if (val->IsNumber()) { | 169 if (val->IsNumber()) |
237 double val_as_double = val->ToNumber()->Value(); | 170 return new base::FundamentalValue(val->ToNumber()->Value()); |
238 if (!base::IsFinite(val_as_double)) | |
239 return NULL; | |
240 return new base::FundamentalValue(val_as_double); | |
241 } | |
242 | 171 |
243 if (val->IsString()) { | 172 if (val->IsString()) { |
244 v8::String::Utf8Value utf8(val->ToString()); | 173 v8::String::Utf8Value utf8(val->ToString()); |
245 return new base::StringValue(std::string(*utf8, utf8.length())); | 174 return new base::StringValue(std::string(*utf8, utf8.length())); |
246 } | 175 } |
247 | 176 |
248 if (val->IsUndefined()) | 177 if (val->IsUndefined()) |
249 // JSON.stringify ignores undefined. | 178 // JSON.stringify ignores undefined. |
250 return NULL; | 179 return NULL; |
251 | 180 |
252 if (val->IsDate()) { | 181 if (val->IsDate()) { |
253 if (!date_allowed_) | 182 if (!date_allowed_) |
254 // JSON.stringify would convert this to a string, but an object is more | 183 // JSON.stringify would convert this to a string, but an object is more |
255 // consistent within this class. | 184 // consistent within this class. |
256 return FromV8Object(val->ToObject(), state); | 185 return FromV8Object(val->ToObject(), unique_map); |
257 v8::Date* date = v8::Date::Cast(*val); | 186 v8::Date* date = v8::Date::Cast(*val); |
258 return new base::FundamentalValue(date->NumberValue() / 1000.0); | 187 return new base::FundamentalValue(date->NumberValue() / 1000.0); |
259 } | 188 } |
260 | 189 |
261 if (val->IsRegExp()) { | 190 if (val->IsRegExp()) { |
262 if (!reg_exp_allowed_) | 191 if (!reg_exp_allowed_) |
263 // JSON.stringify converts to an object. | 192 // JSON.stringify converts to an object. |
264 return FromV8Object(val->ToObject(), state); | 193 return FromV8Object(val->ToObject(), unique_map); |
265 return new base::StringValue(*v8::String::Utf8Value(val->ToString())); | 194 return new base::StringValue(*v8::String::Utf8Value(val->ToString())); |
266 } | 195 } |
267 | 196 |
268 // v8::Value doesn't have a ToArray() method for some reason. | 197 // v8::Value doesn't have a ToArray() method for some reason. |
269 if (val->IsArray()) | 198 if (val->IsArray()) |
270 return FromV8Array(val.As<v8::Array>(), state); | 199 return FromV8Array(val.As<v8::Array>(), unique_map); |
271 | 200 |
272 if (val->IsFunction()) { | 201 if (val->IsFunction()) { |
273 if (!function_allowed_) | 202 if (!function_allowed_) |
274 // JSON.stringify refuses to convert function(){}. | 203 // JSON.stringify refuses to convert function(){}. |
275 return NULL; | 204 return NULL; |
276 return FromV8Object(val->ToObject(), state); | 205 return FromV8Object(val->ToObject(), unique_map); |
277 } | 206 } |
278 | 207 |
279 if (val->IsObject()) { | 208 if (val->IsObject()) { |
280 base::BinaryValue* binary_value = FromV8Buffer(val); | 209 base::BinaryValue* binary_value = FromV8Buffer(val); |
281 if (binary_value) { | 210 if (binary_value) { |
282 return binary_value; | 211 return binary_value; |
283 } else { | 212 } else { |
284 return FromV8Object(val->ToObject(), state); | 213 return FromV8Object(val->ToObject(), unique_map); |
285 } | 214 } |
286 } | 215 } |
287 | 216 |
288 LOG(ERROR) << "Unexpected v8 value type encountered."; | 217 LOG(ERROR) << "Unexpected v8 value type encountered."; |
289 return NULL; | 218 return NULL; |
290 } | 219 } |
291 | 220 |
292 Value* V8ValueConverterImpl::FromV8Array( | 221 Value* V8ValueConverterImpl::FromV8Array(v8::Handle<v8::Array> val, |
293 v8::Handle<v8::Array> val, | 222 HashToHandleMap* unique_map) const { |
294 FromV8ValueState* state) const { | 223 if (!UpdateAndCheckUniqueness(unique_map, val)) |
295 if (!state->UpdateAndCheckUniqueness(val)) | |
296 return base::Value::CreateNullValue(); | 224 return base::Value::CreateNullValue(); |
297 | 225 |
298 scoped_ptr<v8::Context::Scope> scope; | 226 scoped_ptr<v8::Context::Scope> scope; |
299 // If val was created in a different context than our current one, change to | 227 // If val was created in a different context than our current one, change to |
300 // that context, but change back after val is converted. | 228 // that context, but change back after val is converted. |
301 if (!val->CreationContext().IsEmpty() && | 229 if (!val->CreationContext().IsEmpty() && |
302 val->CreationContext() != v8::Context::GetCurrent()) | 230 val->CreationContext() != v8::Context::GetCurrent()) |
303 scope.reset(new v8::Context::Scope(val->CreationContext())); | 231 scope.reset(new v8::Context::Scope(val->CreationContext())); |
304 | 232 |
305 base::ListValue* result = new base::ListValue(); | 233 base::ListValue* result = new base::ListValue(); |
306 | 234 |
307 // Only fields with integer keys are carried over to the ListValue. | 235 // Only fields with integer keys are carried over to the ListValue. |
308 for (uint32 i = 0; i < val->Length(); ++i) { | 236 for (uint32 i = 0; i < val->Length(); ++i) { |
309 v8::TryCatch try_catch; | 237 v8::TryCatch try_catch; |
310 v8::Handle<v8::Value> child_v8 = val->Get(i); | 238 v8::Handle<v8::Value> child_v8 = val->Get(i); |
311 if (try_catch.HasCaught()) { | 239 if (try_catch.HasCaught()) { |
312 LOG(ERROR) << "Getter for index " << i << " threw an exception."; | 240 LOG(ERROR) << "Getter for index " << i << " threw an exception."; |
313 child_v8 = v8::Null(); | 241 child_v8 = v8::Null(); |
314 } | 242 } |
315 | 243 |
316 if (!val->HasRealIndexedProperty(i)) | 244 if (!val->HasRealIndexedProperty(i)) |
317 continue; | 245 continue; |
318 | 246 |
319 base::Value* child = FromV8ValueImpl(child_v8, state); | 247 base::Value* child = FromV8ValueImpl(child_v8, unique_map); |
320 if (child) | 248 if (child) |
321 result->Append(child); | 249 result->Append(child); |
322 else | 250 else |
323 // JSON.stringify puts null in places where values don't serialize, for | 251 // JSON.stringify puts null in places where values don't serialize, for |
324 // example undefined and functions. Emulate that behavior. | 252 // example undefined and functions. Emulate that behavior. |
325 result->Append(base::Value::CreateNullValue()); | 253 result->Append(base::Value::CreateNullValue()); |
326 } | 254 } |
327 return result; | 255 return result; |
328 } | 256 } |
329 | 257 |
(...skipping 17 matching lines...) Expand all Loading... |
347 } | 275 } |
348 | 276 |
349 if (data) | 277 if (data) |
350 return base::BinaryValue::CreateWithCopiedBuffer(data, length); | 278 return base::BinaryValue::CreateWithCopiedBuffer(data, length); |
351 else | 279 else |
352 return NULL; | 280 return NULL; |
353 } | 281 } |
354 | 282 |
355 Value* V8ValueConverterImpl::FromV8Object( | 283 Value* V8ValueConverterImpl::FromV8Object( |
356 v8::Handle<v8::Object> val, | 284 v8::Handle<v8::Object> val, |
357 FromV8ValueState* state) const { | 285 HashToHandleMap* unique_map) const { |
358 if (!state->UpdateAndCheckUniqueness(val)) | 286 if (!UpdateAndCheckUniqueness(unique_map, val)) |
359 return base::Value::CreateNullValue(); | 287 return base::Value::CreateNullValue(); |
360 scoped_ptr<v8::Context::Scope> scope; | 288 scoped_ptr<v8::Context::Scope> scope; |
361 // If val was created in a different context than our current one, change to | 289 // If val was created in a different context than our current one, change to |
362 // that context, but change back after val is converted. | 290 // that context, but change back after val is converted. |
363 if (!val->CreationContext().IsEmpty() && | 291 if (!val->CreationContext().IsEmpty() && |
364 val->CreationContext() != v8::Context::GetCurrent()) | 292 val->CreationContext() != v8::Context::GetCurrent()) |
365 scope.reset(new v8::Context::Scope(val->CreationContext())); | 293 scope.reset(new v8::Context::Scope(val->CreationContext())); |
366 | 294 |
367 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); | 295 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); |
368 v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames()); | 296 v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames()); |
369 | 297 |
370 for (uint32 i = 0; i < property_names->Length(); ++i) { | 298 for (uint32 i = 0; i < property_names->Length(); ++i) { |
371 v8::Handle<v8::Value> key(property_names->Get(i)); | 299 v8::Handle<v8::Value> key(property_names->Get(i)); |
372 | 300 |
373 // Extend this test to cover more types as necessary and if sensible. | 301 // Extend this test to cover more types as necessary and if sensible. |
374 if (!key->IsString() && | 302 if (!key->IsString() && |
375 !key->IsNumber()) { | 303 !key->IsNumber()) { |
376 NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" " | 304 NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" " |
377 "is neither a string nor a number"; | 305 "is neither a string nor a number"; |
378 continue; | 306 continue; |
379 } | 307 } |
380 | 308 |
| 309 // Skip all callbacks: crbug.com/139933 |
| 310 if (val->HasRealNamedCallbackProperty(key->ToString())) |
| 311 continue; |
| 312 |
381 v8::String::Utf8Value name_utf8(key->ToString()); | 313 v8::String::Utf8Value name_utf8(key->ToString()); |
382 | 314 |
383 v8::TryCatch try_catch; | 315 v8::TryCatch try_catch; |
384 v8::Handle<v8::Value> child_v8 = val->Get(key); | 316 v8::Handle<v8::Value> child_v8 = val->Get(key); |
385 | 317 |
386 if (try_catch.HasCaught()) { | 318 if (try_catch.HasCaught()) { |
387 LOG(ERROR) << "Getter for property " << *name_utf8 | 319 LOG(ERROR) << "Getter for property " << *name_utf8 |
388 << " threw an exception."; | 320 << " threw an exception."; |
389 child_v8 = v8::Null(); | 321 child_v8 = v8::Null(); |
390 } | 322 } |
391 | 323 |
392 scoped_ptr<base::Value> child(FromV8ValueImpl(child_v8, state)); | 324 scoped_ptr<base::Value> child(FromV8ValueImpl(child_v8, unique_map)); |
393 if (!child) | 325 if (!child) |
394 // JSON.stringify skips properties whose values don't serialize, for | 326 // JSON.stringify skips properties whose values don't serialize, for |
395 // example undefined and functions. Emulate that behavior. | 327 // example undefined and functions. Emulate that behavior. |
396 continue; | 328 continue; |
397 | 329 |
398 // Strip null if asked (and since undefined is turned into null, undefined | 330 // Strip null if asked (and since undefined is turned into null, undefined |
399 // too). The use case for supporting this is JSON-schema support, | 331 // too). The use case for supporting this is JSON-schema support, |
400 // specifically for extensions, where "optional" JSON properties may be | 332 // specifically for extensions, where "optional" JSON properties may be |
401 // represented as null, yet due to buggy legacy code elsewhere isn't | 333 // represented as null, yet due to buggy legacy code elsewhere isn't |
402 // treated as such (potentially causing crashes). For example, the | 334 // treated as such (potentially causing crashes). For example, the |
(...skipping 15 matching lines...) Expand all Loading... |
418 if (strip_null_from_objects_ && child->IsType(Value::TYPE_NULL)) | 350 if (strip_null_from_objects_ && child->IsType(Value::TYPE_NULL)) |
419 continue; | 351 continue; |
420 | 352 |
421 result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()), | 353 result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()), |
422 child.release()); | 354 child.release()); |
423 } | 355 } |
424 | 356 |
425 return result.release(); | 357 return result.release(); |
426 } | 358 } |
427 | 359 |
| 360 bool V8ValueConverterImpl::UpdateAndCheckUniqueness( |
| 361 HashToHandleMap* map, |
| 362 v8::Handle<v8::Object> handle) const { |
| 363 typedef HashToHandleMap::const_iterator Iterator; |
| 364 |
| 365 int hash = avoid_identity_hash_for_testing_ ? 0 : handle->GetIdentityHash(); |
| 366 // We only compare using == with handles to objects with the same identity |
| 367 // hash. Different hash obviously means different objects, but two objects in |
| 368 // a couple of thousands could have the same identity hash. |
| 369 std::pair<Iterator, Iterator> range = map->equal_range(hash); |
| 370 for (Iterator it = range.first; it != range.second; ++it) { |
| 371 // Operator == for handles actually compares the underlying objects. |
| 372 if (it->second == handle) |
| 373 return false; |
| 374 } |
| 375 |
| 376 map->insert(std::pair<int, v8::Handle<v8::Object> >(hash, handle)); |
| 377 return true; |
| 378 } |
| 379 |
428 } // namespace content | 380 } // namespace content |
OLD | NEW |