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/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 
     24 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 
     32 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 
     40 bool V8ValueConverter::Strategy::FromV8ArrayBuffer(v8::Handle<v8::Object> value,
     41                                                    base::Value** out,
     42                                                    v8::Isolate* isolate) const {
     43   return false;
     44 }
     45 
     46 bool V8ValueConverter::Strategy::FromV8Number(v8::Handle<v8::Number> value,
     47                                               base::Value** out) const {
     48   return false;
     49 }
     50 
     51 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:
     69     explicit Level(FromV8ValueState* state) : state_(state) {
     70       state_->max_recursion_depth_--;
     71     }
     72     ~Level() {
     73       state_->max_recursion_depth_++;
     74     }
     75 
     76    private:
     77     FromV8ValueState* state_;
     78   };
     79 
     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.
     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 
    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 
    120 V8ValueConverter* V8ValueConverter::create() {
    121   return new V8ValueConverterImpl();
    122 }
    123 
    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 
    132 void V8ValueConverterImpl::SetDateAllowed(bool val) {
    133   date_allowed_ = val;
    134 }
    135 
    136 void V8ValueConverterImpl::SetRegExpAllowed(bool val) {
    137   reg_exp_allowed_ = val;
    138 }
    139 
    140 void V8ValueConverterImpl::SetFunctionAllowed(bool val) {
    141   function_allowed_ = val;
    142 }
    143 
    144 void V8ValueConverterImpl::SetStripNullFromObjects(bool val) {
    145   strip_null_from_objects_ = val;
    146 }
    147 
    148 void V8ValueConverterImpl::SetStrategy(Strategy* strategy) {
    149   strategy_ = strategy;
    150 }
    151 
    152 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 
    160 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 
    169 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 
    224 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 
    247 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 
    274 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 
    285 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 
    369 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 
    422 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 
    454 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