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