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);
 }