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 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 
     80 V8ValueConverter* V8ValueConverter::create() {
     81   return new V8ValueConverterImpl();
     82 }
     83 
     84 V8ValueConverterImpl::V8ValueConverterImpl()
     85     : date_allowed_(false),
     86       reg_exp_allowed_(false),
     87       function_allowed_(false),
     88       strip_null_from_objects_(false),
     89       avoid_identity_hash_for_testing_(false),
     90       strategy_(NULL) {}
     91 
     92 void V8ValueConverterImpl::SetDateAllowed(bool val) {
     93   date_allowed_ = val;
     94 }
     95 
     96 void V8ValueConverterImpl::SetRegExpAllowed(bool val) {
     97   reg_exp_allowed_ = val;
     98 }
     99 
    100 void V8ValueConverterImpl::SetFunctionAllowed(bool val) {
    101   function_allowed_ = val;
    102 }
    103 
    104 void V8ValueConverterImpl::SetStripNullFromObjects(bool val) {
    105   strip_null_from_objects_ = val;
    106 }
    107 
    108 void V8ValueConverterImpl::SetStrategy(Strategy* strategy) {
    109   strategy_ = strategy;
    110 }
    111 
    112 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Value(
    113     const base::Value* value, v8::Handle<v8::Context> context) const {
    114   v8::Context::Scope context_scope(context);
    115   v8::HandleScope handle_scope;
    116   return handle_scope.Close(ToV8ValueImpl(value));
    117 }
    118 
    119 Value* V8ValueConverterImpl::FromV8Value(
    120     v8::Handle<v8::Value> val,
    121     v8::Handle<v8::Context> context) const {
    122   v8::Context::Scope context_scope(context);
    123   v8::HandleScope handle_scope;
    124   FromV8ValueState state(avoid_identity_hash_for_testing_);
    125   return FromV8ValueImpl(val, &state);
    126 }
    127 
    128 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8ValueImpl(
    129      const base::Value* value) const {
    130   CHECK(value);
    131   switch (value->GetType()) {
    132     case base::Value::TYPE_NULL:
    133       return v8::Null();
    134 
    135     case base::Value::TYPE_BOOLEAN: {
    136       bool val = false;
    137       CHECK(value->GetAsBoolean(&val));
    138       return v8::Boolean::New(val);
    139     }
    140 
    141     case base::Value::TYPE_INTEGER: {
    142       int val = 0;
    143       CHECK(value->GetAsInteger(&val));
    144       return v8::Integer::New(val);
    145     }
    146 
    147     case base::Value::TYPE_DOUBLE: {
    148       double val = 0.0;
    149       CHECK(value->GetAsDouble(&val));
    150       return v8::Number::New(val);
    151     }
    152 
    153     case base::Value::TYPE_STRING: {
    154       std::string val;
    155       CHECK(value->GetAsString(&val));
    156       return v8::String::New(val.c_str(), val.length());
    157     }
    158 
    159     case base::Value::TYPE_LIST:
    160       return ToV8Array(static_cast<const base::ListValue*>(value));
    161 
    162     case base::Value::TYPE_DICTIONARY:
    163       return ToV8Object(static_cast<const base::DictionaryValue*>(value));
    164 
    165     case base::Value::TYPE_BINARY:
    166       return ToArrayBuffer(static_cast<const base::BinaryValue*>(value));
    167 
    168     default:
    169       LOG(ERROR) << "Unexpected value type: " << value->GetType();
    170       return v8::Null();
    171   }
    172 }
    173 
    174 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Array(
    175     const base::ListValue* val) const {
    176   v8::Handle<v8::Array> result(v8::Array::New(val->GetSize()));
    177 
    178   for (size_t i = 0; i < val->GetSize(); ++i) {
    179     const base::Value* child = NULL;
    180     CHECK(val->Get(i, &child));
    181 
    182     v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(child);
    183     CHECK(!child_v8.IsEmpty());
    184 
    185     v8::TryCatch try_catch;
    186     result->Set(static_cast<uint32>(i), child_v8);
    187     if (try_catch.HasCaught())
    188       LOG(ERROR) << "Setter for index " << i << " threw an exception.";
    189   }
    190 
    191   return result;
    192 }
    193 
    194 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Object(
    195     const base::DictionaryValue* val) const {
    196   v8::Handle<v8::Object> result(v8::Object::New());
    197 
    198   for (base::DictionaryValue::Iterator iter(*val);
    199        !iter.IsAtEnd(); iter.Advance()) {
    200     const std::string& key = iter.key();
    201     v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(&iter.value());
    202     CHECK(!child_v8.IsEmpty());
    203 
    204     v8::TryCatch try_catch;
    205     result->Set(v8::String::New(key.c_str(), key.length()), child_v8);
    206     if (try_catch.HasCaught()) {
    207       LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
    208                  << "exception.";
    209     }
    210   }
    211 
    212   return result;
    213 }
    214 
    215 v8::Handle<v8::Value> V8ValueConverterImpl::ToArrayBuffer(
    216     const base::BinaryValue* value) const {
    217   WebKit::WebArrayBuffer buffer =
    218       WebKit::WebArrayBuffer::create(value->GetSize(), 1);
    219   memcpy(buffer.data(), value->GetBuffer(), value->GetSize());
    220   return buffer.toV8Value();
    221 }
    222 
    223 Value* V8ValueConverterImpl::FromV8ValueImpl(
    224     v8::Handle<v8::Value> val,
    225     FromV8ValueState* state) const {
    226   CHECK(!val.IsEmpty());
    227 
    228   FromV8ValueState::Level state_level(state);
    229   if (state->HasReachedMaxRecursionDepth())
    230     return NULL;
    231 
    232   if (val->IsNull())
    233     return base::Value::CreateNullValue();
    234 
    235   if (val->IsBoolean())
    236     return new base::FundamentalValue(val->ToBoolean()->Value());
    237 
    238   if (val->IsInt32())
    239     return new base::FundamentalValue(val->ToInt32()->Value());
    240 
    241   if (val->IsNumber()) {
    242     double val_as_double = val->ToNumber()->Value();
    243     if (!base::IsFinite(val_as_double))
    244       return NULL;
    245     return new base::FundamentalValue(val_as_double);
    246   }
    247 
    248   if (val->IsString()) {
    249     v8::String::Utf8Value utf8(val->ToString());
    250     return new base::StringValue(std::string(*utf8, utf8.length()));
    251   }
    252 
    253   if (val->IsUndefined())
    254     // JSON.stringify ignores undefined.
    255     return NULL;
    256 
    257   if (val->IsDate()) {
    258     if (!date_allowed_)
    259       // JSON.stringify would convert this to a string, but an object is more
    260       // consistent within this class.
    261       return FromV8Object(val->ToObject(), state);
    262     v8::Date* date = v8::Date::Cast(*val);
    263     return new base::FundamentalValue(date->NumberValue() / 1000.0);
    264   }
    265 
    266   if (val->IsRegExp()) {
    267     if (!reg_exp_allowed_)
    268       // JSON.stringify converts to an object.
    269       return FromV8Object(val->ToObject(), state);
    270     return new base::StringValue(*v8::String::Utf8Value(val->ToString()));
    271   }
    272 
    273   // v8::Value doesn't have a ToArray() method for some reason.
    274   if (val->IsArray())
    275     return FromV8Array(val.As<v8::Array>(), state);
    276 
    277   if (val->IsFunction()) {
    278     if (!function_allowed_)
    279       // JSON.stringify refuses to convert function(){}.
    280       return NULL;
    281     return FromV8Object(val->ToObject(), state);
    282   }
    283 
    284   if (val->IsObject()) {
    285     base::BinaryValue* binary_value = FromV8Buffer(val);
    286     if (binary_value) {
    287       return binary_value;
    288     } else {
    289       return FromV8Object(val->ToObject(), state);
    290     }
    291   }
    292 
    293   LOG(ERROR) << "Unexpected v8 value type encountered.";
    294   return NULL;
    295 }
    296 
    297 base::Value* V8ValueConverterImpl::FromV8Array(
    298     v8::Handle<v8::Array> val,
    299     FromV8ValueState* state) const {
    300   if (!state->UpdateAndCheckUniqueness(val))
    301     return base::Value::CreateNullValue();
    302 
    303   scoped_ptr<v8::Context::Scope> scope;
    304   // If val was created in a different context than our current one, change to
    305   // that context, but change back after val is converted.
    306   if (!val->CreationContext().IsEmpty() &&
    307       val->CreationContext() != v8::Context::GetCurrent())
    308     scope.reset(new v8::Context::Scope(val->CreationContext()));
    309 
    310   if (strategy_) {
    311     Value* out = NULL;
    312     if (strategy_->FromV8Array(val, &out))
    313       return out;
    314   }
    315 
    316   base::ListValue* result = new base::ListValue();
    317 
    318   // Only fields with integer keys are carried over to the ListValue.
    319   for (uint32 i = 0; i < val->Length(); ++i) {
    320     v8::TryCatch try_catch;
    321     v8::Handle<v8::Value> child_v8 = val->Get(i);
    322     if (try_catch.HasCaught()) {
    323       LOG(ERROR) << "Getter for index " << i << " threw an exception.";
    324       child_v8 = v8::Null();
    325     }
    326 
    327     if (!val->HasRealIndexedProperty(i))
    328       continue;
    329 
    330     base::Value* child = FromV8ValueImpl(child_v8, state);
    331     if (child)
    332       result->Append(child);
    333     else
    334       // JSON.stringify puts null in places where values don't serialize, for
    335       // example undefined and functions. Emulate that behavior.
    336       result->Append(base::Value::CreateNullValue());
    337   }
    338   return result;
    339 }
    340 
    341 base::BinaryValue* V8ValueConverterImpl::FromV8Buffer(
    342     v8::Handle<v8::Value> val) const {
    343   char* data = NULL;
    344   size_t length = 0;
    345 
    346   scoped_ptr<WebKit::WebArrayBuffer> array_buffer(
    347       WebKit::WebArrayBuffer::createFromV8Value(val));
    348   scoped_ptr<WebKit::WebArrayBufferView> view;
    349   if (array_buffer) {
    350     data = reinterpret_cast<char*>(array_buffer->data());
    351     length = array_buffer->byteLength();
    352   } else {
    353     view.reset(WebKit::WebArrayBufferView::createFromV8Value(val));
    354     if (view) {
    355       data = reinterpret_cast<char*>(view->baseAddress()) + view->byteOffset();
    356       length = view->byteLength();
    357     }
    358   }
    359 
    360   if (data)
    361     return base::BinaryValue::CreateWithCopiedBuffer(data, length);
    362   else
    363     return NULL;
    364 }
    365 
    366 base::Value* V8ValueConverterImpl::FromV8Object(
    367     v8::Handle<v8::Object> val,
    368     FromV8ValueState* state) const {
    369   if (!state->UpdateAndCheckUniqueness(val))
    370     return base::Value::CreateNullValue();
    371 
    372   scoped_ptr<v8::Context::Scope> scope;
    373   // If val was created in a different context than our current one, change to
    374   // that context, but change back after val is converted.
    375   if (!val->CreationContext().IsEmpty() &&
    376       val->CreationContext() != v8::Context::GetCurrent())
    377     scope.reset(new v8::Context::Scope(val->CreationContext()));
    378 
    379   if (strategy_) {
    380     Value* out = NULL;
    381     if (strategy_->FromV8Object(val, &out))
    382       return out;
    383   }
    384 
    385   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
    386   v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames());
    387 
    388   for (uint32 i = 0; i < property_names->Length(); ++i) {
    389     v8::Handle<v8::Value> key(property_names->Get(i));
    390 
    391     // Extend this test to cover more types as necessary and if sensible.
    392     if (!key->IsString() &&
    393         !key->IsNumber()) {
    394       NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" "
    395                       "is neither a string nor a number";
    396       continue;
    397     }
    398 
    399     v8::String::Utf8Value name_utf8(key->ToString());
    400 
    401     v8::TryCatch try_catch;
    402     v8::Handle<v8::Value> child_v8 = val->Get(key);
    403 
    404     if (try_catch.HasCaught()) {
    405       LOG(ERROR) << "Getter for property " << *name_utf8
    406                  << " threw an exception.";
    407       child_v8 = v8::Null();
    408     }
    409 
    410     scoped_ptr<base::Value> child(FromV8ValueImpl(child_v8, state));
    411     if (!child)
    412       // JSON.stringify skips properties whose values don't serialize, for
    413       // example undefined and functions. Emulate that behavior.
    414       continue;
    415 
    416     // Strip null if asked (and since undefined is turned into null, undefined
    417     // too). The use case for supporting this is JSON-schema support,
    418     // specifically for extensions, where "optional" JSON properties may be
    419     // represented as null, yet due to buggy legacy code elsewhere isn't
    420     // treated as such (potentially causing crashes). For example, the
    421     // "tabs.create" function takes an object as its first argument with an
    422     // optional "windowId" property.
    423     //
    424     // Given just
    425     //
    426     //   tabs.create({})
    427     //
    428     // this will work as expected on code that only checks for the existence of
    429     // a "windowId" property (such as that legacy code). However given
    430     //
    431     //   tabs.create({windowId: null})
    432     //
    433     // there *is* a "windowId" property, but since it should be an int, code
    434     // on the browser which doesn't additionally check for null will fail.
    435     // We can avoid all bugs related to this by stripping null.
    436     if (strip_null_from_objects_ && child->IsType(base::Value::TYPE_NULL))
    437       continue;
    438 
    439     result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
    440                                     child.release());
    441   }
    442 
    443   return result.release();
    444 }
    445 
    446 }  // namespace content
    447