summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormnaganov@chromium.org <mnaganov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-27 18:10:06 +0000
committermnaganov@chromium.org <mnaganov@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-27 18:10:06 +0000
commitb8b6708119105a8c2e14f6de22d02cf320f6b76d (patch)
tree944931b848ba371ae953c7c5fc2657f0e7aae423
parent9c50a6cb09ac89e3dfe70dd8f8468b9cea5eab7d (diff)
downloadchromium_src-b8b6708119105a8c2e14f6de22d02cf320f6b76d.zip
chromium_src-b8b6708119105a8c2e14f6de22d02cf320f6b76d.tar.gz
chromium_src-b8b6708119105a8c2e14f6de22d02cf320f6b76d.tar.bz2
[Android] Implement renderer side of Gin Java Bridge
This change adds GinJavaBridgeObject and GinJavaBridgeDispatcher classes. GinJavaBridgeDispatcher is a per-RenderFrame manager of injected objects. It interacts with the browser side. GinJavaBridgeObject is a Gin-based wrapper injected into page's context that exposes methods of the corresponding Java object and receives method invocation requests from JavaScript. BUG=355644 R=jochen@chromium.org, thakis@chromium.org Review URL: https://codereview.chromium.org/259033002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272997 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/id_map.h4
-rw-r--r--content/content_renderer.gypi12
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/renderer/v8_value_converter.h3
-rw-r--r--content/renderer/java/gin_java_bridge_dispatcher.cc125
-rw-r--r--content/renderer/java/gin_java_bridge_dispatcher.h72
-rw-r--r--content/renderer/java/gin_java_bridge_object.cc165
-rw-r--r--content/renderer/java/gin_java_bridge_object.h70
-rw-r--r--content/renderer/java/gin_java_bridge_value_converter.cc163
-rw-r--r--content/renderer/java/gin_java_bridge_value_converter.h46
-rw-r--r--content/renderer/java/gin_java_bridge_value_converter_unittest.cc138
-rw-r--r--content/renderer/v8_value_converter_impl.cc10
-rw-r--r--content/renderer/v8_value_converter_impl.h3
-rw-r--r--content/renderer/v8_value_converter_impl_unittest.cc6
14 files changed, 809 insertions, 9 deletions
diff --git a/base/id_map.h b/base/id_map.h
index 27098d2..9cbc1f8 100644
--- a/base/id_map.h
+++ b/base/id_map.h
@@ -32,8 +32,10 @@ enum IDMapOwnershipSemantics {
// ownership semantics are set to own because pointers will leak.
template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer>
class IDMap : public base::NonThreadSafe {
- private:
+ public:
typedef int32 KeyType;
+
+ private:
typedef base::hash_map<KeyType, T*> HashTable;
public:
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index cc549dd..370a94a 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -191,6 +191,12 @@
'renderer/input/input_handler_wrapper.h',
'renderer/internal_document_state_data.cc',
'renderer/internal_document_state_data.h',
+ 'renderer/java/gin_java_bridge_dispatcher.cc',
+ 'renderer/java/gin_java_bridge_dispatcher.h',
+ 'renderer/java/gin_java_bridge_object.cc',
+ 'renderer/java/gin_java_bridge_object.h',
+ 'renderer/java/gin_java_bridge_value_converter.cc',
+ 'renderer/java/gin_java_bridge_value_converter.h',
'renderer/java/java_bridge_channel.cc',
'renderer/java/java_bridge_channel.h',
'renderer/java/java_bridge_dispatcher.cc',
@@ -607,6 +613,12 @@
],
}, {
'sources!': [
+ 'renderer/java/gin_java_bridge_dispatcher.cc',
+ 'renderer/java/gin_java_bridge_dispatcher.h',
+ 'renderer/java/gin_java_bridge_object.cc',
+ 'renderer/java/gin_java_bridge_object.h',
+ 'renderer/java/gin_java_bridge_value_converter.cc',
+ 'renderer/java/gin_java_bridge_value_converter.h',
'renderer/java/java_bridge_channel.cc',
'renderer/java/java_bridge_channel.h',
'renderer/java/java_bridge_dispatcher.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 1db6c19..b52985a 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -875,6 +875,7 @@
['OS == "android"', {
'sources': [
'browser/renderer_host/java/jni_helper_unittest.cc',
+ 'renderer/java/gin_java_bridge_value_converter_unittest.cc',
],
'sources!': [
'browser/geolocation/network_location_provider_unittest.cc',
diff --git a/content/public/renderer/v8_value_converter.h b/content/public/renderer/v8_value_converter.h
index 725980e..f3fd126 100644
--- a/content/public/renderer/v8_value_converter.h
+++ b/content/public/renderer/v8_value_converter.h
@@ -55,7 +55,8 @@ class CONTENT_EXPORT V8ValueConverter {
// behavior. v8::Object is passed as ArrayBuffer and ArrayBufferView
// classes are siblings.
virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
- base::Value** out) const;
+ base::Value** out,
+ v8::Isolate* isolate) const;
// If false is returned, V8ValueConverter proceeds with the default
// behavior. This allows to intercept "non-finite" values and do something
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.cc b/content/renderer/java/gin_java_bridge_dispatcher.cc
new file mode 100644
index 0000000..bacc23c
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_dispatcher.cc
@@ -0,0 +1,125 @@
+// Copyright 2014 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/java/gin_java_bridge_dispatcher.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/common/gin_java_bridge_messages.h"
+#include "content/public/renderer/render_frame.h"
+#include "content/renderer/java/gin_java_bridge_object.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebView.h"
+
+namespace content {
+
+GinJavaBridgeDispatcher::GinJavaBridgeDispatcher(RenderFrame* render_frame)
+ : RenderFrameObserver(render_frame) {
+}
+
+GinJavaBridgeDispatcher::~GinJavaBridgeDispatcher() {
+}
+
+bool GinJavaBridgeDispatcher::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(GinJavaBridgeDispatcher, msg)
+ IPC_MESSAGE_HANDLER(GinJavaBridgeMsg_AddNamedObject, OnAddNamedObject)
+ IPC_MESSAGE_HANDLER(GinJavaBridgeMsg_RemoveNamedObject, OnRemoveNamedObject)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void GinJavaBridgeDispatcher::DidClearWindowObject() {
+ for (NamedObjectMap::const_iterator iter = named_objects_.begin();
+ iter != named_objects_.end(); ++iter) {
+ // Always create a new GinJavaBridgeObject, so we don't pull any of the V8
+ // wrapper's custom properties into the context of the page we have
+ // navigated to. The old GinJavaBridgeObject will be automatically
+ // deleted after its wrapper will be collected.
+ // On the browser side, we ignore wrapper deletion events for named objects,
+ // as they are only removed upon embedder's request (RemoveNamedObject).
+ if (objects_.Lookup(iter->second))
+ objects_.Remove(iter->second);
+ GinJavaBridgeObject* object = GinJavaBridgeObject::InjectNamed(
+ render_frame()->GetWebFrame(), AsWeakPtr(), iter->first, iter->second);
+ if (object) {
+ objects_.AddWithID(object, iter->second);
+ } else {
+ // Inform the host about wrapper creation failure.
+ render_frame()->Send(new GinJavaBridgeHostMsg_ObjectWrapperDeleted(
+ routing_id(), iter->second));
+ }
+ }
+}
+
+void GinJavaBridgeDispatcher::OnAddNamedObject(
+ const std::string& name,
+ ObjectID object_id) {
+ // Added objects only become available after page reload, so here they
+ // are only added into the internal map.
+ named_objects_.insert(std::make_pair(name, object_id));
+}
+
+void GinJavaBridgeDispatcher::OnRemoveNamedObject(const std::string& name) {
+ // Removal becomes in effect on next reload. We simply removing the entry
+ // from the map here.
+ NamedObjectMap::iterator iter = named_objects_.find(name);
+ DCHECK(iter != named_objects_.end());
+ named_objects_.erase(iter);
+}
+
+void GinJavaBridgeDispatcher::GetJavaMethods(
+ ObjectID object_id,
+ std::set<std::string>* methods) {
+ render_frame()->Send(new GinJavaBridgeHostMsg_GetMethods(
+ routing_id(), object_id, methods));
+}
+
+bool GinJavaBridgeDispatcher::HasJavaMethod(ObjectID object_id,
+ const std::string& method_name) {
+ bool result;
+ render_frame()->Send(new GinJavaBridgeHostMsg_HasMethod(
+ routing_id(), object_id, method_name, &result));
+ return result;
+}
+
+scoped_ptr<base::Value> GinJavaBridgeDispatcher::InvokeJavaMethod(
+ ObjectID object_id,
+ const std::string& method_name,
+ const base::ListValue& arguments) {
+ base::ListValue result_wrapper;
+ render_frame()->Send(
+ new GinJavaBridgeHostMsg_InvokeMethod(routing_id(),
+ object_id,
+ method_name,
+ arguments,
+ &result_wrapper));
+ base::Value* result;
+ if (result_wrapper.Get(0, &result)) {
+ return scoped_ptr<base::Value>(result->DeepCopy());
+ } else {
+ return scoped_ptr<base::Value>();
+ }
+}
+
+GinJavaBridgeObject* GinJavaBridgeDispatcher::GetObject(ObjectID object_id) {
+ GinJavaBridgeObject* result = objects_.Lookup(object_id);
+ if (!result) {
+ result = GinJavaBridgeObject::InjectAnonymous(AsWeakPtr(), object_id);
+ if (result)
+ objects_.AddWithID(result, object_id);
+ }
+ return result;
+}
+
+void GinJavaBridgeDispatcher::OnGinJavaBridgeObjectDeleted(ObjectID object_id) {
+ if (!objects_.Lookup(object_id))
+ return;
+ objects_.Remove(object_id);
+ render_frame()->Send(
+ new GinJavaBridgeHostMsg_ObjectWrapperDeleted(routing_id(), object_id));
+}
+
+} // namespace content
diff --git a/content/renderer/java/gin_java_bridge_dispatcher.h b/content/renderer/java/gin_java_bridge_dispatcher.h
new file mode 100644
index 0000000..c5348b97
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_dispatcher.h
@@ -0,0 +1,72 @@
+// Copyright 2014 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 CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_H_
+#define CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_H_
+
+#include <map>
+#include <set>
+
+#include "base/id_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/renderer/render_frame_observer.h"
+
+namespace blink {
+class WebFrame;
+}
+
+namespace content {
+
+class GinJavaBridgeObject;
+
+// This class handles injecting Java objects into the main frame of a
+// RenderView. The 'add' and 'remove' messages received from the browser
+// process modify the entries in a map of 'pending' objects. These objects are
+// bound to the window object of the main frame when that window object is next
+// cleared. These objects remain bound until the window object is cleared
+// again.
+class GinJavaBridgeDispatcher
+ : public base::SupportsWeakPtr<GinJavaBridgeDispatcher>,
+ public RenderFrameObserver {
+ public:
+ // GinJavaBridgeObjects are managed by gin. An object gets destroyed
+ // when it is no more referenced from JS. As GinJavaBridgeObject reports
+ // deletion of self to GinJavaBridgeDispatcher, we would not have stale
+ // pointers here.
+ typedef IDMap<GinJavaBridgeObject, IDMapExternalPointer> ObjectMap;
+ typedef ObjectMap::KeyType ObjectID;
+
+ explicit GinJavaBridgeDispatcher(RenderFrame* render_frame);
+ virtual ~GinJavaBridgeDispatcher();
+
+ // RenderFrameObserver override:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void DidClearWindowObject() OVERRIDE;
+
+ void GetJavaMethods(ObjectID object_id, std::set<std::string>* methods);
+ bool HasJavaMethod(ObjectID object_id, const std::string& method_name);
+ scoped_ptr<base::Value> InvokeJavaMethod(ObjectID object_id,
+ const std::string& method_name,
+ const base::ListValue& arguments);
+ GinJavaBridgeObject* GetObject(ObjectID object_id);
+ void OnGinJavaBridgeObjectDeleted(ObjectID object_id);
+
+ private:
+ void OnAddNamedObject(const std::string& name,
+ ObjectID object_id);
+ void OnRemoveNamedObject(const std::string& name);
+ void OnSetAllowObjectContentsInspection(bool allow);
+
+ typedef std::map<std::string, ObjectID> NamedObjectMap;
+ NamedObjectMap named_objects_;
+ ObjectMap objects_;
+
+ DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeDispatcher);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_DISPATCHER_H_
diff --git a/content/renderer/java/gin_java_bridge_object.cc b/content/renderer/java/gin_java_bridge_object.cc
new file mode 100644
index 0000000..a153f2a
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_object.cc
@@ -0,0 +1,165 @@
+// Copyright 2014 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/java/gin_java_bridge_object.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/public/renderer/v8_value_converter.h"
+#include "content/renderer/java/gin_java_bridge_value_converter.h"
+#include "gin/function_template.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+
+namespace content {
+
+namespace {
+
+const char kMethodInvocationErrorMessage[] =
+ "Java bridge method invocation error";
+
+} // namespace
+
+
+// static
+GinJavaBridgeObject* GinJavaBridgeObject::InjectNamed(
+ blink::WebFrame* frame,
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ const std::string& object_name,
+ GinJavaBridgeDispatcher::ObjectID object_id) {
+ v8::Isolate* isolate = blink::mainThreadIsolate();
+ v8::HandleScope handle_scope(isolate);
+ v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
+ if (context.IsEmpty())
+ return NULL;
+
+ GinJavaBridgeObject* object =
+ new GinJavaBridgeObject(isolate, dispatcher, object_id);
+
+ v8::Context::Scope context_scope(context);
+ v8::Handle<v8::Object> global = context->Global();
+ gin::Handle<GinJavaBridgeObject> controller =
+ gin::CreateHandle(isolate, object);
+ // WrappableBase instance deletes itself in case of a wrapper
+ // creation failure, thus there is no need to delete |object|.
+ if (controller.IsEmpty())
+ return NULL;
+
+ global->Set(gin::StringToV8(isolate, object_name), controller.ToV8());
+ return object;
+}
+
+// static
+GinJavaBridgeObject* GinJavaBridgeObject::InjectAnonymous(
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ GinJavaBridgeDispatcher::ObjectID object_id) {
+ return new GinJavaBridgeObject(
+ blink::mainThreadIsolate(), dispatcher, object_id);
+}
+
+GinJavaBridgeObject::GinJavaBridgeObject(
+ v8::Isolate* isolate,
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ GinJavaBridgeDispatcher::ObjectID object_id)
+ : gin::NamedPropertyInterceptor(isolate, this),
+ dispatcher_(dispatcher),
+ object_id_(object_id),
+ converter_(new GinJavaBridgeValueConverter()) {
+}
+
+GinJavaBridgeObject::~GinJavaBridgeObject() {
+ if (dispatcher_)
+ dispatcher_->OnGinJavaBridgeObjectDeleted(object_id_);
+}
+
+gin::ObjectTemplateBuilder GinJavaBridgeObject::GetObjectTemplateBuilder(
+ v8::Isolate* isolate) {
+ return gin::Wrappable<GinJavaBridgeObject>::GetObjectTemplateBuilder(isolate)
+ .AddNamedPropertyInterceptor();
+}
+
+v8::Local<v8::Value> GinJavaBridgeObject::GetNamedProperty(
+ v8::Isolate* isolate,
+ const std::string& property) {
+ if (dispatcher_ && dispatcher_->HasJavaMethod(object_id_, property)) {
+ return gin::CreateFunctionTemplate(
+ isolate,
+ base::Bind(&GinJavaBridgeObject::InvokeMethod,
+ base::Unretained(this),
+ property))->GetFunction();
+ } else {
+ return v8::Local<v8::Value>();
+ }
+}
+
+std::vector<std::string> GinJavaBridgeObject::EnumerateNamedProperties(
+ v8::Isolate* isolate) {
+ std::set<std::string> method_names;
+ if (dispatcher_)
+ dispatcher_->GetJavaMethods(object_id_, &method_names);
+ return std::vector<std::string> (method_names.begin(), method_names.end());
+}
+
+v8::Handle<v8::Value> GinJavaBridgeObject::InvokeMethod(
+ const std::string& name,
+ gin::Arguments* args) {
+ if (!dispatcher_) {
+ args->isolate()->ThrowException(v8::Exception::Error(gin::StringToV8(
+ args->isolate(), kMethodInvocationErrorMessage)));
+ return v8::Undefined(args->isolate());
+ }
+
+ base::ListValue arguments;
+ {
+ v8::HandleScope handle_scope(args->isolate());
+ v8::Handle<v8::Context> context = args->isolate()->GetCurrentContext();
+ v8::Handle<v8::Value> val;
+ while (args->GetNext(&val)) {
+ scoped_ptr<base::Value> arg(converter_->FromV8Value(val, context));
+ if (arg.get()) {
+ arguments.Append(arg.release());
+ } else {
+ arguments.Append(base::Value::CreateNullValue());
+ }
+ }
+ }
+
+ scoped_ptr<base::Value> result =
+ dispatcher_->InvokeJavaMethod(object_id_, name, arguments);
+ if (!result.get()) {
+ args->isolate()->ThrowException(v8::Exception::Error(gin::StringToV8(
+ args->isolate(), kMethodInvocationErrorMessage)));
+ return v8::Undefined(args->isolate());
+ }
+ if (!result->IsType(base::Value::TYPE_BINARY)) {
+ return converter_->ToV8Value(result.get(),
+ args->isolate()->GetCurrentContext());
+ }
+
+ scoped_ptr<const GinJavaBridgeValue> gin_value =
+ GinJavaBridgeValue::FromValue(result.get());
+ if (gin_value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID)) {
+ GinJavaBridgeObject* result = NULL;
+ GinJavaBridgeDispatcher::ObjectID object_id;
+ if (gin_value->GetAsObjectID(&object_id)) {
+ result = dispatcher_->GetObject(object_id);
+ }
+ if (result) {
+ gin::Handle<GinJavaBridgeObject> controller =
+ gin::CreateHandle(args->isolate(), result);
+ if (controller.IsEmpty())
+ return v8::Undefined(args->isolate());
+ return controller.ToV8();
+ }
+ } else if (gin_value->IsType(GinJavaBridgeValue::TYPE_NONFINITE)) {
+ float float_value;
+ gin_value->GetAsNonFinite(&float_value);
+ return v8::Number::New(args->isolate(), float_value);
+ }
+ return v8::Undefined(args->isolate());
+}
+
+gin::WrapperInfo GinJavaBridgeObject::kWrapperInfo = {gin::kEmbedderNativeGin};
+
+} // namespace content
diff --git a/content/renderer/java/gin_java_bridge_object.h b/content/renderer/java/gin_java_bridge_object.h
new file mode 100644
index 0000000..97b74c5
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_object.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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 CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_OBJECT_H_
+#define CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_OBJECT_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/renderer/java/gin_java_bridge_dispatcher.h"
+#include "gin/handle.h"
+#include "gin/interceptor.h"
+#include "gin/object_template_builder.h"
+#include "gin/wrappable.h"
+
+namespace blink {
+class WebFrame;
+}
+
+namespace content {
+
+class GinJavaBridgeValueConverter;
+
+class GinJavaBridgeObject : public gin::Wrappable<GinJavaBridgeObject>,
+ public gin::NamedPropertyInterceptor {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ GinJavaBridgeDispatcher::ObjectID object_id() const { return object_id_; }
+
+ // gin::Wrappable.
+ virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+ v8::Isolate* isolate) OVERRIDE;
+
+ // gin::NamedPropertyInterceptor
+ virtual v8::Local<v8::Value> GetNamedProperty(
+ v8::Isolate* isolate, const std::string& property) OVERRIDE;
+ virtual std::vector<std::string> EnumerateNamedProperties(
+ v8::Isolate* isolate) OVERRIDE;
+
+ static GinJavaBridgeObject* InjectNamed(
+ blink::WebFrame* frame,
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ const std::string& object_name,
+ GinJavaBridgeDispatcher::ObjectID object_id);
+ static GinJavaBridgeObject* InjectAnonymous(
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ GinJavaBridgeDispatcher::ObjectID object_id);
+
+ private:
+ GinJavaBridgeObject(v8::Isolate* isolate,
+ const base::WeakPtr<GinJavaBridgeDispatcher>& dispatcher,
+ GinJavaBridgeDispatcher::ObjectID object_id);
+ virtual ~GinJavaBridgeObject();
+
+ v8::Handle<v8::Value> InvokeMethod(const std::string& name,
+ gin::Arguments* args);
+
+ base::WeakPtr<GinJavaBridgeDispatcher> dispatcher_;
+ GinJavaBridgeDispatcher::ObjectID object_id_;
+ scoped_ptr<GinJavaBridgeValueConverter> converter_;
+
+ DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeObject);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_OBJECT_H_
diff --git a/content/renderer/java/gin_java_bridge_value_converter.cc b/content/renderer/java/gin_java_bridge_value_converter.cc
new file mode 100644
index 0000000..ad8fee9
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_value_converter.cc
@@ -0,0 +1,163 @@
+// Copyright 2014 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/java/gin_java_bridge_value_converter.h"
+
+#include "base/float_util.h"
+#include "base/values.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/renderer/java/gin_java_bridge_object.h"
+#include "gin/array_buffer.h"
+
+namespace content {
+
+GinJavaBridgeValueConverter::GinJavaBridgeValueConverter()
+ : converter_(V8ValueConverter::create()) {
+ converter_->SetDateAllowed(false);
+ converter_->SetRegExpAllowed(false);
+ converter_->SetFunctionAllowed(true);
+ converter_->SetStrategy(this);
+}
+
+GinJavaBridgeValueConverter::~GinJavaBridgeValueConverter() {
+}
+
+v8::Handle<v8::Value> GinJavaBridgeValueConverter::ToV8Value(
+ const base::Value* value,
+ v8::Handle<v8::Context> context) const {
+ return converter_->ToV8Value(value, context);
+}
+
+scoped_ptr<base::Value> GinJavaBridgeValueConverter::FromV8Value(
+ v8::Handle<v8::Value> value,
+ v8::Handle<v8::Context> context) const {
+ return make_scoped_ptr(converter_->FromV8Value(value, context));
+}
+
+bool GinJavaBridgeValueConverter::FromV8Object(
+ v8::Handle<v8::Object> value,
+ base::Value** out,
+ v8::Isolate* isolate,
+ const FromV8ValueCallback& callback) const {
+ GinJavaBridgeObject* unwrapped;
+ if (!gin::ConvertFromV8(isolate, value, &unwrapped)) {
+ return false;
+ }
+ *out =
+ GinJavaBridgeValue::CreateObjectIDValue(unwrapped->object_id()).release();
+ return true;
+}
+
+namespace {
+
+class TypedArraySerializer {
+ public:
+ virtual ~TypedArraySerializer() {}
+ static scoped_ptr<TypedArraySerializer> Create(
+ v8::Handle<v8::TypedArray> typed_array);
+ virtual void serializeTo(char* data,
+ size_t data_length,
+ base::ListValue* out) = 0;
+ protected:
+ TypedArraySerializer() {}
+};
+
+template <typename ElementType, typename ListType>
+class TypedArraySerializerImpl : public TypedArraySerializer {
+ public:
+ static scoped_ptr<TypedArraySerializer> Create(
+ v8::Handle<v8::TypedArray> typed_array) {
+ scoped_ptr<TypedArraySerializerImpl<ElementType, ListType> > result(
+ new TypedArraySerializerImpl<ElementType, ListType>(typed_array));
+ return result.template PassAs<TypedArraySerializer>();
+ }
+
+ virtual void serializeTo(char* data,
+ size_t data_length,
+ base::ListValue* out) OVERRIDE {
+ DCHECK_EQ(data_length, typed_array_->Length() * sizeof(ElementType));
+ for (ElementType *element = reinterpret_cast<ElementType*>(data),
+ *end = element + typed_array_->Length();
+ element != end;
+ ++element) {
+ const ListType list_value = *element;
+ out->Append(new base::FundamentalValue(list_value));
+ }
+ }
+
+ private:
+ explicit TypedArraySerializerImpl(v8::Handle<v8::TypedArray> typed_array)
+ : typed_array_(typed_array) {}
+
+ v8::Handle<v8::TypedArray> typed_array_;
+
+ DISALLOW_COPY_AND_ASSIGN(TypedArraySerializerImpl);
+};
+
+// static
+scoped_ptr<TypedArraySerializer> TypedArraySerializer::Create(
+ v8::Handle<v8::TypedArray> typed_array) {
+ if (typed_array->IsInt8Array() ||
+ typed_array->IsUint8Array() ||
+ typed_array->IsUint8ClampedArray()) {
+ return TypedArraySerializerImpl<char, int>::Create(typed_array).Pass();
+ } else if (typed_array->IsInt16Array() || typed_array->IsUint16Array()) {
+ return TypedArraySerializerImpl<int16_t, int>::Create(typed_array).Pass();
+ } else if (typed_array->IsInt32Array() || typed_array->IsUint32Array()) {
+ return TypedArraySerializerImpl<int32_t, int>::Create(typed_array).Pass();
+ } else if (typed_array->IsFloat32Array()) {
+ return TypedArraySerializerImpl<float, double>::Create(typed_array).Pass();
+ } else if (typed_array->IsFloat64Array()) {
+ return TypedArraySerializerImpl<double, double>::Create(typed_array).Pass();
+ }
+ NOTREACHED();
+ return scoped_ptr<TypedArraySerializer>();
+}
+
+} // namespace
+
+bool GinJavaBridgeValueConverter::FromV8ArrayBuffer(
+ v8::Handle<v8::Object> value,
+ base::Value** out,
+ v8::Isolate* isolate) const {
+ if (!value->IsTypedArray()) {
+ *out = GinJavaBridgeValue::CreateUndefinedValue().release();
+ return true;
+ }
+
+ char* data = NULL;
+ size_t data_length = 0;
+ gin::ArrayBufferView view;
+ if (ConvertFromV8(isolate, value.As<v8::ArrayBufferView>(), &view)) {
+ data = reinterpret_cast<char*>(view.bytes());
+ data_length = view.num_bytes();
+ }
+ if (!data) {
+ *out = GinJavaBridgeValue::CreateUndefinedValue().release();
+ return true;
+ }
+
+ base::ListValue* result = new base::ListValue();
+ *out = result;
+ scoped_ptr<TypedArraySerializer> serializer(
+ TypedArraySerializer::Create(value.As<v8::TypedArray>()));
+ serializer->serializeTo(data, data_length, result);
+ return true;
+}
+
+bool GinJavaBridgeValueConverter::FromV8Number(v8::Handle<v8::Number> value,
+ base::Value** out) const {
+ double double_value = value->Value();
+ if (base::IsFinite(double_value))
+ return false;
+ *out = GinJavaBridgeValue::CreateNonFiniteValue(double_value).release();
+ return true;
+}
+
+bool GinJavaBridgeValueConverter::FromV8Undefined(base::Value** out) const {
+ *out = GinJavaBridgeValue::CreateUndefinedValue().release();
+ return true;
+}
+
+} // namespace content
diff --git a/content/renderer/java/gin_java_bridge_value_converter.h b/content/renderer/java/gin_java_bridge_value_converter.h
new file mode 100644
index 0000000..5b834ea
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_value_converter.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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 CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_VALUE_CONVERTER_H_
+#define CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_VALUE_CONVERTER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "content/public/renderer/v8_value_converter.h"
+
+namespace content {
+
+class GinJavaBridgeValueConverter : public content::V8ValueConverter::Strategy {
+ public:
+ CONTENT_EXPORT GinJavaBridgeValueConverter();
+ CONTENT_EXPORT virtual ~GinJavaBridgeValueConverter();
+
+ CONTENT_EXPORT v8::Handle<v8::Value> ToV8Value(
+ const base::Value* value,
+ v8::Handle<v8::Context> context) const;
+ CONTENT_EXPORT scoped_ptr<base::Value> FromV8Value(
+ v8::Handle<v8::Value> value,
+ v8::Handle<v8::Context> context) const;
+
+ // content::V8ValueConverter::Strategy
+ virtual bool FromV8Object(v8::Handle<v8::Object> value,
+ base::Value** out,
+ v8::Isolate* isolate,
+ const FromV8ValueCallback& callback) const OVERRIDE;
+ virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
+ base::Value** out,
+ v8::Isolate* isolate) const OVERRIDE;
+ virtual bool FromV8Number(v8::Handle<v8::Number> value,
+ base::Value** out) const OVERRIDE;
+ virtual bool FromV8Undefined(base::Value** out) const OVERRIDE;
+
+ private:
+ scoped_ptr<V8ValueConverter> converter_;
+
+ DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeValueConverter);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_JAVA_GIN_JAVA_BRIDGE_VALUE_CONVERTER_H_
diff --git a/content/renderer/java/gin_java_bridge_value_converter_unittest.cc b/content/renderer/java/gin_java_bridge_value_converter_unittest.cc
new file mode 100644
index 0000000..1a0812b
--- /dev/null
+++ b/content/renderer/java/gin_java_bridge_value_converter_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/float_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/stringprintf.h"
+#include "content/common/android/gin_java_bridge_value.h"
+#include "content/renderer/java/gin_java_bridge_value_converter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "v8/include/v8.h"
+
+namespace content {
+
+class GinJavaBridgeValueConverterTest : public testing::Test {
+ public:
+ GinJavaBridgeValueConverterTest()
+ : isolate_(v8::Isolate::GetCurrent()) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
+ context_.Reset(isolate_, v8::Context::New(isolate_, NULL, global));
+ }
+
+ virtual void TearDown() {
+ context_.Reset();
+ }
+
+ v8::Isolate* isolate_;
+
+ // Context for the JavaScript in the test.
+ v8::Persistent<v8::Context> context_;
+};
+
+TEST_F(GinJavaBridgeValueConverterTest, BasicValues) {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate_, context_);
+ v8::Context::Scope context_scope(context);
+
+ scoped_ptr<GinJavaBridgeValueConverter> converter(
+ new GinJavaBridgeValueConverter());
+
+ v8::Handle<v8::Primitive> v8_undefined(v8::Undefined(isolate_));
+ scoped_ptr<base::Value> undefined(
+ converter->FromV8Value(v8_undefined, context));
+ ASSERT_TRUE(undefined.get());
+ EXPECT_TRUE(GinJavaBridgeValue::ContainsGinJavaBridgeValue(undefined.get()));
+ scoped_ptr<const GinJavaBridgeValue> undefined_value(
+ GinJavaBridgeValue::FromValue(undefined.get()));
+ ASSERT_TRUE(undefined_value.get());
+ EXPECT_TRUE(undefined_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED));
+
+ v8::Handle<v8::Number> v8_infinity(
+ v8::Number::New(isolate_, std::numeric_limits<double>::infinity()));
+ scoped_ptr<base::Value> infinity(
+ converter->FromV8Value(v8_infinity, context));
+ ASSERT_TRUE(infinity.get());
+ EXPECT_TRUE(
+ GinJavaBridgeValue::ContainsGinJavaBridgeValue(infinity.get()));
+ scoped_ptr<const GinJavaBridgeValue> infinity_value(
+ GinJavaBridgeValue::FromValue(infinity.get()));
+ ASSERT_TRUE(infinity_value.get());
+ float native_float;
+ EXPECT_TRUE(
+ infinity_value->IsType(GinJavaBridgeValue::TYPE_NONFINITE));
+ EXPECT_TRUE(infinity_value->GetAsNonFinite(&native_float));
+ EXPECT_FALSE(base::IsFinite(native_float));
+ EXPECT_FALSE(base::IsNaN(native_float));
+}
+
+TEST_F(GinJavaBridgeValueConverterTest, ArrayBuffer) {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate_, context_);
+ v8::Context::Scope context_scope(context);
+
+ scoped_ptr<GinJavaBridgeValueConverter> converter(
+ new GinJavaBridgeValueConverter());
+
+ v8::Handle<v8::ArrayBuffer> v8_array_buffer(
+ v8::ArrayBuffer::New(isolate_, 0));
+ scoped_ptr<base::Value> undefined(
+ converter->FromV8Value(v8_array_buffer, context));
+ ASSERT_TRUE(undefined.get());
+ EXPECT_TRUE(GinJavaBridgeValue::ContainsGinJavaBridgeValue(undefined.get()));
+ scoped_ptr<const GinJavaBridgeValue> undefined_value(
+ GinJavaBridgeValue::FromValue(undefined.get()));
+ ASSERT_TRUE(undefined_value.get());
+ EXPECT_TRUE(undefined_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED));
+}
+
+TEST_F(GinJavaBridgeValueConverterTest, TypedArrays) {
+ v8::HandleScope handle_scope(isolate_);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate_, context_);
+ v8::Context::Scope context_scope(context);
+
+ scoped_ptr<GinJavaBridgeValueConverter> converter(
+ new GinJavaBridgeValueConverter());
+
+ const char* source_template = "(function() {"
+ "var array_buffer = new ArrayBuffer(%s);"
+ "var array_view = new %s(array_buffer);"
+ "array_view[0] = 42;"
+ "return array_view;"
+ "})();";
+ const char* array_types[] = {
+ "1", "Int8Array", "1", "Uint8Array", "1", "Uint8ClampedArray",
+ "2", "Int16Array", "2", "Uint16Array",
+ "4", "Int32Array", "4", "Uint32Array",
+ "4", "Float32Array", "8", "Float64Array"
+ };
+ for (size_t i = 0; i < arraysize(array_types); i += 2) {
+ const char* typed_array_type = array_types[i + 1];
+ v8::Handle<v8::Script> script(v8::Script::Compile(v8::String::NewFromUtf8(
+ isolate_,
+ base::StringPrintf(
+ source_template, array_types[i], typed_array_type).c_str())));
+ v8::Handle<v8::Value> v8_typed_array = script->Run();
+ scoped_ptr<base::Value> list_value(
+ converter->FromV8Value(v8_typed_array, context));
+ ASSERT_TRUE(list_value.get()) << typed_array_type;
+ EXPECT_TRUE(list_value->IsType(base::Value::TYPE_LIST)) << typed_array_type;
+ base::ListValue* list;
+ ASSERT_TRUE(list_value->GetAsList(&list)) << typed_array_type;
+ EXPECT_EQ(1u, list->GetSize()) << typed_array_type;
+ double first_element;
+ ASSERT_TRUE(list->GetDouble(0, &first_element)) << typed_array_type;
+ EXPECT_EQ(42.0, first_element) << typed_array_type;
+ }
+}
+
+} // namespace content
diff --git a/content/renderer/v8_value_converter_impl.cc b/content/renderer/v8_value_converter_impl.cc
index b321f55..d12b26b 100644
--- a/content/renderer/v8_value_converter_impl.cc
+++ b/content/renderer/v8_value_converter_impl.cc
@@ -38,7 +38,8 @@ bool V8ValueConverter::Strategy::FromV8Array(
}
bool V8ValueConverter::Strategy::FromV8ArrayBuffer(v8::Handle<v8::Object> value,
- base::Value** out) const {
+ base::Value** out,
+ v8::Isolate* isolate) const {
return false;
}
@@ -342,7 +343,7 @@ base::Value* V8ValueConverterImpl::FromV8ValueImpl(
}
if (val->IsArrayBuffer() || val->IsArrayBufferView())
- return FromV8ArrayBuffer(val->ToObject());
+ return FromV8ArrayBuffer(val->ToObject(), isolate);
if (val->IsObject())
return FromV8Object(val->ToObject(), state, isolate);
@@ -405,10 +406,11 @@ base::Value* V8ValueConverterImpl::FromV8Array(
}
base::Value* V8ValueConverterImpl::FromV8ArrayBuffer(
- v8::Handle<v8::Object> val) const {
+ v8::Handle<v8::Object> val,
+ v8::Isolate* isolate) const {
if (strategy_) {
base::Value* out = NULL;
- if (strategy_->FromV8ArrayBuffer(val, &out))
+ if (strategy_->FromV8ArrayBuffer(val, &out, isolate))
return out;
}
diff --git a/content/renderer/v8_value_converter_impl.h b/content/renderer/v8_value_converter_impl.h
index d026a18..a5288ae 100644
--- a/content/renderer/v8_value_converter_impl.h
+++ b/content/renderer/v8_value_converter_impl.h
@@ -61,7 +61,8 @@ class CONTENT_EXPORT V8ValueConverterImpl : public V8ValueConverter {
// This will convert objects of type ArrayBuffer or any of the
// ArrayBufferView subclasses.
- base::Value* FromV8ArrayBuffer(v8::Handle<v8::Object> val) const;
+ base::Value* FromV8ArrayBuffer(v8::Handle<v8::Object> val,
+ v8::Isolate* isolate) const;
base::Value* FromV8Object(v8::Handle<v8::Object> object,
FromV8ValueState* state,
diff --git a/content/renderer/v8_value_converter_impl_unittest.cc b/content/renderer/v8_value_converter_impl_unittest.cc
index f6f252d..6f1302d 100644
--- a/content/renderer/v8_value_converter_impl_unittest.cc
+++ b/content/renderer/v8_value_converter_impl_unittest.cc
@@ -732,7 +732,8 @@ class V8ValueConverterOverridingStrategyForTesting
return true;
}
virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
- base::Value** out) const OVERRIDE {
+ base::Value** out,
+ v8::Isolate* isolate) const OVERRIDE {
*out = NewReferenceValue();
return true;
}
@@ -822,7 +823,8 @@ class V8ValueConverterBypassStrategyForTesting
return false;
}
virtual bool FromV8ArrayBuffer(v8::Handle<v8::Object> value,
- base::Value** out) const OVERRIDE {
+ base::Value** out,
+ v8::Isolate* isolate) const OVERRIDE {
return false;
}
virtual bool FromV8Number(v8::Handle<v8::Number> value,