Home | History | Annotate | Download | only in guest_view
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "extensions/renderer/guest_view/guest_view_container.h"
      6 
      7 #include "content/public/renderer/browser_plugin_delegate.h"
      8 #include "content/public/renderer/render_frame.h"
      9 #include "content/public/renderer/render_view.h"
     10 #include "extensions/common/extension_messages.h"
     11 #include "extensions/common/guest_view/guest_view_constants.h"
     12 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     13 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
     14 #include "third_party/WebKit/public/web/WebView.h"
     15 
     16 namespace {
     17 typedef std::pair<int, int> GuestViewID;
     18 typedef std::map<GuestViewID, extensions::GuestViewContainer*>
     19     GuestViewContainerMap;
     20 static base::LazyInstance<GuestViewContainerMap> g_guest_view_container_map =
     21     LAZY_INSTANCE_INITIALIZER;
     22 }  // namespace
     23 
     24 namespace extensions {
     25 
     26 GuestViewContainer::GuestViewContainer(
     27     content::RenderFrame* render_frame,
     28     const std::string& mime_type)
     29     : content::BrowserPluginDelegate(render_frame, mime_type),
     30       content::RenderFrameObserver(render_frame),
     31       mime_type_(mime_type),
     32       element_instance_id_(guestview::kInstanceIDNone),
     33       render_view_routing_id_(render_frame->GetRenderView()->GetRoutingID()),
     34       attached_(false),
     35       attach_pending_(false),
     36       isolate_(NULL) {
     37 }
     38 
     39 GuestViewContainer::~GuestViewContainer() {
     40   if (element_instance_id_ != guestview::kInstanceIDNone) {
     41     g_guest_view_container_map.Get().erase(
     42         GuestViewID(render_view_routing_id_, element_instance_id_));
     43   }
     44 }
     45 
     46 GuestViewContainer* GuestViewContainer::FromID(int render_view_routing_id,
     47                                                int element_instance_id) {
     48   GuestViewContainerMap* guest_view_containers =
     49       g_guest_view_container_map.Pointer();
     50   GuestViewContainerMap::iterator it = guest_view_containers->find(
     51       GuestViewID(render_view_routing_id, element_instance_id));
     52   return it == guest_view_containers->end() ? NULL : it->second;
     53 }
     54 
     55 
     56 void GuestViewContainer::AttachGuest(int element_instance_id,
     57                                      int guest_instance_id,
     58                                      scoped_ptr<base::DictionaryValue> params,
     59                                      v8::Handle<v8::Function> callback,
     60                                      v8::Isolate* isolate) {
     61   // GuestViewContainer supports reattachment (i.e. attached_ == true) but not
     62   // while a current attach process is pending.
     63   if (attach_pending_)
     64     return;
     65 
     66   // Step 1, send the attach params to chrome/.
     67   render_frame()->Send(new ExtensionHostMsg_AttachGuest(render_view_routing_id_,
     68                                                         element_instance_id,
     69                                                         guest_instance_id,
     70                                                         *params));
     71 
     72   // Step 2, attach plugin through content/.
     73   render_frame()->AttachGuest(element_instance_id);
     74 
     75   callback_.reset(callback);
     76   isolate_ = isolate;
     77   attach_pending_ = true;
     78 }
     79 
     80 void GuestViewContainer::SetElementInstanceID(int element_instance_id) {
     81   GuestViewID guest_view_id(render_view_routing_id_, element_instance_id);
     82   DCHECK_EQ(element_instance_id_, guestview::kInstanceIDNone);
     83   DCHECK(g_guest_view_container_map.Get().find(guest_view_id) ==
     84             g_guest_view_container_map.Get().end());
     85   element_instance_id_ = element_instance_id;
     86   g_guest_view_container_map.Get().insert(std::make_pair(guest_view_id, this));
     87 }
     88 
     89 void GuestViewContainer::DidFinishLoading() {
     90   if (mime_type_.empty())
     91     return;
     92 
     93   DCHECK_NE(element_instance_id_, guestview::kInstanceIDNone);
     94   render_frame()->Send(new ExtensionHostMsg_CreateMimeHandlerViewGuest(
     95       routing_id(), html_string_, mime_type_, element_instance_id_));
     96 }
     97 
     98 void GuestViewContainer::DidReceiveData(const char* data, int data_length) {
     99   std::string value(data, data_length);
    100   html_string_ += value;
    101 }
    102 
    103 void GuestViewContainer::OnDestruct() {
    104   // GuestViewContainer's lifetime is managed by BrowserPlugin so don't let
    105   // RenderFrameObserver self-destruct here.
    106 }
    107 
    108 bool GuestViewContainer::OnMessageReceived(const IPC::Message& message) {
    109   if (!ShouldHandleMessage(message))
    110     return false;
    111 
    112   DCHECK_NE(element_instance_id_, guestview::kInstanceIDNone);
    113   int element_instance_id = guestview::kInstanceIDNone;
    114   PickleIterator iter(message);
    115   bool success = iter.ReadInt(&element_instance_id);
    116   DCHECK(success);
    117   if (element_instance_id != element_instance_id_)
    118     return false;
    119 
    120   bool handled = true;
    121   IPC_BEGIN_MESSAGE_MAP(GuestViewContainer, message)
    122     IPC_MESSAGE_HANDLER(ExtensionMsg_CreateMimeHandlerViewGuestACK,
    123                         OnCreateMimeHandlerViewGuestACK)
    124     IPC_MESSAGE_HANDLER(ExtensionMsg_GuestAttached, OnGuestAttached)
    125     IPC_MESSAGE_UNHANDLED(handled = false)
    126   IPC_END_MESSAGE_MAP()
    127   return handled;
    128 }
    129 
    130 void GuestViewContainer::OnCreateMimeHandlerViewGuestACK(
    131     int element_instance_id) {
    132   DCHECK_NE(element_instance_id_, guestview::kInstanceIDNone);
    133   DCHECK_EQ(element_instance_id_, element_instance_id);
    134   DCHECK(!mime_type_.empty());
    135   render_frame()->AttachGuest(element_instance_id);
    136 }
    137 
    138 void GuestViewContainer::OnGuestAttached(int element_instance_id,
    139                                          int guest_routing_id) {
    140   attached_ = true;
    141   attach_pending_ = false;
    142 
    143   // If we don't have a callback then there's nothing more to do.
    144   if (callback_.IsEmpty())
    145     return;
    146 
    147   content::RenderView* guest_proxy_render_view =
    148       content::RenderView::FromRoutingID(guest_routing_id);
    149   // TODO(fsamuel): Should we be reporting an error to JavaScript or DCHECKing?
    150   if (!guest_proxy_render_view)
    151     return;
    152 
    153   v8::HandleScope handle_scope(isolate_);
    154   v8::Handle<v8::Function> callback = callback_.NewHandle(isolate_);
    155   v8::Handle<v8::Context> context = callback->CreationContext();
    156   if (context.IsEmpty())
    157     return;
    158 
    159   blink::WebFrame* frame = guest_proxy_render_view->GetWebView()->mainFrame();
    160   v8::Local<v8::Value> window = frame->mainWorldScriptContext()->Global();
    161 
    162   const int argc = 1;
    163   v8::Handle<v8::Value> argv[argc] = { window };
    164 
    165   v8::Context::Scope context_scope(context);
    166   blink::WebScopedMicrotaskSuppression suppression;
    167 
    168   // Call the AttachGuest API's callback with the guest proxy as the first
    169   // parameter.
    170   callback->Call(context->Global(), argc, argv);
    171   callback_.reset();
    172 }
    173 
    174 // static
    175 bool GuestViewContainer::ShouldHandleMessage(const IPC::Message& message) {
    176   switch (message.type()) {
    177     case ExtensionMsg_CreateMimeHandlerViewGuestACK::ID:
    178     case ExtensionMsg_GuestAttached::ID:
    179       return true;
    180     default:
    181       break;
    182   }
    183   return false;
    184 }
    185 
    186 }  // namespace extensions
    187