Apps V2 in Pepper: Host side implementation of ExntensionsCommon - Part 1.
This change handles the most common case: There is no need to call into JS custom bindings; requests are directly relayed to the browser process.
BUG=None
TEST=None
Review URL: https://chromiumcodereview.appspot.com/12567028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191355 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/renderer/extensions/chrome_v8_context.cc b/chrome/renderer/extensions/chrome_v8_context.cc
index fa1bcd6..b4ae80e 100644
--- a/chrome/renderer/extensions/chrome_v8_context.cc
+++ b/chrome/renderer/extensions/chrome_v8_context.cc
@@ -6,6 +6,7 @@
#include "base/debug/trace_event.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/strings/string_split.h"
#include "base/values.h"
#include "chrome/common/extensions/api/extension_api.h"
@@ -15,10 +16,13 @@
#include "chrome/renderer/extensions/module_system.h"
#include "chrome/renderer/extensions/user_script_slave.h"
#include "content/public/renderer/render_view.h"
+#include "content/public/renderer/v8_value_converter.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "v8/include/v8.h"
+using content::V8ValueConverter;
+
namespace extensions {
namespace {
@@ -186,4 +190,37 @@
return "";
}
+ChromeV8Context* ChromeV8Context::GetContext() {
+ return this;
+}
+
+void ChromeV8Context::OnResponseReceived(const std::string& name,
+ int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error) {
+ v8::HandleScope handle_scope;
+
+ scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
+ v8::Handle<v8::Value> argv[] = {
+ v8::Integer::New(request_id),
+ v8::String::New(name.c_str()),
+ v8::Boolean::New(success),
+ converter->ToV8Value(&response, v8_context_.get()),
+ v8::String::New(error.c_str())
+ };
+
+ v8::Handle<v8::Value> retval;
+ CHECK(CallChromeHiddenMethod("handleResponse", arraysize(argv), argv,
+ &retval));
+ // In debug, the js will validate the callback parameters and return a
+ // string if a validation error has occured.
+ if (DCHECK_IS_ON()) {
+ if (!retval.IsEmpty() && !retval->IsUndefined()) {
+ std::string error = *v8::String::AsciiValue(retval);
+ DCHECK(false) << error;
+ }
+ }
+}
+
} // namespace extensions
diff --git a/chrome/renderer/extensions/chrome_v8_context.h b/chrome/renderer/extensions/chrome_v8_context.h
index 323ce77..8b07a453 100644
--- a/chrome/renderer/extensions/chrome_v8_context.h
+++ b/chrome/renderer/extensions/chrome_v8_context.h
@@ -8,8 +8,10 @@
#include <string>
#include "base/basictypes.h"
+#include "base/compiler_specific.h"
#include "chrome/common/extensions/features/feature.h"
#include "chrome/renderer/extensions/module_system.h"
+#include "chrome/renderer/extensions/request_sender.h"
#include "chrome/renderer/extensions/scoped_persistent.h"
#include "v8/include/v8.h"
@@ -29,13 +31,13 @@
// TODO(aa): Consider converting this back to a set of bindings_utils. It would
// require adding WebFrame::GetIsolatedWorldIdByV8Context() to WebCore, but then
// we won't need this object and it's a bit less state to keep track of.
-class ChromeV8Context {
+class ChromeV8Context : public RequestSender::Source {
public:
ChromeV8Context(v8::Handle<v8::Context> context,
WebKit::WebFrame* frame,
const Extension* extension,
Feature::Context context_type);
- ~ChromeV8Context();
+ virtual ~ChromeV8Context();
// Clears the WebFrame for this contexts and invalidates the associated
// ModuleSystem.
@@ -104,6 +106,14 @@
// Returns a string description of the type of context this is.
std::string GetContextTypeDescription();
+ // RequestSender::Source implementation.
+ virtual ChromeV8Context* GetContext() OVERRIDE;
+ virtual void OnResponseReceived(const std::string& name,
+ int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error) OVERRIDE;
+
private:
// The v8 context the bindings are accessible to.
ScopedPersistent<v8::Context> v8_context_;
diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc
index e4f0bfa..f5e08e3b 100644
--- a/chrome/renderer/extensions/dispatcher.cc
+++ b/chrome/renderer/extensions/dispatcher.cc
@@ -1080,7 +1080,7 @@
context->DispatchOnUnloadEvent();
// TODO(kalman): add an invalidation observer interface to ChromeV8Context.
- request_sender_->InvalidateContext(context);
+ request_sender_->InvalidateSource(context);
v8_context_set_.Remove(context);
VLOG(1) << "Num tracked contexts: " << v8_context_set_.size();
diff --git a/chrome/renderer/extensions/dispatcher.h b/chrome/renderer/extensions/dispatcher.h
index 4f568b8..d443000d0 100644
--- a/chrome/renderer/extensions/dispatcher.h
+++ b/chrome/renderer/extensions/dispatcher.h
@@ -70,6 +70,9 @@
ContentWatcher* content_watcher() {
return content_watcher_.get();
}
+ RequestSender* request_sender() {
+ return request_sender_.get();
+ }
bool IsExtensionActive(const std::string& extension_id) const;
diff --git a/chrome/renderer/extensions/request_sender.cc b/chrome/renderer/extensions/request_sender.cc
index e59be5a..59d1d920 100644
--- a/chrome/renderer/extensions/request_sender.cc
+++ b/chrome/renderer/extensions/request_sender.cc
@@ -8,27 +8,23 @@
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/renderer/extensions/chrome_v8_context.h"
#include "chrome/renderer/extensions/dispatcher.h"
-#include "chrome/renderer/extensions/scoped_persistent.h"
#include "content/public/renderer/render_view.h"
-#include "content/public/renderer/v8_value_converter.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebUserGestureIndicator.h"
-using content::V8ValueConverter;
-
namespace extensions {
// Contains info relevant to a pending API request.
struct PendingRequest {
public :
- PendingRequest(const std::string& name, ChromeV8Context* context)
- : name(name), context(context) {
+ PendingRequest(const std::string& name, RequestSender::Source* source)
+ : name(name), source(source) {
}
std::string name;
- ChromeV8Context* context;
+ RequestSender::Source* source;
};
RequestSender::RequestSender(Dispatcher* dispatcher) : dispatcher_(dispatcher) {
@@ -52,12 +48,21 @@
return result;
}
-void RequestSender::StartRequest(ChromeV8Context* context,
+int RequestSender::GetNextRequestId() const {
+ static int next_request_id = 0;
+ return next_request_id++;
+}
+
+void RequestSender::StartRequest(Source* source,
const std::string& name,
int request_id,
bool has_callback,
bool for_io_thread,
base::ListValue* value_args) {
+ ChromeV8Context* context = source->GetContext();
+ if (!context)
+ return;
+
// Get the current RenderView so that we can send a routed IPC message from
// the correct source.
content::RenderView* renderview = context->GetRenderView();
@@ -83,7 +88,7 @@
source_origin = webframe->document().securityOrigin();
}
- InsertRequest(request_id, new PendingRequest(name, context));
+ InsertRequest(request_id, new PendingRequest(name, source));
ExtensionHostMsg_Request_Params params;
params.name = name;
@@ -106,7 +111,7 @@
void RequestSender::HandleResponse(int request_id,
bool success,
- const base::ListValue& responseList,
+ const base::ListValue& response,
const std::string& error) {
linked_ptr<PendingRequest> request = RemoveRequest(request_id);
@@ -115,36 +120,14 @@
return;
}
- v8::HandleScope handle_scope;
-
- scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
- v8::Handle<v8::Value> argv[] = {
- v8::Integer::New(request_id),
- v8::String::New(request->name.c_str()),
- v8::Boolean::New(success),
- converter->ToV8Value(&responseList, request->context->v8_context()),
- v8::String::New(error.c_str())
- };
-
- v8::Handle<v8::Value> retval;
- CHECK(request->context->CallChromeHiddenMethod("handleResponse",
- arraysize(argv),
- argv,
- &retval));
- // In debug, the js will validate the callback parameters and return a
- // string if a validation error has occured.
- if (DCHECK_IS_ON()) {
- if (!retval.IsEmpty() && !retval->IsUndefined()) {
- std::string error = *v8::String::AsciiValue(retval);
- DCHECK(false) << error;
- }
- }
+ request->source->OnResponseReceived(request->name, request_id, success,
+ response, error);
}
-void RequestSender::InvalidateContext(ChromeV8Context* context) {
+void RequestSender::InvalidateSource(Source* source) {
for (PendingRequestMap::iterator it = pending_requests_.begin();
it != pending_requests_.end();) {
- if (it->second->context == context)
+ if (it->second->source == source)
pending_requests_.erase(it++);
else
++it;
diff --git a/chrome/renderer/extensions/request_sender.h b/chrome/renderer/extensions/request_sender.h
index b1463fd..618a9678 100644
--- a/chrome/renderer/extensions/request_sender.h
+++ b/chrome/renderer/extensions/request_sender.h
@@ -25,16 +25,37 @@
// extension host and routing the responses back to the caller.
class RequestSender {
public:
+ // Source represents a user of RequestSender. Every request is associated with
+ // a Source object, which will be notified when the corresponding response
+ // arrives. When a Source object is going away and there are pending requests,
+ // it should call InvalidateSource() to make sure no notifications are sent to
+ // it later.
+ class Source {
+ public:
+ virtual ~Source() {}
+
+ virtual ChromeV8Context* GetContext() = 0;
+ virtual void OnResponseReceived(const std::string& name,
+ int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error) = 0;
+ };
+
explicit RequestSender(Dispatcher* dispatcher);
~RequestSender();
+ // In order to avoid collision, all |request_id|s passed into StartRequest()
+ // should be generated by this method.
+ int GetNextRequestId() const;
+
// Makes a call to the API function |name| that is to be handled by the
// extension host. The response to this request will be received in
// HandleResponse().
// TODO(koz): Remove |request_id| and generate that internally.
// There are multiple of these per render view though, so we'll
// need to vend the IDs centrally.
- void StartRequest(ChromeV8Context* target_context,
+ void StartRequest(Source* source,
const std::string& name,
int request_id,
bool has_callback,
@@ -47,9 +68,9 @@
const base::ListValue& response,
const std::string& error);
- // Notifies this that a context is no longer valid.
+ // Notifies this that a request source is no longer valid.
// TODO(kalman): Do this in a generic/safe way.
- void InvalidateContext(ChromeV8Context* context);
+ void InvalidateSource(Source* source);
private:
typedef std::map<int, linked_ptr<PendingRequest> > PendingRequestMap;
diff --git a/chrome/renderer/extensions/send_request_natives.cc b/chrome/renderer/extensions/send_request_natives.cc
index be87840..6af99850 100644
--- a/chrome/renderer/extensions/send_request_natives.cc
+++ b/chrome/renderer/extensions/send_request_natives.cc
@@ -31,8 +31,7 @@
v8::Handle<v8::Value> SendRequestNatives::GetNextRequestId(
const v8::Arguments& args) {
- static int next_request_id = 0;
- return v8::Integer::New(next_request_id++);
+ return v8::Integer::New(request_sender_->GetNextRequestId());
}
// Starts an API request to the browser, with an optional callback. The
diff --git a/chrome/renderer/pepper/pepper_extensions_common_host.cc b/chrome/renderer/pepper/pepper_extensions_common_host.cc
index dacfd7c..63a17c7 100644
--- a/chrome/renderer/pepper/pepper_extensions_common_host.cc
+++ b/chrome/renderer/pepper/pepper_extensions_common_host.cc
@@ -4,25 +4,38 @@
#include "chrome/renderer/pepper/pepper_extensions_common_host.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
+#include "chrome/renderer/extensions/chrome_v8_context.h"
+#include "chrome/renderer/extensions/dispatcher.h"
+#include "chrome/renderer/extensions/extension_helper.h"
+#include "content/public/renderer/render_view.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
namespace chrome {
PepperExtensionsCommonHost::PepperExtensionsCommonHost(
content::RendererPpapiHost* host,
PP_Instance instance,
- PP_Resource resource)
+ PP_Resource resource,
+ extensions::Dispatcher* dispatcher)
: ResourceHost(host->GetPpapiHost(), instance, resource),
- renderer_ppapi_host_(host) {
+ renderer_ppapi_host_(host),
+ dispatcher_(dispatcher) {
}
PepperExtensionsCommonHost::~PepperExtensionsCommonHost() {
+ dispatcher_->request_sender()->InvalidateSource(this);
}
// static
@@ -30,7 +43,18 @@
content::RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource) {
- return new PepperExtensionsCommonHost(host, instance, resource);
+ content::RenderView* render_view = host->GetRenderViewForInstance(instance);
+ if (!render_view)
+ return NULL;
+ extensions::ExtensionHelper* extension_helper =
+ extensions::ExtensionHelper::Get(render_view);
+ if (!extension_helper)
+ return NULL;
+ extensions::Dispatcher* dispatcher = extension_helper->dispatcher();
+ if (!dispatcher)
+ return NULL;
+
+ return new PepperExtensionsCommonHost(host, instance, resource, dispatcher);
}
int32_t PepperExtensionsCommonHost::OnResourceMessageReceived(
@@ -45,20 +69,66 @@
return PP_ERROR_FAILED;
}
+extensions::ChromeV8Context* PepperExtensionsCommonHost::GetContext() {
+ WebKit::WebPluginContainer* container =
+ renderer_ppapi_host_->GetContainerForInstance(pp_instance());
+ if (!container)
+ return NULL;
+
+ WebKit::WebFrame* frame = container->element().document().frame();
+ v8::HandleScope scope;
+ return dispatcher_->v8_context_set().GetByV8Context(
+ frame->mainWorldScriptContext());
+}
+
+void PepperExtensionsCommonHost::OnResponseReceived(
+ const std::string& /* name */,
+ int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& /* error */) {
+ PendingRequestMap::iterator iter = pending_request_map_.find(request_id);
+
+ // Ignore responses resulted from calls to OnPost().
+ if (iter == pending_request_map_.end()) {
+ DCHECK_EQ(0u, response.GetSize());
+ return;
+ }
+
+ linked_ptr<ppapi::host::ReplyMessageContext> context = iter->second;
+ pending_request_map_.erase(iter);
+
+ context->params.set_result(success ? PP_OK : PP_ERROR_FAILED);
+ SendReply(*context, PpapiPluginMsg_ExtensionsCommon_CallReply(response));
+}
+
int32_t PepperExtensionsCommonHost::OnPost(
ppapi::host::HostMessageContext* context,
const std::string& request_name,
- const base::ListValue& args) {
- // TODO(yzshen): Implement it.
- return PP_ERROR_NOTSUPPORTED;
+ base::ListValue& args) {
+ // TODO(yzshen): Add support for calling into JS for APIs that have custom
+ // bindings.
+ int request_id = dispatcher_->request_sender()->GetNextRequestId();
+ dispatcher_->request_sender()->StartRequest(this, request_name, request_id,
+ false, false, &args);
+ return PP_OK;
}
int32_t PepperExtensionsCommonHost::OnCall(
ppapi::host::HostMessageContext* context,
const std::string& request_name,
- const base::ListValue& args) {
- // TODO(yzshen): Implement it.
- return PP_ERROR_NOTSUPPORTED;
+ base::ListValue& args) {
+ // TODO(yzshen): Add support for calling into JS for APIs that have custom
+ // bindings.
+ int request_id = dispatcher_->request_sender()->GetNextRequestId();
+ pending_request_map_[request_id] =
+ linked_ptr<ppapi::host::ReplyMessageContext>(
+ new ppapi::host::ReplyMessageContext(
+ context->MakeReplyMessageContext()));
+
+ dispatcher_->request_sender()->StartRequest(this, request_name, request_id,
+ true, false, &args);
+ return PP_OK_COMPLETIONPENDING;
}
} // namespace chrome
diff --git a/chrome/renderer/pepper/pepper_extensions_common_host.h b/chrome/renderer/pepper/pepper_extensions_common_host.h
index 86f932a..054709d 100644
--- a/chrome/renderer/pepper/pepper_extensions_common_host.h
+++ b/chrome/renderer/pepper/pepper_extensions_common_host.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/linked_ptr.h"
+#include "chrome/renderer/extensions/request_sender.h"
#include "ppapi/host/resource_host.h"
namespace base {
@@ -27,9 +28,14 @@
}
}
+namespace extensions {
+class Dispatcher;
+}
+
namespace chrome {
-class PepperExtensionsCommonHost : public ppapi::host::ResourceHost {
+class PepperExtensionsCommonHost : public ppapi::host::ResourceHost,
+ public extensions::RequestSender::Source {
public:
virtual ~PepperExtensionsCommonHost();
@@ -42,24 +48,34 @@
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
+ // extensions::RequestSender::Source implementation.
+ virtual extensions::ChromeV8Context* GetContext() OVERRIDE;
+ virtual void OnResponseReceived(const std::string& name,
+ int request_id,
+ bool success,
+ const base::ListValue& response,
+ const std::string& error) OVERRIDE;
private:
typedef std::map<int, linked_ptr<ppapi::host::ReplyMessageContext> >
PendingRequestMap;
PepperExtensionsCommonHost(content::RendererPpapiHost* host,
PP_Instance instance,
- PP_Resource resource);
+ PP_Resource resource,
+ extensions::Dispatcher* dispatcher);
int32_t OnPost(ppapi::host::HostMessageContext* context,
const std::string& request_name,
- const base::ListValue& args);
+ base::ListValue& args);
int32_t OnCall(ppapi::host::HostMessageContext* context,
const std::string& request_name,
- const base::ListValue& args);
+ base::ListValue& args);
// Non-owning pointer.
content::RendererPpapiHost* renderer_ppapi_host_;
+ // Non-owning pointer.
+ extensions::Dispatcher* dispatcher_;
PendingRequestMap pending_request_map_;
diff --git a/ppapi/host/dispatch_host_message.h b/ppapi/host/dispatch_host_message.h
index b9e57dc..2498725 100644
--- a/ppapi/host/dispatch_host_message.h
+++ b/ppapi/host/dispatch_host_message.h
@@ -22,42 +22,42 @@
template <class ObjT, class Method>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple0& arg) {
+ Tuple0& arg) {
return (obj->*method)(context);
}
template <class ObjT, class Method, class A>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple1<A>& arg) {
+ Tuple1<A>& arg) {
return (obj->*method)(context, arg.a);
}
template<class ObjT, class Method, class A, class B>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple2<A, B>& arg) {
+ Tuple2<A, B>& arg) {
return (obj->*method)(context, arg.a, arg.b);
}
template<class ObjT, class Method, class A, class B, class C>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple3<A, B, C>& arg) {
+ Tuple3<A, B, C>& arg) {
return (obj->*method)(context, arg.a, arg.b, arg.c);
}
template<class ObjT, class Method, class A, class B, class C, class D>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple4<A, B, C, D>& arg) {
+ Tuple4<A, B, C, D>& arg) {
return (obj->*method)(context, arg.a, arg.b, arg.c, arg.d);
}
template<class ObjT, class Method, class A, class B, class C, class D, class E>
inline int32_t DispatchResourceCall(ObjT* obj, Method method,
HostMessageContext* context,
- const Tuple5<A, B, C, D, E>& arg) {
+ Tuple5<A, B, C, D, E>& arg) {
return (obj->*method)(context, arg.a, arg.b, arg.c, arg.d, arg.e);
}