diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 21:28:10 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-20 21:28:10 +0000 |
commit | b1cf337a2722a4c24802b78258ea96e8e8d00276 (patch) | |
tree | c473a077154b97d23c781736aece0b1f847803af | |
parent | 25c171129de0c3da8fa5e45be90335f07c06a20a (diff) | |
download | chromium_src-b1cf337a2722a4c24802b78258ea96e8e8d00276.zip chromium_src-b1cf337a2722a4c24802b78258ea96e8e8d00276.tar.gz chromium_src-b1cf337a2722a4c24802b78258ea96e8e8d00276.tar.bz2 |
Add V8ValueConverter utility to convert between v8::Value and
base::Value.
BUG=
TEST=
Review URL: http://codereview.chromium.org/6882054
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82358 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/extension_messages_browsertest.cc | 9 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/renderer/extensions/chrome_app_bindings.cc | 21 | ||||
-rw-r--r-- | chrome/renderer/extensions/event_bindings.cc | 20 | ||||
-rw-r--r-- | chrome/renderer/extensions/event_bindings.h | 5 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_dispatcher.cc | 2 | ||||
-rw-r--r-- | chrome/renderer/extensions/extension_helper.cc | 4 | ||||
-rw-r--r-- | chrome/renderer/extensions/renderer_extension_bindings.cc | 63 | ||||
-rw-r--r-- | chrome/renderer/extensions/renderer_extension_bindings.h | 10 | ||||
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/renderer/render_view.cc | 40 | ||||
-rw-r--r-- | content/renderer/v8_value_converter.cc | 156 | ||||
-rw-r--r-- | content/renderer/v8_value_converter.h | 56 | ||||
-rw-r--r-- | content/renderer/v8_value_converter_browsertest.cc | 84 |
14 files changed, 341 insertions, 132 deletions
diff --git a/chrome/browser/extensions/extension_messages_browsertest.cc b/chrome/browser/extensions/extension_messages_browsertest.cc index c7a9b76..f4f7649 100644 --- a/chrome/browser/extensions/extension_messages_browsertest.cc +++ b/chrome/browser/extensions/extension_messages_browsertest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,7 +7,6 @@ #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/extensions/event_bindings.h" -#include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/test/render_view_test.h" #include "content/common/view_messages.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,14 +22,14 @@ static void DispatchOnConnect(int source_port_id, const std::string& name, // to itself". args.Set(3, Value::CreateStringValue(EventBindings::kTestingExtensionId)); args.Set(4, Value::CreateStringValue(EventBindings::kTestingExtensionId)); - RendererExtensionBindings::Invoke( + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnConnect, args, NULL, GURL()); } static void DispatchOnDisconnect(int source_port_id) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); - RendererExtensionBindings::Invoke( + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnDisconnect, args, NULL, GURL()); } @@ -38,7 +37,7 @@ static void DispatchOnMessage(const std::string& message, int source_port_id) { ListValue args; args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); - RendererExtensionBindings::Invoke( + EventBindings::CallFunction( "", ExtensionMessageService::kDispatchOnMessage, args, NULL, GURL()); } diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index d62ee68..395aaf3 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2407,6 +2407,7 @@ '../content/renderer/render_view_browsertest_mac.mm', '../content/renderer/render_widget_browsertest.cc', '../content/renderer/render_widget_browsertest.h', + '../content/renderer/v8_value_converter_browsertest.cc', ], 'conditions': [ ['chromeos==0', { diff --git a/chrome/renderer/extensions/chrome_app_bindings.cc b/chrome/renderer/extensions/chrome_app_bindings.cc index aafa672..5c760dd 100644 --- a/chrome/renderer/extensions/chrome_app_bindings.cc +++ b/chrome/renderer/extensions/chrome_app_bindings.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -16,6 +16,7 @@ #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_helper.h" #include "content/renderer/render_view.h" +#include "content/renderer/v8_value_converter.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "v8/include/v8.h" @@ -71,14 +72,8 @@ class ChromeAppExtensionWrapper : public v8::Extension { " native function GetDetailsForFrame();" " this.__defineGetter__('isInstalled', GetIsInstalled);" " this.install = Install;" - " this.getDetails = function() {" - " var json = GetDetails();" - " return json == null ? null : JSON.parse(json);" - " };" - " this.getDetailsForFrame = function(frame) {" - " var json = GetDetailsForFrame(frame);" - " return json == null ? null : JSON.parse(json);" - " };" + " this.getDetails = GetDetails;" + " this.getDetailsForFrame = GetDetailsForFrame;" " };" "}") { extension_dispatcher_ = extension_dispatcher; @@ -169,14 +164,12 @@ class ChromeAppExtensionWrapper : public v8::Extension { if (!extension) return v8::Null(); - std::string manifest_json; - const bool kPrettyPrint = false; scoped_ptr<DictionaryValue> manifest_copy( extension->manifest_value()->DeepCopy()); manifest_copy->SetString("id", extension->id()); - base::JSONWriter::Write(manifest_copy.get(), kPrettyPrint, &manifest_json); - - return v8::String::New(manifest_json.c_str(), manifest_json.size()); + V8ValueConverter converter; + return converter.ToV8Value(manifest_copy.get(), + frame->mainWorldScriptContext()); } static ExtensionDispatcher* extension_dispatcher_; diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 921a82e..01d0f55 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -4,6 +4,8 @@ #include "chrome/renderer/extensions/event_bindings.h" +#include <vector> + #include "base/basictypes.h" #include "base/lazy_instance.h" #include "base/message_loop.h" @@ -17,6 +19,7 @@ #include "chrome/renderer/extensions/js_only_v8_extensions.h" #include "content/renderer/render_thread.h" #include "content/renderer/render_view.h" +#include "content/renderer/v8_value_converter.h" #include "googleurl/src/gurl.h" #include "grit/renderer_resources.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" @@ -382,14 +385,17 @@ void EventBindings::HandleContextDestroyed(WebFrame* frame) { // static void EventBindings::CallFunction(const std::string& extension_id, const std::string& function_name, - int argc, v8::Handle<v8::Value>* argv, + const ListValue& arguments, RenderView* render_view, const GURL& event_url) { + v8::HandleScope handle_scope; + // We copy the context list, because calling into javascript may modify it // out from under us. We also guard against deleted contexts by checking if // they have been cleared first. ContextList contexts = GetContexts(); + V8ValueConverter converter; for (ContextList::iterator it = contexts.begin(); it != contexts.end(); ++it) { if (render_view && render_view != (*it)->render_view) @@ -404,8 +410,16 @@ void EventBindings::CallFunction(const std::string& extension_id, if (!HasSufficientPermissions(it->get(), event_url)) continue; - v8::Handle<v8::Value> retval = CallFunctionInContext((*it)->context, - function_name, argc, argv); + v8::Local<v8::Context> context(*((*it)->context)); + std::vector<v8::Handle<v8::Value> > v8_arguments; + for (size_t i = 0; i < arguments.GetSize(); ++i) { + Value* item = NULL; + CHECK(arguments.Get(i, &item)); + v8_arguments.push_back(converter.ToV8Value(item, context)); + } + + v8::Handle<v8::Value> retval = CallFunctionInContext( + context, function_name, v8_arguments.size(), &v8_arguments[0]); // In debug, the js will validate the event parameters and return a // string if a validation error has occured. // TODO(rafaelw): Consider only doing this check if function_name == diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index 893e3a6..59d26e6 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,6 +12,7 @@ class ExtensionDispatcher; class GURL; +class ListValue; class RenderThreadBase; class RenderView; @@ -45,7 +46,7 @@ class EventBindings { // v8::Undefined(). A DCHECK is setup to break if it is otherwise. static void CallFunction(const std::string& extension_id, const std::string& function_name, - int argc, v8::Handle<v8::Value>* argv, + const ListValue& arguments, RenderView* render_view, const GURL& event_url); }; diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc index 2d789ac..c1f2a6e 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -124,7 +124,7 @@ void ExtensionDispatcher::OnMessageInvoke(const std::string& extension_id, const std::string& function_name, const ListValue& args, const GURL& event_url) { - RendererExtensionBindings::Invoke( + EventBindings::CallFunction( extension_id, function_name, args, NULL, event_url); // Reset the idle handler each time there's any activity like event or message diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc index 691e7cb..bbe638b 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -12,9 +12,9 @@ #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" +#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_dispatcher.h" #include "chrome/renderer/extensions/extension_process_bindings.h" -#include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/renderer/extensions/user_script_idle_scheduler.h" #include "chrome/renderer/extensions/user_script_slave.h" #include "content/common/json_value_serializer.h" @@ -182,7 +182,7 @@ void ExtensionHelper::OnExtensionMessageInvoke(const std::string& extension_id, const std::string& function_name, const ListValue& args, const GURL& event_url) { - RendererExtensionBindings::Invoke( + EventBindings::CallFunction( extension_id, function_name, args, render_view(), event_url); } diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index d9507b5..3d9784a 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,7 +9,6 @@ #include "base/basictypes.h" #include "base/lazy_instance.h" -#include "base/values.h" #include "chrome/common/extensions/extension_message_bundle.h" #include "chrome/common/extensions/extension_messages.h" #include "chrome/common/url_constants.h" @@ -245,51 +244,6 @@ class ExtensionImpl : public ExtensionBase { } }; -// Convert a ListValue to a vector of V8 values. -static std::vector< v8::Handle<v8::Value> > ListValueToV8( - const ListValue& value) { - std::vector< v8::Handle<v8::Value> > v8_values; - - for (size_t i = 0; i < value.GetSize(); ++i) { - Value* elem = NULL; - value.Get(i, &elem); - switch (elem->GetType()) { - case Value::TYPE_NULL: - v8_values.push_back(v8::Null()); - break; - case Value::TYPE_BOOLEAN: { - bool val; - elem->GetAsBoolean(&val); - v8_values.push_back(v8::Boolean::New(val)); - break; - } - case Value::TYPE_INTEGER: { - int val; - elem->GetAsInteger(&val); - v8_values.push_back(v8::Integer::New(val)); - break; - } - case Value::TYPE_DOUBLE: { - double val; - elem->GetAsDouble(&val); - v8_values.push_back(v8::Number::New(val)); - break; - } - case Value::TYPE_STRING: { - std::string val; - elem->GetAsString(&val); - v8_values.push_back(v8::String::New(val.c_str())); - break; - } - default: - NOTREACHED() << "Unsupported Value type."; - break; - } - } - - return v8_values; -} - } // namespace const char* RendererExtensionBindings::kName = @@ -299,18 +253,3 @@ v8::Extension* RendererExtensionBindings::Get() { static v8::Extension* extension = new ExtensionImpl(); return extension; } - -void RendererExtensionBindings::Invoke(const std::string& extension_id, - const std::string& function_name, - const ListValue& args, - RenderView* renderview, - const GURL& event_url) { - v8::HandleScope handle_scope; - std::vector< v8::Handle<v8::Value> > argv = ListValueToV8(args); - EventBindings::CallFunction(extension_id, - function_name, - argv.size(), - &argv[0], - renderview, - event_url); -} diff --git a/chrome/renderer/extensions/renderer_extension_bindings.h b/chrome/renderer/extensions/renderer_extension_bindings.h index 194b0c8..76a1a9c 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.h +++ b/chrome/renderer/extensions/renderer_extension_bindings.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,7 +11,6 @@ #include <string> class GURL; -class ListValue; class RenderView; // This class adds extension-related javascript bindings to a renderer. It is @@ -23,13 +22,6 @@ class RendererExtensionBindings { // Creates an instance of the extension. static v8::Extension* Get(); - - // Call the given javascript function with the specified arguments. - static void Invoke(const std::string& extension_id, - const std::string& function_name, - const ListValue& args, - RenderView* renderview, - const GURL& event_url); }; #endif // CHROME_RENDERER_EXTENSIONS_RENDERER_EXTENSION_BINDINGS_H_ diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 53f1b97..f711060 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -134,6 +134,8 @@ 'renderer/transport_texture_host.h', 'renderer/transport_texture_service.cc', 'renderer/transport_texture_service.h', + 'renderer/v8_value_converter.cc', + 'renderer/v8_value_converter.h', 'renderer/video_decode_accelerator_host.cc', 'renderer/video_decode_accelerator_host.h', 'renderer/webgraphicscontext3d_command_buffer_impl.cc', diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc index d7da0d0..f324b72 100644 --- a/content/renderer/render_view.cc +++ b/content/renderer/render_view.cc @@ -12,6 +12,7 @@ #include "base/callback.h" #include "base/command_line.h" #include "base/compiler_specific.h" +#include "base/json/json_writer.h" #include "base/lazy_instance.h" #include "base/metrics/histogram.h" #include "base/path_service.h" @@ -58,6 +59,7 @@ #include "content/renderer/renderer_webapplicationcachehost_impl.h" #include "content/renderer/renderer_webstoragenamespace_impl.h" #include "content/renderer/speech_input_dispatcher.h" +#include "content/renderer/v8_value_converter.h" #include "content/renderer/web_ui_bindings.h" #include "content/renderer/webgraphicscontext3d_command_buffer_impl.h" #include "content/renderer/webplugin_delegate_proxy.h" @@ -297,39 +299,6 @@ static bool WebAccessibilityNotificationToViewHostMsg( return true; } -// Conversion for the incoming value. The map isn't perfect; v8 has Uint32, -// and int64 which don't fit as Value::TYPE_INTEGER, so we let them fall into -// being TYPE_DOUBLEs. Dates are converted to a string (which can then be -// parsed into a base::Time), as are regexps. Arrays are converted into lists, -// recursively. We don't deal with binary objects or functions - they become -// null values. -static Value* ConvertV8Value(const v8::Handle<v8::Value>& v8value) { - if (v8value->IsBoolean()) { - return Value::CreateBooleanValue(v8value->BooleanValue()); - } else if (v8value->IsInt32()) { - return Value::CreateIntegerValue(v8value->Int32Value()); - } else if (v8value->IsNumber()) { - return Value::CreateDoubleValue(v8value->NumberValue()); - } else if (v8value->IsString()) { - return Value::CreateStringValue(*v8::String::Utf8Value(v8value)); - } else if (v8value->IsDate()) { - v8::Date* date = v8::Date::Cast(*v8value); - return Value::CreateDoubleValue(date->NumberValue() / 1000.0); - } else if (v8value->IsRegExp()) { - return Value::CreateStringValue( - *v8::String::Utf8Value(v8value->ToString())); - } else if (v8value->IsArray()) { - v8::Array* array = v8::Array::Cast(*v8value); - uint32_t length = array->Length(); - scoped_ptr<ListValue> list(new ListValue); - for (uint32_t i = 0 ; i < length ; ++i) { - list->Set(i, ConvertV8Value(array->Get(i))); - } - return list.release(); - } - return Value::CreateNullValue(); -} - /////////////////////////////////////////////////////////////////////////////// int32 RenderView::next_page_id_ = 1; @@ -3370,7 +3339,10 @@ void RenderView::EvaluateScript(const string16& frame_xpath, v8::HandleScope handle_scope; v8::Local<v8::Context> context = web_frame->mainWorldScriptContext(); v8::Context::Scope context_scope(context); - list.Set(0, ConvertV8Value(result)); + V8ValueConverter converter; + converter.set_allow_date(true); + converter.set_allow_regexp(true); + list.Set(0, converter.FromV8Value(result, context)); } else { list.Set(0, Value::CreateNullValue()); } diff --git a/content/renderer/v8_value_converter.cc b/content/renderer/v8_value_converter.cc new file mode 100644 index 0000000..9b7c2fd --- /dev/null +++ b/content/renderer/v8_value_converter.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/v8_value_converter.h" + +#include <string> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "v8/include/v8.h" + +V8ValueConverter::V8ValueConverter() { +} + +v8::Handle<v8::Value> V8ValueConverter::ToV8Value( + Value* value, v8::Handle<v8::Context> context) { + v8::Context::Scope context_scope(context); + return ToV8ValueImpl(value); +} + +Value* V8ValueConverter::FromV8Value(v8::Handle<v8::Value> val, + v8::Handle<v8::Context> context) { + v8::Context::Scope context_scope(context); + return FromV8ValueImpl(val); +} + +v8::Handle<v8::Value> V8ValueConverter::ToV8ValueImpl(Value* value) { + CHECK(value); + switch (value->GetType()) { + case Value::TYPE_NULL: + return v8::Null(); + + case Value::TYPE_BOOLEAN: { + bool val = false; + CHECK(value->GetAsBoolean(&val)); + return v8::Boolean::New(val); + } + + case Value::TYPE_INTEGER: { + int val = 0; + CHECK(value->GetAsInteger(&val)); + return v8::Integer::New(val); + } + + case Value::TYPE_DOUBLE: { + double val = 0.0; + CHECK(value->GetAsDouble(&val)); + return v8::Number::New(val); + } + + case Value::TYPE_STRING: { + std::string val; + CHECK(value->GetAsString(&val)); + return v8::String::New(val.c_str(), val.length()); + } + + case Value::TYPE_LIST: + return ToV8Array(static_cast<ListValue*>(value)); + + case Value::TYPE_DICTIONARY: + return ToV8Object(static_cast<DictionaryValue*>(value)); + + default: + NOTREACHED() << "Unexpected value type: " << value->GetType(); + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> V8ValueConverter::ToV8Array(ListValue* val) { + v8::Handle<v8::Array> result(v8::Array::New(val->GetSize())); + + for (size_t i = 0; i < val->GetSize(); ++i) { + Value* child = NULL; + CHECK(val->Get(i, &child)); + result->Set(static_cast<uint32>(i), ToV8ValueImpl(child)); + } + + return result; +} + +v8::Handle<v8::Value> V8ValueConverter::ToV8Object(DictionaryValue* val) { + v8::Handle<v8::Object> result(v8::Object::New()); + + for (DictionaryValue::key_iterator iter = val->begin_keys(); + iter != val->end_keys(); ++iter) { + Value* child = NULL; + CHECK(val->GetWithoutPathExpansion(*iter, &child)); + const std::string& key = *iter; + result->Set(v8::String::New(key.c_str(), key.length()), + ToV8ValueImpl(child)); + } + + return result; +} + +Value* V8ValueConverter::FromV8ValueImpl(v8::Handle<v8::Value> val) { + if (val->IsNull()) + return Value::CreateNullValue(); + + if (val->IsBoolean()) + return Value::CreateBooleanValue(val->ToBoolean()->Value()); + + if (val->IsInt32()) + return Value::CreateIntegerValue(val->ToInt32()->Value()); + + if (val->IsNumber()) + return Value::CreateDoubleValue(val->ToNumber()->Value()); + + if (val->IsString()) { + v8::String::Utf8Value utf8(val->ToString()); + return Value::CreateStringValue(std::string(*utf8, utf8.length())); + } + + if (allow_date_ && val->IsDate()) { + v8::Date* date = v8::Date::Cast(*val); + return Value::CreateDoubleValue(date->NumberValue() / 1000.0); + } + + if (allow_regexp_ && val->IsRegExp()) { + return Value::CreateStringValue( + *v8::String::Utf8Value(val->ToString())); + } + + // v8::Value doesn't have a ToArray() method for some reason. + if (val->IsArray()) + return FromV8Array(v8::Handle<v8::Array>::Cast(val)); + + if (val->IsObject()) + return FromV8Object(val->ToObject()); + + NOTREACHED() << "Unexpected v8::Value type."; + return Value::CreateNullValue(); +} + +ListValue* V8ValueConverter::FromV8Array(v8::Handle<v8::Array> val) { + ListValue* result = new ListValue(); + for (uint32 i = 0; i < val->Length(); ++i) { + result->Append(FromV8ValueImpl(val->Get(i))); + } + return result; +} + +DictionaryValue* V8ValueConverter::FromV8Object(v8::Handle<v8::Object> val) { + DictionaryValue* result = new DictionaryValue(); + v8::Handle<v8::Array> property_names(val->GetPropertyNames()); + for (uint32 i = 0; i < property_names->Length(); ++i) { + v8::Handle<v8::String> name( + v8::Handle<v8::String>::Cast(property_names->Get(i))); + v8::String::Utf8Value name_utf8(name->ToString()); + result->Set(std::string(*name_utf8, name_utf8.length()), + FromV8ValueImpl(val->Get(name))); + } + return result; +} diff --git a/content/renderer/v8_value_converter.h b/content/renderer/v8_value_converter.h new file mode 100644 index 0000000..4bfce75 --- /dev/null +++ b/content/renderer/v8_value_converter.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_V8_VALUE_CONVERTER_H_ +#define CHROME_RENDERER_V8_VALUE_CONVERTER_H_ + +#include "v8/include/v8.h" + +class Value; +class ListValue; +class DictionaryValue; + +// Converts between v8::Value (JavaScript values in the v8 heap) and Chrome's +// values (from base/values.h). Lists and dictionaries are converted +// recursively. +class V8ValueConverter { + public: + V8ValueConverter(); + + bool allow_date() const { return allow_date_; } + void set_allow_date(bool val) { allow_date_ = val; } + + bool allow_regexp() const { return allow_regexp_; } + void set_allow_regexp(bool val) { allow_regexp_ = val; } + + // Converts Value to v8::Value. Only the JSON types (null, boolean, string, + // number, array, and object) are supported. Other types DCHECK and are + // replaced with null. + v8::Handle<v8::Value> ToV8Value(Value* value, + v8::Handle<v8::Context> context); + + // Converts v8::Value to Value. Only the JSON types (null, boolean, string, + // number, array, and object) are supported by default, but additional types + // can be supported with setters. If a number fits in int32, we will convert + // to Value::TYPE_INTEGER instead of Value::TYPE_DOUBLE. + Value* FromV8Value(v8::Handle<v8::Value> value, + v8::Handle<v8::Context> context); + + private: + v8::Handle<v8::Value> ToV8ValueImpl(Value* value); + v8::Handle<v8::Value> ToV8Array(ListValue* list); + v8::Handle<v8::Value> ToV8Object(DictionaryValue* dictionary); + + Value* FromV8ValueImpl(v8::Handle<v8::Value> value); + ListValue* FromV8Array(v8::Handle<v8::Array> array); + DictionaryValue* FromV8Object(v8::Handle<v8::Object> object); + + // If true, we will convert Date JavaScript objects to doubles. + bool allow_date_; + + // If true, we will convet RegExp JavaScript objects to string. + bool allow_regexp_; +}; + +#endif // CHROME_RENDERER_V8_VALUE_CONVERTER_H_ diff --git a/content/renderer/v8_value_converter_browsertest.cc b/content/renderer/v8_value_converter_browsertest.cc new file mode 100644 index 0000000..8c75279 --- /dev/null +++ b/content/renderer/v8_value_converter_browsertest.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cmath> + +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "content/renderer/v8_value_converter.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +class V8ValueConverterTest : public testing::Test { + protected: + virtual void SetUp() { + v8::HandleScope handle_scope; + v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); + context_ = v8::Context::New(NULL, global); + } + + virtual void TearDown() { + context_.Dispose(); + } + + // Context for the JavaScript in the test. + v8::Persistent<v8::Context> context_; +}; + +TEST_F(V8ValueConverterTest, BasicRoundTrip) { + DictionaryValue original_root; + original_root.Set("null", Value::CreateNullValue()); + original_root.Set("true", Value::CreateBooleanValue(true)); + original_root.Set("false", Value::CreateBooleanValue(false)); + original_root.Set("positive-int", Value::CreateIntegerValue(42)); + original_root.Set("negative-int", Value::CreateIntegerValue(-42)); + original_root.Set("zero", Value::CreateIntegerValue(0)); + original_root.Set("double", Value::CreateDoubleValue(88.8)); + original_root.Set("big-integral-double", + Value::CreateDoubleValue(pow(2.0, 53))); + original_root.Set("string", Value::CreateStringValue("foobar")); + original_root.Set("empty-string", Value::CreateStringValue("")); + + DictionaryValue* original_sub1 = new DictionaryValue(); + original_sub1->Set("foo", Value::CreateStringValue("bar")); + original_sub1->Set("hot", Value::CreateStringValue("dog")); + original_root.Set("dictionary", original_sub1); + original_root.Set("empty-dictionary", new DictionaryValue()); + + ListValue* original_sub2 = new ListValue(); + original_sub2->Append(Value::CreateStringValue("monkey")); + original_sub2->Append(Value::CreateStringValue("balls")); + original_root.Set("list", original_sub2); + original_root.Set("empty-list", new ListValue()); + + v8::Context::Scope context_scope(context_); + v8::HandleScope handle_scope; + + V8ValueConverter converter; + v8::Handle<v8::Object> v8_object( + v8::Handle<v8::Object>::Cast( + converter.ToV8Value(&original_root, context_))); + ASSERT_FALSE(v8_object.IsEmpty()); + + EXPECT_EQ(original_root.size(), v8_object->GetPropertyNames()->Length()); + EXPECT_TRUE(v8_object->Get(v8::String::New("null"))->IsNull()); + EXPECT_TRUE(v8_object->Get(v8::String::New("true"))->IsTrue()); + EXPECT_TRUE(v8_object->Get(v8::String::New("false"))->IsFalse()); + EXPECT_TRUE(v8_object->Get(v8::String::New("positive-int"))->IsInt32()); + EXPECT_TRUE(v8_object->Get(v8::String::New("negative-int"))->IsInt32()); + EXPECT_TRUE(v8_object->Get(v8::String::New("zero"))->IsInt32()); + EXPECT_TRUE(v8_object->Get(v8::String::New("double"))->IsNumber()); + EXPECT_TRUE( + v8_object->Get(v8::String::New("big-integral-double"))->IsNumber()); + EXPECT_TRUE(v8_object->Get(v8::String::New("string"))->IsString()); + EXPECT_TRUE(v8_object->Get(v8::String::New("empty-string"))->IsString()); + EXPECT_TRUE(v8_object->Get(v8::String::New("dictionary"))->IsObject()); + EXPECT_TRUE(v8_object->Get(v8::String::New("empty-dictionary"))->IsObject()); + EXPECT_TRUE(v8_object->Get(v8::String::New("list"))->IsArray()); + EXPECT_TRUE(v8_object->Get(v8::String::New("empty-list"))->IsArray()); + + scoped_ptr<Value> new_root(converter.FromV8Value(v8_object, context_)); + EXPECT_NE(&original_root, new_root.get()); + EXPECT_TRUE(original_root.Equals(new_root.get())); +} |