• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <string>
8 
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/float_util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/values.h"
15 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
16 #include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
17 #include "third_party/WebKit/public/web/WebArrayBufferView.h"
18 #include "v8/include/v8.h"
19 
20 namespace content {
21 
22 // Default implementation of V8ValueConverter::Strategy
23 
FromV8Object(v8::Handle<v8::Object> value,base::Value ** out,v8::Isolate * isolate,const FromV8ValueCallback & callback) const24 bool V8ValueConverter::Strategy::FromV8Object(
25     v8::Handle<v8::Object> value,
26     base::Value** out,
27     v8::Isolate* isolate,
28     const FromV8ValueCallback& callback) const {
29   return false;
30 }
31 
FromV8Array(v8::Handle<v8::Array> value,base::Value ** out,v8::Isolate * isolate,const FromV8ValueCallback & callback) const32 bool V8ValueConverter::Strategy::FromV8Array(
33     v8::Handle<v8::Array> value,
34     base::Value** out,
35     v8::Isolate* isolate,
36     const FromV8ValueCallback& callback) const {
37   return false;
38 }
39 
FromV8ArrayBuffer(v8::Handle<v8::Object> value,base::Value ** out,v8::Isolate * isolate) const40 bool V8ValueConverter::Strategy::FromV8ArrayBuffer(v8::Handle<v8::Object> value,
41                                                    base::Value** out,
42                                                    v8::Isolate* isolate) const {
43   return false;
44 }
45 
FromV8Number(v8::Handle<v8::Number> value,base::Value ** out) const46 bool V8ValueConverter::Strategy::FromV8Number(v8::Handle<v8::Number> value,
47                                               base::Value** out) const {
48   return false;
49 }
50 
FromV8Undefined(base::Value ** out) const51 bool V8ValueConverter::Strategy::FromV8Undefined(base::Value** out) const {
52   return false;
53 }
54 
55 
56 namespace {
57 
58 // For the sake of the storage API, make this quite large.
59 const int kMaxRecursionDepth = 100;
60 
61 }  // namespace
62 
63 // The state of a call to FromV8Value.
64 class V8ValueConverterImpl::FromV8ValueState {
65  public:
66   // Level scope which updates the current depth of some FromV8ValueState.
67   class Level {
68    public:
Level(FromV8ValueState * state)69     explicit Level(FromV8ValueState* state) : state_(state) {
70       state_->max_recursion_depth_--;
71     }
~Level()72     ~Level() {
73       state_->max_recursion_depth_++;
74     }
75 
76    private:
77     FromV8ValueState* state_;
78   };
79 
FromV8ValueState(bool avoid_identity_hash_for_testing)80   explicit FromV8ValueState(bool avoid_identity_hash_for_testing)
81       : max_recursion_depth_(kMaxRecursionDepth),
82         avoid_identity_hash_for_testing_(avoid_identity_hash_for_testing) {}
83 
84   // If |handle| is not in |unique_map_|, then add it to |unique_map_| and
85   // return true.
86   //
87   // Otherwise do nothing and return false. Here "A is unique" means that no
88   // other handle B in the map points to the same object as A. Note that A can
89   // be unique even if there already is another handle with the same identity
90   // hash (key) in the map, because two objects can have the same hash.
UpdateAndCheckUniqueness(v8::Handle<v8::Object> handle)91   bool UpdateAndCheckUniqueness(v8::Handle<v8::Object> handle) {
92     typedef HashToHandleMap::const_iterator Iterator;
93     int hash = avoid_identity_hash_for_testing_ ? 0 : handle->GetIdentityHash();
94     // We only compare using == with handles to objects with the same identity
95     // hash. Different hash obviously means different objects, but two objects
96     // in a couple of thousands could have the same identity hash.
97     std::pair<Iterator, Iterator> range = unique_map_.equal_range(hash);
98     for (Iterator it = range.first; it != range.second; ++it) {
99       // Operator == for handles actually compares the underlying objects.
100       if (it->second == handle)
101         return false;
102     }
103     unique_map_.insert(std::make_pair(hash, handle));
104     return true;
105   }
106 
HasReachedMaxRecursionDepth()107   bool HasReachedMaxRecursionDepth() {
108     return max_recursion_depth_ < 0;
109   }
110 
111  private:
112   typedef std::multimap<int, v8::Handle<v8::Object> > HashToHandleMap;
113   HashToHandleMap unique_map_;
114 
115   int max_recursion_depth_;
116 
117   bool avoid_identity_hash_for_testing_;
118 };
119 
create()120 V8ValueConverter* V8ValueConverter::create() {
121   return new V8ValueConverterImpl();
122 }
123 
V8ValueConverterImpl()124 V8ValueConverterImpl::V8ValueConverterImpl()
125     : date_allowed_(false),
126       reg_exp_allowed_(false),
127       function_allowed_(false),
128       strip_null_from_objects_(false),
129       avoid_identity_hash_for_testing_(false),
130       strategy_(NULL) {}
131 
SetDateAllowed(bool val)132 void V8ValueConverterImpl::SetDateAllowed(bool val) {
133   date_allowed_ = val;
134 }
135 
SetRegExpAllowed(bool val)136 void V8ValueConverterImpl::SetRegExpAllowed(bool val) {
137   reg_exp_allowed_ = val;
138 }
139 
SetFunctionAllowed(bool val)140 void V8ValueConverterImpl::SetFunctionAllowed(bool val) {
141   function_allowed_ = val;
142 }
143 
SetStripNullFromObjects(bool val)144 void V8ValueConverterImpl::SetStripNullFromObjects(bool val) {
145   strip_null_from_objects_ = val;
146 }
147 
SetStrategy(Strategy * strategy)148 void V8ValueConverterImpl::SetStrategy(Strategy* strategy) {
149   strategy_ = strategy;
150 }
151 
ToV8Value(const base::Value * value,v8::Handle<v8::Context> context) const152 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Value(
153     const base::Value* value, v8::Handle<v8::Context> context) const {
154   v8::Context::Scope context_scope(context);
155   v8::EscapableHandleScope handle_scope(context->GetIsolate());
156   return handle_scope.Escape(
157       ToV8ValueImpl(context->GetIsolate(), context->Global(), value));
158 }
159 
FromV8Value(v8::Handle<v8::Value> val,v8::Handle<v8::Context> context) const160 base::Value* V8ValueConverterImpl::FromV8Value(
161     v8::Handle<v8::Value> val,
162     v8::Handle<v8::Context> context) const {
163   v8::Context::Scope context_scope(context);
164   v8::HandleScope handle_scope(context->GetIsolate());
165   FromV8ValueState state(avoid_identity_hash_for_testing_);
166   return FromV8ValueImpl(&state, val, context->GetIsolate());
167 }
168 
ToV8ValueImpl(v8::Isolate * isolate,v8::Handle<v8::Object> creation_context,const base::Value * value) const169 v8::Local<v8::Value> V8ValueConverterImpl::ToV8ValueImpl(
170     v8::Isolate* isolate,
171     v8::Handle<v8::Object> creation_context,
172     const base::Value* value) const {
173   CHECK(value);
174   switch (value->GetType()) {
175     case base::Value::TYPE_NULL:
176       return v8::Null(isolate);
177 
178     case base::Value::TYPE_BOOLEAN: {
179       bool val = false;
180       CHECK(value->GetAsBoolean(&val));
181       return v8::Boolean::New(isolate, val);
182     }
183 
184     case base::Value::TYPE_INTEGER: {
185       int val = 0;
186       CHECK(value->GetAsInteger(&val));
187       return v8::Integer::New(isolate, val);
188     }
189 
190     case base::Value::TYPE_DOUBLE: {
191       double val = 0.0;
192       CHECK(value->GetAsDouble(&val));
193       return v8::Number::New(isolate, val);
194     }
195 
196     case base::Value::TYPE_STRING: {
197       std::string val;
198       CHECK(value->GetAsString(&val));
199       return v8::String::NewFromUtf8(
200           isolate, val.c_str(), v8::String::kNormalString, val.length());
201     }
202 
203     case base::Value::TYPE_LIST:
204       return ToV8Array(isolate,
205                        creation_context,
206                        static_cast<const base::ListValue*>(value));
207 
208     case base::Value::TYPE_DICTIONARY:
209       return ToV8Object(isolate,
210                         creation_context,
211                         static_cast<const base::DictionaryValue*>(value));
212 
213     case base::Value::TYPE_BINARY:
214       return ToArrayBuffer(isolate,
215                            creation_context,
216                            static_cast<const base::BinaryValue*>(value));
217 
218     default:
219       LOG(ERROR) << "Unexpected value type: " << value->GetType();
220       return v8::Null(isolate);
221   }
222 }
223 
ToV8Array(v8::Isolate * isolate,v8::Handle<v8::Object> creation_context,const base::ListValue * val) const224 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Array(
225     v8::Isolate* isolate,
226     v8::Handle<v8::Object> creation_context,
227     const base::ListValue* val) const {
228   v8::Handle<v8::Array> result(v8::Array::New(isolate, val->GetSize()));
229 
230   for (size_t i = 0; i < val->GetSize(); ++i) {
231     const base::Value* child = NULL;
232     CHECK(val->Get(i, &child));
233 
234     v8::Handle<v8::Value> child_v8 =
235         ToV8ValueImpl(isolate, creation_context, child);
236     CHECK(!child_v8.IsEmpty());
237 
238     v8::TryCatch try_catch;
239     result->Set(static_cast<uint32>(i), child_v8);
240     if (try_catch.HasCaught())
241       LOG(ERROR) << "Setter for index " << i << " threw an exception.";
242   }
243 
244   return result;
245 }
246 
ToV8Object(v8::Isolate * isolate,v8::Handle<v8::Object> creation_context,const base::DictionaryValue * val) const247 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Object(
248     v8::Isolate* isolate,
249     v8::Handle<v8::Object> creation_context,
250     const base::DictionaryValue* val) const {
251   v8::Handle<v8::Object> result(v8::Object::New(isolate));
252 
253   for (base::DictionaryValue::Iterator iter(*val);
254        !iter.IsAtEnd(); iter.Advance()) {
255     const std::string& key = iter.key();
256     v8::Handle<v8::Value> child_v8 =
257         ToV8ValueImpl(isolate, creation_context, &iter.value());
258     CHECK(!child_v8.IsEmpty());
259 
260     v8::TryCatch try_catch;
261     result->Set(
262         v8::String::NewFromUtf8(
263             isolate, key.c_str(), v8::String::kNormalString, key.length()),
264         child_v8);
265     if (try_catch.HasCaught()) {
266       LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
267                  << "exception.";
268     }
269   }
270 
271   return result;
272 }
273 
ToArrayBuffer(v8::Isolate * isolate,v8::Handle<v8::Object> creation_context,const base::BinaryValue * value) const274 v8::Handle<v8::Value> V8ValueConverterImpl::ToArrayBuffer(
275     v8::Isolate* isolate,
276     v8::Handle<v8::Object> creation_context,
277     const base::BinaryValue* value) const {
278   blink::WebArrayBuffer buffer =
279       blink::WebArrayBuffer::create(value->GetSize(), 1);
280   memcpy(buffer.data(), value->GetBuffer(), value->GetSize());
281   return blink::WebArrayBufferConverter::toV8Value(
282       &buffer, creation_context, isolate);
283 }
284 
FromV8ValueImpl(FromV8ValueState * state,v8::Handle<v8::Value> val,v8::Isolate * isolate) const285 base::Value* V8ValueConverterImpl::FromV8ValueImpl(
286     FromV8ValueState* state,
287     v8::Handle<v8::Value> val,
288     v8::Isolate* isolate) const {
289   CHECK(!val.IsEmpty());
290 
291   FromV8ValueState::Level state_level(state);
292   if (state->HasReachedMaxRecursionDepth())
293     return NULL;
294 
295   if (val->IsNull())
296     return base::Value::CreateNullValue();
297 
298   if (val->IsBoolean())
299     return new base::FundamentalValue(val->ToBoolean()->Value());
300 
301   if (val->IsNumber() && strategy_) {
302     base::Value* out = NULL;
303     if (strategy_->FromV8Number(val->ToNumber(), &out))
304       return out;
305   }
306 
307   if (val->IsInt32())
308     return new base::FundamentalValue(val->ToInt32()->Value());
309 
310   if (val->IsNumber()) {
311     double val_as_double = val->ToNumber()->Value();
312     if (!base::IsFinite(val_as_double))
313       return NULL;
314     return new base::FundamentalValue(val_as_double);
315   }
316 
317   if (val->IsString()) {
318     v8::String::Utf8Value utf8(val->ToString());
319     return new base::StringValue(std::string(*utf8, utf8.length()));
320   }
321 
322   if (val->IsUndefined()) {
323     if (strategy_) {
324       base::Value* out = NULL;
325       if (strategy_->FromV8Undefined(&out))
326         return out;
327     }
328     // JSON.stringify ignores undefined.
329     return NULL;
330   }
331 
332   if (val->IsDate()) {
333     if (!date_allowed_)
334       // JSON.stringify would convert this to a string, but an object is more
335       // consistent within this class.
336       return FromV8Object(val->ToObject(), state, isolate);
337     v8::Date* date = v8::Date::Cast(*val);
338     return new base::FundamentalValue(date->ValueOf() / 1000.0);
339   }
340 
341   if (val->IsRegExp()) {
342     if (!reg_exp_allowed_)
343       // JSON.stringify converts to an object.
344       return FromV8Object(val->ToObject(), state, isolate);
345     return new base::StringValue(*v8::String::Utf8Value(val->ToString()));
346   }
347 
348   // v8::Value doesn't have a ToArray() method for some reason.
349   if (val->IsArray())
350     return FromV8Array(val.As<v8::Array>(), state, isolate);
351 
352   if (val->IsFunction()) {
353     if (!function_allowed_)
354       // JSON.stringify refuses to convert function(){}.
355       return NULL;
356     return FromV8Object(val->ToObject(), state, isolate);
357   }
358 
359   if (val->IsArrayBuffer() || val->IsArrayBufferView())
360     return FromV8ArrayBuffer(val->ToObject(), isolate);
361 
362   if (val->IsObject())
363     return FromV8Object(val->ToObject(), state, isolate);
364 
365   LOG(ERROR) << "Unexpected v8 value type encountered.";
366   return NULL;
367 }
368 
FromV8Array(v8::Handle<v8::Array> val,FromV8ValueState * state,v8::Isolate * isolate) const369 base::Value* V8ValueConverterImpl::FromV8Array(
370     v8::Handle<v8::Array> val,
371     FromV8ValueState* state,
372     v8::Isolate* isolate) const {
373   if (!state->UpdateAndCheckUniqueness(val))
374     return base::Value::CreateNullValue();
375 
376   scoped_ptr<v8::Context::Scope> scope;
377   // If val was created in a different context than our current one, change to
378   // that context, but change back after val is converted.
379   if (!val->CreationContext().IsEmpty() &&
380       val->CreationContext() != isolate->GetCurrentContext())
381     scope.reset(new v8::Context::Scope(val->CreationContext()));
382 
383   if (strategy_) {
384     // These base::Unretained's are safe, because Strategy::FromV8Value should
385     // be synchronous, so this object can't be out of scope.
386     V8ValueConverter::Strategy::FromV8ValueCallback callback =
387         base::Bind(&V8ValueConverterImpl::FromV8ValueImpl,
388                    base::Unretained(this),
389                    base::Unretained(state));
390     base::Value* out = NULL;
391     if (strategy_->FromV8Array(val, &out, isolate, callback))
392       return out;
393   }
394 
395   base::ListValue* result = new base::ListValue();
396 
397   // Only fields with integer keys are carried over to the ListValue.
398   for (uint32 i = 0; i < val->Length(); ++i) {
399     v8::TryCatch try_catch;
400     v8::Handle<v8::Value> child_v8 = val->Get(i);
401     if (try_catch.HasCaught()) {
402       LOG(ERROR) << "Getter for index " << i << " threw an exception.";
403       child_v8 = v8::Null(isolate);
404     }
405 
406     if (!val->HasRealIndexedProperty(i)) {
407       result->Append(base::Value::CreateNullValue());
408       continue;
409     }
410 
411     base::Value* child = FromV8ValueImpl(state, child_v8, isolate);
412     if (child)
413       result->Append(child);
414     else
415       // JSON.stringify puts null in places where values don't serialize, for
416       // example undefined and functions. Emulate that behavior.
417       result->Append(base::Value::CreateNullValue());
418   }
419   return result;
420 }
421 
FromV8ArrayBuffer(v8::Handle<v8::Object> val,v8::Isolate * isolate) const422 base::Value* V8ValueConverterImpl::FromV8ArrayBuffer(
423     v8::Handle<v8::Object> val,
424     v8::Isolate* isolate) const {
425   if (strategy_) {
426     base::Value* out = NULL;
427     if (strategy_->FromV8ArrayBuffer(val, &out, isolate))
428       return out;
429   }
430 
431   char* data = NULL;
432   size_t length = 0;
433 
434   scoped_ptr<blink::WebArrayBuffer> array_buffer(
435       blink::WebArrayBufferConverter::createFromV8Value(val, isolate));
436   scoped_ptr<blink::WebArrayBufferView> view;
437   if (array_buffer) {
438     data = reinterpret_cast<char*>(array_buffer->data());
439     length = array_buffer->byteLength();
440   } else {
441     view.reset(blink::WebArrayBufferView::createFromV8Value(val));
442     if (view) {
443       data = reinterpret_cast<char*>(view->baseAddress()) + view->byteOffset();
444       length = view->byteLength();
445     }
446   }
447 
448   if (data)
449     return base::BinaryValue::CreateWithCopiedBuffer(data, length);
450   else
451     return NULL;
452 }
453 
FromV8Object(v8::Handle<v8::Object> val,FromV8ValueState * state,v8::Isolate * isolate) const454 base::Value* V8ValueConverterImpl::FromV8Object(
455     v8::Handle<v8::Object> val,
456     FromV8ValueState* state,
457     v8::Isolate* isolate) const {
458   if (!state->UpdateAndCheckUniqueness(val))
459     return base::Value::CreateNullValue();
460 
461   scoped_ptr<v8::Context::Scope> scope;
462   // If val was created in a different context than our current one, change to
463   // that context, but change back after val is converted.
464   if (!val->CreationContext().IsEmpty() &&
465       val->CreationContext() != isolate->GetCurrentContext())
466     scope.reset(new v8::Context::Scope(val->CreationContext()));
467 
468   if (strategy_) {
469     // These base::Unretained's are safe, because Strategy::FromV8Value should
470     // be synchronous, so this object can't be out of scope.
471     V8ValueConverter::Strategy::FromV8ValueCallback callback =
472         base::Bind(&V8ValueConverterImpl::FromV8ValueImpl,
473                    base::Unretained(this),
474                    base::Unretained(state));
475     base::Value* out = NULL;
476     if (strategy_->FromV8Object(val, &out, isolate, callback))
477       return out;
478   }
479 
480   // Don't consider DOM objects. This check matches isHostObject() in Blink's
481   // bindings/v8/V8Binding.h used in structured cloning. It reads:
482   //
483   // If the object has any internal fields, then we won't be able to serialize
484   // or deserialize them; conveniently, this is also a quick way to detect DOM
485   // wrapper objects, because the mechanism for these relies on data stored in
486   // these fields.
487   //
488   // NOTE: check this after |strategy_| so that callers have a chance to
489   // do something else, such as convert to the node's name rather than NULL.
490   //
491   // ANOTHER NOTE: returning an empty dictionary here to minimise surprise.
492   // See also http://crbug.com/330559.
493   if (val->InternalFieldCount())
494     return new base::DictionaryValue();
495 
496   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
497   v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames());
498 
499   for (uint32 i = 0; i < property_names->Length(); ++i) {
500     v8::Handle<v8::Value> key(property_names->Get(i));
501 
502     // Extend this test to cover more types as necessary and if sensible.
503     if (!key->IsString() &&
504         !key->IsNumber()) {
505       NOTREACHED() << "Key \"" << *v8::String::Utf8Value(key) << "\" "
506                       "is neither a string nor a number";
507       continue;
508     }
509 
510     v8::String::Utf8Value name_utf8(key->ToString());
511 
512     v8::TryCatch try_catch;
513     v8::Handle<v8::Value> child_v8 = val->Get(key);
514 
515     if (try_catch.HasCaught()) {
516       LOG(WARNING) << "Getter for property " << *name_utf8
517                    << " threw an exception.";
518       child_v8 = v8::Null(isolate);
519     }
520 
521     scoped_ptr<base::Value> child(FromV8ValueImpl(state, child_v8, isolate));
522     if (!child)
523       // JSON.stringify skips properties whose values don't serialize, for
524       // example undefined and functions. Emulate that behavior.
525       continue;
526 
527     // Strip null if asked (and since undefined is turned into null, undefined
528     // too). The use case for supporting this is JSON-schema support,
529     // specifically for extensions, where "optional" JSON properties may be
530     // represented as null, yet due to buggy legacy code elsewhere isn't
531     // treated as such (potentially causing crashes). For example, the
532     // "tabs.create" function takes an object as its first argument with an
533     // optional "windowId" property.
534     //
535     // Given just
536     //
537     //   tabs.create({})
538     //
539     // this will work as expected on code that only checks for the existence of
540     // a "windowId" property (such as that legacy code). However given
541     //
542     //   tabs.create({windowId: null})
543     //
544     // there *is* a "windowId" property, but since it should be an int, code
545     // on the browser which doesn't additionally check for null will fail.
546     // We can avoid all bugs related to this by stripping null.
547     if (strip_null_from_objects_ && child->IsType(base::Value::TYPE_NULL))
548       continue;
549 
550     result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
551                                     child.release());
552   }
553 
554   return result.release();
555 }
556 
557 }  // namespace content
558