Disable ContentSettingBubbleModelTest.RPHAllow which is flaky.
[chromium-blink-merge.git] / content / renderer / v8_value_converter_impl.cc
blobe8b256303df7b0fa3e9f9e127928e28d6ba89b8e
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.
5 #include "content/renderer/v8_value_converter_impl.h"
7 #include <string>
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/values.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBufferView.h"
14 #include "v8/include/v8.h"
16 namespace content {
18 V8ValueConverter* V8ValueConverter::create() {
19 return new V8ValueConverterImpl();
22 V8ValueConverterImpl::V8ValueConverterImpl()
23 : date_allowed_(false),
24 reg_exp_allowed_(false),
25 function_allowed_(false),
26 strip_null_from_objects_(false),
27 avoid_identity_hash_for_testing_(false) {}
29 void V8ValueConverterImpl::SetDateAllowed(bool val) {
30 date_allowed_ = val;
33 void V8ValueConverterImpl::SetRegExpAllowed(bool val) {
34 reg_exp_allowed_ = val;
37 void V8ValueConverterImpl::SetFunctionAllowed(bool val) {
38 function_allowed_ = val;
41 void V8ValueConverterImpl::SetStripNullFromObjects(bool val) {
42 strip_null_from_objects_ = val;
45 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Value(
46 const base::Value* value, v8::Handle<v8::Context> context) const {
47 v8::Context::Scope context_scope(context);
48 v8::HandleScope handle_scope;
49 return handle_scope.Close(ToV8ValueImpl(value));
52 Value* V8ValueConverterImpl::FromV8Value(
53 v8::Handle<v8::Value> val,
54 v8::Handle<v8::Context> context) const {
55 v8::Context::Scope context_scope(context);
56 v8::HandleScope handle_scope;
57 HashToHandleMap unique_map;
58 return FromV8ValueImpl(val, &unique_map);
61 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8ValueImpl(
62 const base::Value* value) const {
63 CHECK(value);
64 switch (value->GetType()) {
65 case base::Value::TYPE_NULL:
66 return v8::Null();
68 case base::Value::TYPE_BOOLEAN: {
69 bool val = false;
70 CHECK(value->GetAsBoolean(&val));
71 return v8::Boolean::New(val);
74 case base::Value::TYPE_INTEGER: {
75 int val = 0;
76 CHECK(value->GetAsInteger(&val));
77 return v8::Integer::New(val);
80 case base::Value::TYPE_DOUBLE: {
81 double val = 0.0;
82 CHECK(value->GetAsDouble(&val));
83 return v8::Number::New(val);
86 case base::Value::TYPE_STRING: {
87 std::string val;
88 CHECK(value->GetAsString(&val));
89 return v8::String::New(val.c_str(), val.length());
92 case base::Value::TYPE_LIST:
93 return ToV8Array(static_cast<const base::ListValue*>(value));
95 case base::Value::TYPE_DICTIONARY:
96 return ToV8Object(static_cast<const base::DictionaryValue*>(value));
98 case base::Value::TYPE_BINARY:
99 return ToArrayBuffer(static_cast<const base::BinaryValue*>(value));
101 default:
102 LOG(ERROR) << "Unexpected value type: " << value->GetType();
103 return v8::Null();
107 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Array(
108 const base::ListValue* val) const {
109 v8::Handle<v8::Array> result(v8::Array::New(val->GetSize()));
111 for (size_t i = 0; i < val->GetSize(); ++i) {
112 const base::Value* child = NULL;
113 CHECK(val->Get(i, &child));
115 v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(child);
116 CHECK(!child_v8.IsEmpty());
118 v8::TryCatch try_catch;
119 result->Set(static_cast<uint32>(i), child_v8);
120 if (try_catch.HasCaught())
121 LOG(ERROR) << "Setter for index " << i << " threw an exception.";
124 return result;
127 v8::Handle<v8::Value> V8ValueConverterImpl::ToV8Object(
128 const base::DictionaryValue* val) const {
129 v8::Handle<v8::Object> result(v8::Object::New());
131 for (base::DictionaryValue::Iterator iter(*val);
132 !iter.IsAtEnd(); iter.Advance()) {
133 const std::string& key = iter.key();
134 v8::Handle<v8::Value> child_v8 = ToV8ValueImpl(&iter.value());
135 CHECK(!child_v8.IsEmpty());
137 v8::TryCatch try_catch;
138 result->Set(v8::String::New(key.c_str(), key.length()), child_v8);
139 if (try_catch.HasCaught()) {
140 LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
141 << "exception.";
145 return result;
148 v8::Handle<v8::Value> V8ValueConverterImpl::ToArrayBuffer(
149 const base::BinaryValue* value) const {
150 WebKit::WebArrayBuffer buffer =
151 WebKit::WebArrayBuffer::create(value->GetSize(), 1);
152 memcpy(buffer.data(), value->GetBuffer(), value->GetSize());
153 return buffer.toV8Value();
156 Value* V8ValueConverterImpl::FromV8ValueImpl(v8::Handle<v8::Value> val,
157 HashToHandleMap* unique_map) const {
158 CHECK(!val.IsEmpty());
160 if (val->IsNull())
161 return base::Value::CreateNullValue();
163 if (val->IsBoolean())
164 return new base::FundamentalValue(val->ToBoolean()->Value());
166 if (val->IsInt32())
167 return new base::FundamentalValue(val->ToInt32()->Value());
169 if (val->IsNumber())
170 return new base::FundamentalValue(val->ToNumber()->Value());
172 if (val->IsString()) {
173 v8::String::Utf8Value utf8(val->ToString());
174 return new base::StringValue(std::string(*utf8, utf8.length()));
177 if (val->IsUndefined())
178 // JSON.stringify ignores undefined.
179 return NULL;
181 if (val->IsDate()) {
182 if (!date_allowed_)
183 // JSON.stringify would convert this to a string, but an object is more
184 // consistent within this class.
185 return FromV8Object(val->ToObject(), unique_map);
186 v8::Date* date = v8::Date::Cast(*val);
187 return new base::FundamentalValue(date->NumberValue() / 1000.0);
190 if (val->IsRegExp()) {
191 if (!reg_exp_allowed_)
192 // JSON.stringify converts to an object.
193 return FromV8Object(val->ToObject(), unique_map);
194 return new base::StringValue(*v8::String::Utf8Value(val->ToString()));
197 // v8::Value doesn't have a ToArray() method for some reason.
198 if (val->IsArray())
199 return FromV8Array(val.As<v8::Array>(), unique_map);
201 if (val->IsFunction()) {
202 if (!function_allowed_)
203 // JSON.stringify refuses to convert function(){}.
204 return NULL;
205 return FromV8Object(val->ToObject(), unique_map);
208 if (val->IsObject()) {
209 base::BinaryValue* binary_value = FromV8Buffer(val);
210 if (binary_value) {
211 return binary_value;
212 } else {
213 return FromV8Object(val->ToObject(), unique_map);
217 LOG(ERROR) << "Unexpected v8 value type encountered.";
218 return NULL;
221 Value* V8ValueConverterImpl::FromV8Array(v8::Handle<v8::Array> val,
222 HashToHandleMap* unique_map) const {
223 if (!UpdateAndCheckUniqueness(unique_map, val))
224 return base::Value::CreateNullValue();
226 scoped_ptr<v8::Context::Scope> scope;
227 // If val was created in a different context than our current one, change to
228 // that context, but change back after val is converted.
229 if (!val->CreationContext().IsEmpty() &&
230 val->CreationContext() != v8::Context::GetCurrent())
231 scope.reset(new v8::Context::Scope(val->CreationContext()));
233 base::ListValue* result = new base::ListValue();
235 // Only fields with integer keys are carried over to the ListValue.
236 for (uint32 i = 0; i < val->Length(); ++i) {
237 v8::TryCatch try_catch;
238 v8::Handle<v8::Value> child_v8 = val->Get(i);
239 if (try_catch.HasCaught()) {
240 LOG(ERROR) << "Getter for index " << i << " threw an exception.";
241 child_v8 = v8::Null();
244 if (!val->HasRealIndexedProperty(i))
245 continue;
247 base::Value* child = FromV8ValueImpl(child_v8, unique_map);
248 if (child)
249 result->Append(child);
250 else
251 // JSON.stringify puts null in places where values don't serialize, for
252 // example undefined and functions. Emulate that behavior.
253 result->Append(base::Value::CreateNullValue());
255 return result;
258 base::BinaryValue* V8ValueConverterImpl::FromV8Buffer(
259 v8::Handle<v8::Value> val) const {
260 char* data = NULL;
261 size_t length = 0;
263 scoped_ptr<WebKit::WebArrayBuffer> array_buffer(
264 WebKit::WebArrayBuffer::createFromV8Value(val));
265 scoped_ptr<WebKit::WebArrayBufferView> view;
266 if (array_buffer) {
267 data = reinterpret_cast<char*>(array_buffer->data());
268 length = array_buffer->byteLength();
269 } else {
270 view.reset(WebKit::WebArrayBufferView::createFromV8Value(val));
271 if (view) {
272 data = reinterpret_cast<char*>(view->baseAddress()) + view->byteOffset();
273 length = view->byteLength();
277 if (data)
278 return base::BinaryValue::CreateWithCopiedBuffer(data, length);
279 else
280 return NULL;
283 Value* V8ValueConverterImpl::FromV8Object(
284 v8::Handle<v8::Object> val,
285 HashToHandleMap* unique_map) const {
286 if (!UpdateAndCheckUniqueness(unique_map, val))
287 return base::Value::CreateNullValue();
288 scoped_ptr<v8::Context::Scope> scope;
289 // If val was created in a different context than our current one, change to
290 // that context, but change back after val is converted.
291 if (!val->CreationContext().IsEmpty() &&
292 val->CreationContext() != v8::Context::GetCurrent())
293 scope.reset(new v8::Context::Scope(val->CreationContext()));
295 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
296 v8::Handle<v8::Array> property_names(val->GetOwnPropertyNames());
298 for (uint32 i = 0; i < property_names->Length(); ++i) {
299 v8::Handle<v8::Value> key(property_names->Get(i));
301 // Extend this test to cover more types as necessary and if sensible.
302 if (!key->IsString() &&
303 !key->IsNumber()) {
304 NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" "
305 "is neither a string nor a number";
306 continue;
309 // Skip all callbacks: crbug.com/139933
310 if (val->HasRealNamedCallbackProperty(key->ToString()))
311 continue;
313 v8::String::Utf8Value name_utf8(key->ToString());
315 v8::TryCatch try_catch;
316 v8::Handle<v8::Value> child_v8 = val->Get(key);
318 if (try_catch.HasCaught()) {
319 LOG(ERROR) << "Getter for property " << *name_utf8
320 << " threw an exception.";
321 child_v8 = v8::Null();
324 scoped_ptr<base::Value> child(FromV8ValueImpl(child_v8, unique_map));
325 if (!child)
326 // JSON.stringify skips properties whose values don't serialize, for
327 // example undefined and functions. Emulate that behavior.
328 continue;
330 // Strip null if asked (and since undefined is turned into null, undefined
331 // too). The use case for supporting this is JSON-schema support,
332 // specifically for extensions, where "optional" JSON properties may be
333 // represented as null, yet due to buggy legacy code elsewhere isn't
334 // treated as such (potentially causing crashes). For example, the
335 // "tabs.create" function takes an object as its first argument with an
336 // optional "windowId" property.
338 // Given just
340 // tabs.create({})
342 // this will work as expected on code that only checks for the existence of
343 // a "windowId" property (such as that legacy code). However given
345 // tabs.create({windowId: null})
347 // there *is* a "windowId" property, but since it should be an int, code
348 // on the browser which doesn't additionally check for null will fail.
349 // We can avoid all bugs related to this by stripping null.
350 if (strip_null_from_objects_ && child->IsType(Value::TYPE_NULL))
351 continue;
353 result->SetWithoutPathExpansion(std::string(*name_utf8, name_utf8.length()),
354 child.release());
357 return result.release();
360 bool V8ValueConverterImpl::UpdateAndCheckUniqueness(
361 HashToHandleMap* map,
362 v8::Handle<v8::Object> handle) const {
363 typedef HashToHandleMap::const_iterator Iterator;
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;
376 map->insert(std::pair<int, v8::Handle<v8::Object> >(hash, handle));
377 return true;
380 } // namespace content