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/guest_view_internal_custom_bindings.h"
10 #include "base/bind.h"
11 #include "components/guest_view/common/guest_view_constants.h"
12 #include "components/guest_view/common/guest_view_messages.h"
13 #include "components/guest_view/renderer/guest_view_request.h"
14 #include "components/guest_view/renderer/iframe_guest_view_container.h"
15 #include "components/guest_view/renderer/iframe_guest_view_request.h"
16 #include "content/public/child/v8_value_converter.h"
17 #include "content/public/renderer/render_frame.h"
18 #include "content/public/renderer/render_thread.h"
19 #include "content/public/renderer/render_view.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/extension_messages.h"
22 #include "extensions/common/guest_view/extensions_guest_view_messages.h"
23 #include "extensions/renderer/guest_view/extensions_guest_view_container.h"
24 #include "extensions/renderer/script_context.h"
25 #include "third_party/WebKit/public/web/WebFrame.h"
26 #include "third_party/WebKit/public/web/WebLocalFrame.h"
27 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
28 #include "third_party/WebKit/public/web/WebView.h"
29 #include "v8/include/v8.h"
31 using content::V8ValueConverter
;
35 // A map from view instance ID to view object (stored via weak V8 reference).
36 // Views are registered into this map via
37 // GuestViewInternalCustomBindings::RegisterView(), and accessed via
38 // GuestViewInternalCustomBindings::GetViewFromID().
39 using ViewMap
= std::map
<int, v8::Global
<v8::Object
>*>;
40 static base::LazyInstance
<ViewMap
> weak_view_map
= LAZY_INSTANCE_INITIALIZER
;
44 namespace extensions
{
48 content::RenderFrame
* GetRenderFrame(v8::Handle
<v8::Value
> value
) {
49 v8::Local
<v8::Context
> context
=
50 v8::Local
<v8::Object
>::Cast(value
)->CreationContext();
51 if (context
.IsEmpty())
53 blink::WebLocalFrame
* frame
= blink::WebLocalFrame::frameForContext(context
);
56 return content::RenderFrame::FromWebFrame(frame
);
61 GuestViewInternalCustomBindings::GuestViewInternalCustomBindings(
62 ScriptContext
* context
)
63 : ObjectBackedNativeHandler(context
) {
64 RouteFunction("AttachGuest",
65 base::Bind(&GuestViewInternalCustomBindings::AttachGuest
,
66 base::Unretained(this)));
67 RouteFunction("DetachGuest",
68 base::Bind(&GuestViewInternalCustomBindings::DetachGuest
,
69 base::Unretained(this)));
70 RouteFunction("AttachIframeGuest",
71 base::Bind(&GuestViewInternalCustomBindings::AttachIframeGuest
,
72 base::Unretained(this)));
73 RouteFunction("DestroyContainer",
74 base::Bind(&GuestViewInternalCustomBindings::DestroyContainer
,
75 base::Unretained(this)));
76 RouteFunction("GetContentWindow",
77 base::Bind(&GuestViewInternalCustomBindings::GetContentWindow
,
78 base::Unretained(this)));
79 RouteFunction("GetViewFromID",
80 base::Bind(&GuestViewInternalCustomBindings::GetViewFromID
,
81 base::Unretained(this)));
83 "RegisterDestructionCallback",
84 base::Bind(&GuestViewInternalCustomBindings::RegisterDestructionCallback
,
85 base::Unretained(this)));
87 "RegisterElementResizeCallback",
89 &GuestViewInternalCustomBindings::RegisterElementResizeCallback
,
90 base::Unretained(this)));
91 RouteFunction("RegisterView",
92 base::Bind(&GuestViewInternalCustomBindings::RegisterView
,
93 base::Unretained(this)));
96 base::Bind(&GuestViewInternalCustomBindings::RunWithGesture
,
97 base::Unretained(this)));
100 GuestViewInternalCustomBindings::~GuestViewInternalCustomBindings() {}
103 void GuestViewInternalCustomBindings::ResetMapEntry(
104 const v8::WeakCallbackInfo
<int>& data
) {
105 int* param
= data
.GetParameter();
106 int view_instance_id
= *param
;
108 ViewMap
& view_map
= weak_view_map
.Get();
109 auto entry
= view_map
.find(view_instance_id
);
110 if (entry
== view_map
.end())
113 // V8 says we need to explicitly reset weak handles from their callbacks.
114 // It is not implicit as one might expect.
115 entry
->second
->Reset();
116 delete entry
->second
;
117 view_map
.erase(entry
);
119 // Let the GuestViewManager know that a GuestView has been garbage collected.
120 content::RenderThread::Get()->Send(
121 new GuestViewHostMsg_ViewGarbageCollected(view_instance_id
));
124 void GuestViewInternalCustomBindings::AttachGuest(
125 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
126 // Allow for an optional callback parameter.
127 CHECK(args
.Length() >= 3 && args
.Length() <= 4);
128 // Element Instance ID.
129 CHECK(args
[0]->IsInt32());
130 // Guest Instance ID.
131 CHECK(args
[1]->IsInt32());
132 // Attach Parameters.
133 CHECK(args
[2]->IsObject());
134 // Optional Callback Function.
135 CHECK(args
.Length() < 4 || args
[3]->IsFunction());
137 int element_instance_id
= args
[0]->Int32Value();
138 // An element instance ID uniquely identifies a GuestViewContainer.
139 auto guest_view_container
=
140 guest_view::GuestViewContainer::FromID(element_instance_id
);
142 // TODO(fsamuel): Should we be reporting an error if the element instance ID
144 if (!guest_view_container
)
147 int guest_instance_id
= args
[1]->Int32Value();
149 scoped_ptr
<base::DictionaryValue
> params
;
151 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
152 scoped_ptr
<base::Value
> params_as_value(
153 converter
->FromV8Value(args
[2], context()->v8_context()));
154 CHECK(params_as_value
->IsType(base::Value::TYPE_DICTIONARY
));
156 static_cast<base::DictionaryValue
*>(params_as_value
.release()));
159 // Add flag to |params| to indicate that the element size is specified in
161 params
->SetBoolean(guest_view::kElementSizeIsLogical
, true);
163 linked_ptr
<guest_view::GuestViewRequest
> request(
164 new guest_view::GuestViewAttachRequest(
165 guest_view_container
, guest_instance_id
, params
.Pass(),
166 args
.Length() == 4 ? args
[3].As
<v8::Function
>()
167 : v8::Local
<v8::Function
>(),
169 guest_view_container
->IssueRequest(request
);
171 args
.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
174 void GuestViewInternalCustomBindings::DetachGuest(
175 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
176 // Allow for an optional callback parameter.
177 CHECK(args
.Length() >= 1 && args
.Length() <= 2);
178 // Element Instance ID.
179 CHECK(args
[0]->IsInt32());
180 // Optional Callback Function.
181 CHECK(args
.Length() < 2 || args
[1]->IsFunction());
183 int element_instance_id
= args
[0]->Int32Value();
184 // An element instance ID uniquely identifies a GuestViewContainer.
185 auto guest_view_container
=
186 guest_view::GuestViewContainer::FromID(element_instance_id
);
188 // TODO(fsamuel): Should we be reporting an error if the element instance ID
190 if (!guest_view_container
)
193 linked_ptr
<guest_view::GuestViewRequest
> request(
194 new guest_view::GuestViewDetachRequest(
195 guest_view_container
, args
.Length() == 2 ? args
[1].As
<v8::Function
>()
196 : v8::Local
<v8::Function
>(),
198 guest_view_container
->IssueRequest(request
);
200 args
.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
203 void GuestViewInternalCustomBindings::AttachIframeGuest(
204 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
205 // Allow for an optional callback parameter.
206 const int num_required_params
= 4;
207 CHECK(args
.Length() >= num_required_params
&&
208 args
.Length() <= (num_required_params
+ 1));
209 // Element Instance ID.
210 CHECK(args
[0]->IsInt32());
211 // Guest Instance ID.
212 CHECK(args
[1]->IsInt32());
213 // Attach Parameters.
214 CHECK(args
[2]->IsObject());
215 // <iframe>.contentWindow.
216 CHECK(args
[3]->IsObject());
217 // Optional Callback Function.
218 CHECK(args
.Length() <= num_required_params
||
219 args
[num_required_params
]->IsFunction());
221 int element_instance_id
= args
[0]->Int32Value();
222 int guest_instance_id
= args
[1]->Int32Value();
224 scoped_ptr
<base::DictionaryValue
> params
;
226 scoped_ptr
<V8ValueConverter
> converter(V8ValueConverter::create());
227 scoped_ptr
<base::Value
> params_as_value(
228 converter
->FromV8Value(args
[2], context()->v8_context()));
229 CHECK(params_as_value
->IsType(base::Value::TYPE_DICTIONARY
));
231 static_cast<base::DictionaryValue
*>(params_as_value
.release()));
234 // Add flag to |params| to indicate that the element size is specified in
236 params
->SetBoolean(guest_view::kElementSizeIsLogical
, true);
238 content::RenderFrame
* render_frame
= GetRenderFrame(args
[3]);
239 blink::WebLocalFrame
* frame
= render_frame
->GetWebFrame();
241 // Parent must exist.
242 blink::WebFrame
* parent_frame
= frame
->parent();
243 DCHECK(parent_frame
);
244 DCHECK(parent_frame
->isWebLocalFrame());
246 content::RenderFrame
* embedder_parent_frame
=
247 content::RenderFrame::FromWebFrame(parent_frame
);
249 // Create a GuestViewContainer if it does not exist.
250 // An element instance ID uniquely identifies an IframeGuestViewContainer
251 // within a RenderView.
252 auto* guest_view_container
=
253 static_cast<guest_view::IframeGuestViewContainer
*>(
254 guest_view::GuestViewContainer::FromID(element_instance_id
));
255 // This is the first time we hear about the |element_instance_id|.
256 DCHECK(!guest_view_container
);
257 // The <webview> element's GC takes ownership of |guest_view_container|.
258 guest_view_container
=
259 new guest_view::IframeGuestViewContainer(embedder_parent_frame
);
260 guest_view_container
->SetElementInstanceID(element_instance_id
);
262 linked_ptr
<guest_view::GuestViewRequest
> request(
263 new guest_view::GuestViewAttachIframeRequest(
264 guest_view_container
, render_frame
->GetRoutingID(), guest_instance_id
,
265 params
.Pass(), args
.Length() == (num_required_params
+ 1)
266 ? args
[num_required_params
].As
<v8::Function
>()
267 : v8::Local
<v8::Function
>(),
269 guest_view_container
->IssueRequest(request
);
271 args
.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
274 void GuestViewInternalCustomBindings::DestroyContainer(
275 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
276 args
.GetReturnValue().SetNull();
278 if (args
.Length() != 1)
281 // Element Instance ID.
282 if (!args
[0]->IsInt32())
285 int element_instance_id
= args
[0]->Int32Value();
286 auto* guest_view_container
=
287 guest_view::GuestViewContainer::FromID(element_instance_id
);
288 if (!guest_view_container
)
291 // Note: |guest_view_container| is deleted.
292 // GuestViewContainer::DidDestroyElement() currently also destroys
293 // a GuestViewContainer. That won't be necessary once GuestViewContainer
294 // always runs w/o plugin.
295 guest_view_container
->Destroy(false /* embedder_frame_destroyed */);
298 void GuestViewInternalCustomBindings::GetContentWindow(
299 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
300 // Default to returning null.
301 args
.GetReturnValue().SetNull();
303 if (args
.Length() != 1)
306 // The routing ID for the RenderView.
307 if (!args
[0]->IsInt32())
310 int view_id
= args
[0]->Int32Value();
311 if (view_id
== MSG_ROUTING_NONE
)
314 content::RenderView
* view
= content::RenderView::FromRoutingID(view_id
);
318 blink::WebFrame
* frame
= view
->GetWebView()->mainFrame();
319 v8::Local
<v8::Value
> window
= frame
->mainWorldScriptContext()->Global();
320 args
.GetReturnValue().Set(window
);
323 void GuestViewInternalCustomBindings::GetViewFromID(
324 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
325 // Default to returning null.
326 args
.GetReturnValue().SetNull();
327 // There is one argument.
328 CHECK(args
.Length() == 1);
330 CHECK(args
[0]->IsInt32());
331 int view_id
= args
[0]->Int32Value();
333 ViewMap
& view_map
= weak_view_map
.Get();
334 auto map_entry
= view_map
.find(view_id
);
335 if (map_entry
== view_map
.end())
338 auto return_object
= v8::Handle
<v8::Object
>::New(args
.GetIsolate(),
340 args
.GetReturnValue().Set(return_object
);
343 void GuestViewInternalCustomBindings::RegisterDestructionCallback(
344 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
345 // There are two parameters.
346 CHECK(args
.Length() == 2);
347 // Element Instance ID.
348 CHECK(args
[0]->IsInt32());
349 // Callback function.
350 CHECK(args
[1]->IsFunction());
352 int element_instance_id
= args
[0]->Int32Value();
353 // An element instance ID uniquely identifies a GuestViewContainer within a
355 auto* guest_view_container
=
356 guest_view::GuestViewContainer::FromID(element_instance_id
);
357 if (!guest_view_container
)
360 guest_view_container
->RegisterDestructionCallback(args
[1].As
<v8::Function
>(),
363 args
.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
366 void GuestViewInternalCustomBindings::RegisterElementResizeCallback(
367 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
368 // There are two parameters.
369 CHECK(args
.Length() == 2);
370 // Element Instance ID.
371 CHECK(args
[0]->IsInt32());
372 // Callback function.
373 CHECK(args
[1]->IsFunction());
375 int element_instance_id
= args
[0]->Int32Value();
376 // An element instance ID uniquely identifies a ExtensionsGuestViewContainer
377 // within a RenderView.
378 auto guest_view_container
= static_cast<ExtensionsGuestViewContainer
*>(
379 guest_view::GuestViewContainer::FromID(element_instance_id
));
380 if (!guest_view_container
)
383 guest_view_container
->RegisterElementResizeCallback(
384 args
[1].As
<v8::Function
>(), args
.GetIsolate());
386 args
.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
389 void GuestViewInternalCustomBindings::RegisterView(
390 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
391 // There are three parameters.
392 CHECK(args
.Length() == 3);
394 CHECK(args
[0]->IsInt32());
396 CHECK(args
[1]->IsObject());
397 // View type (e.g. "webview").
398 CHECK(args
[2]->IsString());
400 // A reference to the view object is stored in |weak_view_map| using its view
401 // ID as the key. The reference is made weak so that it will not extend the
402 // lifetime of the object.
403 int view_instance_id
= args
[0]->Int32Value();
405 new v8::Global
<v8::Object
>(args
.GetIsolate(), args
[1].As
<v8::Object
>());
406 weak_view_map
.Get().insert(std::make_pair(view_instance_id
, object
));
408 // The |view_instance_id| is given to the SetWeak callback so that that view's
409 // entry in |weak_view_map| can be cleared when the view object is garbage
411 object
->SetWeak(new int(view_instance_id
),
412 &GuestViewInternalCustomBindings::ResetMapEntry
,
413 v8::WeakCallbackType::kParameter
);
415 // Let the GuestViewManager know that a GuestView has been created.
416 const std::string
& view_type
= *v8::String::Utf8Value(args
[2]);
417 content::RenderThread::Get()->Send(
418 new GuestViewHostMsg_ViewCreated(view_instance_id
, view_type
));
421 void GuestViewInternalCustomBindings::RunWithGesture(
422 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
423 // Gesture is required to request fullscreen.
424 blink::WebScopedUserGesture user_gesture
;
425 CHECK_EQ(args
.Length(), 1);
426 CHECK(args
[0]->IsFunction());
427 v8::Local
<v8::Value
> no_args
;
428 context()->CallFunction(v8::Local
<v8::Function
>::Cast(args
[0]), 0, &no_args
);
431 } // namespace extensions