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