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