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 16namespace { 17typedef std::pair<int, int> GuestViewID; 18typedef std::map<GuestViewID, extensions::GuestViewContainer*> 19 GuestViewContainerMap; 20static base::LazyInstance<GuestViewContainerMap> g_guest_view_container_map = 21 LAZY_INSTANCE_INITIALIZER; 22} // namespace 23 24namespace extensions { 25 26GuestViewContainer::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 39GuestViewContainer::~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 46GuestViewContainer* 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 56void 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 80void 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 89void 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 98void GuestViewContainer::DidReceiveData(const char* data, int data_length) { 99 std::string value(data, data_length); 100 html_string_ += value; 101} 102 103void GuestViewContainer::OnDestruct() { 104 // GuestViewContainer's lifetime is managed by BrowserPlugin so don't let 105 // RenderFrameObserver self-destruct here. 106} 107 108bool 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 130void 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 138void 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 175bool 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