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.
5 #include "extensions/renderer/guest_view/extensions_guest_view_container.h"
7 #include "content/public/renderer/render_frame.h"
8 #include "content/public/renderer/render_view.h"
9 #include "extensions/common/guest_view/guest_view_constants.h"
10 #include "extensions/common/guest_view/guest_view_messages.h"
11 #include "third_party/WebKit/public/web/WebLocalFrame.h"
12 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
13 #include "third_party/WebKit/public/web/WebView.h"
16 typedef std::map
<int, extensions::ExtensionsGuestViewContainer
*>
17 ExtensionsGuestViewContainerMap
;
18 static base::LazyInstance
<ExtensionsGuestViewContainerMap
>
19 g_guest_view_container_map
= LAZY_INSTANCE_INITIALIZER
;
22 namespace extensions
{
24 ExtensionsGuestViewContainer::Request::Request(
25 GuestViewContainer
* container
,
26 v8::Handle
<v8::Function
> callback
,
28 : container_(container
), callback_(isolate
, callback
), isolate_(isolate
) {
31 ExtensionsGuestViewContainer::Request::~Request() {
34 bool ExtensionsGuestViewContainer::Request::HasCallback() const {
35 return !callback_
.IsEmpty();
38 v8::Handle
<v8::Function
>
39 ExtensionsGuestViewContainer::Request::GetCallback() const {
40 return v8::Local
<v8::Function
>::New(isolate_
, callback_
);
43 void ExtensionsGuestViewContainer::Request::ExecuteCallbackIfAvailable(
44 int argc
, scoped_ptr
<v8::Handle
<v8::Value
>[]> argv
) {
48 v8::HandleScope
handle_scope(isolate());
49 v8::Handle
<v8::Function
> callback
= GetCallback();
50 v8::Handle
<v8::Context
> context
= callback
->CreationContext();
51 if (context
.IsEmpty())
54 v8::Context::Scope
context_scope(context
);
55 blink::WebScopedMicrotaskSuppression suppression
;
57 // Call the AttachGuest API's callback with the guest proxy as the first
59 callback
->Call(context
->Global(), argc
, argv
.get());
62 ExtensionsGuestViewContainer::AttachRequest::AttachRequest(
63 GuestViewContainer
* container
,
64 int guest_instance_id
,
65 scoped_ptr
<base::DictionaryValue
> params
,
66 v8::Handle
<v8::Function
> callback
,
68 : Request(container
, callback
, isolate
),
69 guest_instance_id_(guest_instance_id
),
70 params_(params
.Pass()) {
73 ExtensionsGuestViewContainer::AttachRequest::~AttachRequest() {
76 void ExtensionsGuestViewContainer::AttachRequest::PerformRequest() {
77 if (!container()->render_frame())
80 // Step 1, send the attach params to extensions/.
81 container()->render_frame()->Send(
82 new GuestViewHostMsg_AttachGuest(container()->element_instance_id(),
86 // Step 2, attach plugin through content/.
87 container()->render_frame()->AttachGuest(container()->element_instance_id());
90 void ExtensionsGuestViewContainer::AttachRequest::HandleResponse(
91 const IPC::Message
& message
) {
92 GuestViewMsg_GuestAttached::Param param
;
93 if (!GuestViewMsg_GuestAttached::Read(&message
, ¶m
))
96 content::RenderView
* guest_proxy_render_view
=
97 content::RenderView::FromRoutingID(get
<1>(param
));
98 // TODO(fsamuel): Should we be reporting an error to JavaScript or DCHECKing?
99 if (!guest_proxy_render_view
)
102 v8::HandleScope
handle_scope(isolate());
103 blink::WebFrame
* frame
= guest_proxy_render_view
->GetWebView()->mainFrame();
104 v8::Local
<v8::Value
> window
= frame
->mainWorldScriptContext()->Global();
107 scoped_ptr
<v8::Handle
<v8::Value
>[]> argv(new v8::Handle
<v8::Value
>[argc
]);
110 ExecuteCallbackIfAvailable(argc
, argv
.Pass());
113 ExtensionsGuestViewContainer::DetachRequest::DetachRequest(
114 GuestViewContainer
* container
,
115 v8::Handle
<v8::Function
> callback
,
116 v8::Isolate
* isolate
)
117 : Request(container
, callback
, isolate
) {
120 ExtensionsGuestViewContainer::DetachRequest::~DetachRequest() {
123 void ExtensionsGuestViewContainer::DetachRequest::PerformRequest() {
124 if (!container()->render_frame())
127 container()->render_frame()->DetachGuest(container()->element_instance_id());
130 void ExtensionsGuestViewContainer::DetachRequest::HandleResponse(
131 const IPC::Message
& message
) {
132 ExecuteCallbackIfAvailable(0 /* argc */, nullptr);
135 ExtensionsGuestViewContainer::ExtensionsGuestViewContainer(
136 content::RenderFrame
* render_frame
)
137 : GuestViewContainer(render_frame
),
139 destruction_isolate_(nullptr),
140 element_resize_isolate_(nullptr),
141 weak_ptr_factory_(this) {
144 ExtensionsGuestViewContainer::~ExtensionsGuestViewContainer() {
145 if (element_instance_id() != guestview::kInstanceIDNone
)
146 g_guest_view_container_map
.Get().erase(element_instance_id());
148 if (pending_response_
.get())
149 pending_response_
->ExecuteCallbackIfAvailable(0 /* argc */, nullptr);
151 while (pending_requests_
.size() > 0) {
152 linked_ptr
<Request
> pending_request
= pending_requests_
.front();
153 pending_requests_
.pop_front();
154 // Call the JavaScript callbacks with no arguments which implies an error.
155 pending_request
->ExecuteCallbackIfAvailable(0 /* argc */, nullptr);
158 // Call the destruction callback, if one is registered.
159 if (!destruction_callback_
.IsEmpty()) {
160 v8::HandleScope
handle_scope(destruction_isolate_
);
161 v8::Handle
<v8::Function
> callback
=
162 v8::Local
<v8::Function
>::New(destruction_isolate_
,
163 destruction_callback_
);
164 v8::Handle
<v8::Context
> context
= callback
->CreationContext();
165 if (context
.IsEmpty())
168 v8::Context::Scope
context_scope(context
);
169 blink::WebScopedMicrotaskSuppression suppression
;
171 callback
->Call(context
->Global(), 0 /* argc */, nullptr);
175 ExtensionsGuestViewContainer
* ExtensionsGuestViewContainer::FromID(
176 int element_instance_id
) {
177 ExtensionsGuestViewContainerMap
* guest_view_containers
=
178 g_guest_view_container_map
.Pointer();
179 auto it
= guest_view_containers
->find(element_instance_id
);
180 return it
== guest_view_containers
->end() ? nullptr : it
->second
;
183 void ExtensionsGuestViewContainer::IssueRequest(linked_ptr
<Request
> request
) {
184 EnqueueRequest(request
);
185 PerformPendingRequest();
188 void ExtensionsGuestViewContainer::RegisterDestructionCallback(
189 v8::Handle
<v8::Function
> callback
,
190 v8::Isolate
* isolate
) {
191 destruction_callback_
.Reset(isolate
, callback
);
192 destruction_isolate_
= isolate
;
195 void ExtensionsGuestViewContainer::RegisterElementResizeCallback(
196 v8::Handle
<v8::Function
> callback
,
197 v8::Isolate
* isolate
) {
198 element_resize_callback_
.Reset(isolate
, callback
);
199 element_resize_isolate_
= isolate
;
202 void ExtensionsGuestViewContainer::DidResizeElement(const gfx::Size
& old_size
,
203 const gfx::Size
& new_size
) {
204 // Call the element resize callback, if one is registered.
205 if (element_resize_callback_
.IsEmpty())
208 base::MessageLoop::current()->PostTask(
210 base::Bind(&ExtensionsGuestViewContainer::CallElementResizeCallback
,
211 weak_ptr_factory_
.GetWeakPtr(), old_size
, new_size
));
214 bool ExtensionsGuestViewContainer::OnMessageReceived(
215 const IPC::Message
& message
) {
216 OnHandleCallback(message
);
220 void ExtensionsGuestViewContainer::SetElementInstanceID(
221 int element_instance_id
) {
222 GuestViewContainer::SetElementInstanceID(element_instance_id
);
224 DCHECK(g_guest_view_container_map
.Get().find(element_instance_id
) ==
225 g_guest_view_container_map
.Get().end());
226 g_guest_view_container_map
.Get().insert(
227 std::make_pair(element_instance_id
, this));
230 void ExtensionsGuestViewContainer::Ready() {
232 CHECK(!pending_response_
.get());
233 PerformPendingRequest();
236 void ExtensionsGuestViewContainer::OnHandleCallback(
237 const IPC::Message
& message
) {
238 // Handle the callback for the current request with a pending response.
239 HandlePendingResponseCallback(message
);
240 // Perform the subsequent attach request if one exists.
241 PerformPendingRequest();
244 void ExtensionsGuestViewContainer::CallElementResizeCallback(
245 const gfx::Size
& old_size
,
246 const gfx::Size
& new_size
) {
247 v8::HandleScope
handle_scope(element_resize_isolate_
);
248 v8::Handle
<v8::Function
> callback
= v8::Local
<v8::Function
>::New(
249 element_resize_isolate_
, element_resize_callback_
);
250 v8::Handle
<v8::Context
> context
= callback
->CreationContext();
251 if (context
.IsEmpty())
255 v8::Handle
<v8::Value
> argv
[argc
] = {
256 v8::Integer::New(element_resize_isolate_
, old_size
.width()),
257 v8::Integer::New(element_resize_isolate_
, old_size
.height()),
258 v8::Integer::New(element_resize_isolate_
, new_size
.width()),
259 v8::Integer::New(element_resize_isolate_
, new_size
.height())};
261 v8::Context::Scope
context_scope(context
);
262 blink::WebScopedMicrotaskSuppression suppression
;
264 callback
->Call(context
->Global(), argc
, argv
);
267 void ExtensionsGuestViewContainer::EnqueueRequest(linked_ptr
<Request
> request
) {
268 pending_requests_
.push_back(request
);
271 void ExtensionsGuestViewContainer::PerformPendingRequest() {
272 if (!ready_
|| pending_requests_
.empty() || pending_response_
.get())
275 linked_ptr
<Request
> pending_request
= pending_requests_
.front();
276 pending_requests_
.pop_front();
277 pending_request
->PerformRequest();
278 pending_response_
= pending_request
;
281 void ExtensionsGuestViewContainer::HandlePendingResponseCallback(
282 const IPC::Message
& message
) {
283 CHECK(pending_response_
.get());
284 linked_ptr
<Request
> pending_response(pending_response_
.release());
285 pending_response
->HandleResponse(message
);
288 } // namespace extensions