Prepare GuestView for removing swapped out RenderFrame.
[chromium-blink-merge.git] / extensions / renderer / guest_view / guest_view_internal_custom_bindings.cc
blobee243bf6922e528e5461ab0762d662a64929378b
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"
7 #include <string>
8 #include <utility>
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/WebRemoteFrame.h"
28 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
29 #include "third_party/WebKit/public/web/WebView.h"
30 #include "v8/include/v8.h"
32 using content::V8ValueConverter;
34 namespace {
36 // A map from view instance ID to view object (stored via weak V8 reference).
37 // Views are registered into this map via
38 // GuestViewInternalCustomBindings::RegisterView(), and accessed via
39 // GuestViewInternalCustomBindings::GetViewFromID().
40 using ViewMap = std::map<int, v8::Global<v8::Object>*>;
41 static base::LazyInstance<ViewMap> weak_view_map = LAZY_INSTANCE_INITIALIZER;
43 } // namespace
45 namespace extensions {
47 namespace {
49 content::RenderFrame* GetRenderFrame(v8::Handle<v8::Value> value) {
50 v8::Local<v8::Context> context =
51 v8::Local<v8::Object>::Cast(value)->CreationContext();
52 if (context.IsEmpty())
53 return nullptr;
54 blink::WebLocalFrame* frame = blink::WebLocalFrame::frameForContext(context);
55 if (!frame)
56 return nullptr;
57 return content::RenderFrame::FromWebFrame(frame);
60 } // namespace
62 GuestViewInternalCustomBindings::GuestViewInternalCustomBindings(
63 ScriptContext* context)
64 : ObjectBackedNativeHandler(context) {
65 RouteFunction("AttachGuest",
66 base::Bind(&GuestViewInternalCustomBindings::AttachGuest,
67 base::Unretained(this)));
68 RouteFunction("DetachGuest",
69 base::Bind(&GuestViewInternalCustomBindings::DetachGuest,
70 base::Unretained(this)));
71 RouteFunction("AttachIframeGuest",
72 base::Bind(&GuestViewInternalCustomBindings::AttachIframeGuest,
73 base::Unretained(this)));
74 RouteFunction("DestroyContainer",
75 base::Bind(&GuestViewInternalCustomBindings::DestroyContainer,
76 base::Unretained(this)));
77 RouteFunction("GetContentWindow",
78 base::Bind(&GuestViewInternalCustomBindings::GetContentWindow,
79 base::Unretained(this)));
80 RouteFunction("GetViewFromID",
81 base::Bind(&GuestViewInternalCustomBindings::GetViewFromID,
82 base::Unretained(this)));
83 RouteFunction(
84 "RegisterDestructionCallback",
85 base::Bind(&GuestViewInternalCustomBindings::RegisterDestructionCallback,
86 base::Unretained(this)));
87 RouteFunction(
88 "RegisterElementResizeCallback",
89 base::Bind(
90 &GuestViewInternalCustomBindings::RegisterElementResizeCallback,
91 base::Unretained(this)));
92 RouteFunction("RegisterView",
93 base::Bind(&GuestViewInternalCustomBindings::RegisterView,
94 base::Unretained(this)));
95 RouteFunction(
96 "RunWithGesture",
97 base::Bind(&GuestViewInternalCustomBindings::RunWithGesture,
98 base::Unretained(this)));
101 GuestViewInternalCustomBindings::~GuestViewInternalCustomBindings() {}
103 // static
104 void GuestViewInternalCustomBindings::ResetMapEntry(
105 const v8::WeakCallbackInfo<int>& data) {
106 int* param = data.GetParameter();
107 int view_instance_id = *param;
108 delete param;
109 ViewMap& view_map = weak_view_map.Get();
110 auto entry = view_map.find(view_instance_id);
111 if (entry == view_map.end())
112 return;
114 // V8 says we need to explicitly reset weak handles from their callbacks.
115 // It is not implicit as one might expect.
116 entry->second->Reset();
117 delete entry->second;
118 view_map.erase(entry);
120 // Let the GuestViewManager know that a GuestView has been garbage collected.
121 content::RenderThread::Get()->Send(
122 new GuestViewHostMsg_ViewGarbageCollected(view_instance_id));
125 void GuestViewInternalCustomBindings::AttachGuest(
126 const v8::FunctionCallbackInfo<v8::Value>& args) {
127 // Allow for an optional callback parameter.
128 CHECK(args.Length() >= 3 && args.Length() <= 4);
129 // Element Instance ID.
130 CHECK(args[0]->IsInt32());
131 // Guest Instance ID.
132 CHECK(args[1]->IsInt32());
133 // Attach Parameters.
134 CHECK(args[2]->IsObject());
135 // Optional Callback Function.
136 CHECK(args.Length() < 4 || args[3]->IsFunction());
138 int element_instance_id = args[0]->Int32Value();
139 // An element instance ID uniquely identifies a GuestViewContainer.
140 auto guest_view_container =
141 guest_view::GuestViewContainer::FromID(element_instance_id);
143 // TODO(fsamuel): Should we be reporting an error if the element instance ID
144 // is invalid?
145 if (!guest_view_container)
146 return;
148 int guest_instance_id = args[1]->Int32Value();
150 scoped_ptr<base::DictionaryValue> params;
152 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
153 scoped_ptr<base::Value> params_as_value(
154 converter->FromV8Value(args[2], context()->v8_context()));
155 params = base::DictionaryValue::From(params_as_value.Pass());
156 CHECK(params);
159 // Add flag to |params| to indicate that the element size is specified in
160 // logical units.
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>(),
168 args.GetIsolate()));
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
189 // is invalid?
190 if (!guest_view_container)
191 return;
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>(),
197 args.GetIsolate()));
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 params = base::DictionaryValue::From(params_as_value.Pass());
230 CHECK(params);
233 // Add flag to |params| to indicate that the element size is specified in
234 // logical units.
235 params->SetBoolean(guest_view::kElementSizeIsLogical, true);
237 content::RenderFrame* render_frame = GetRenderFrame(args[3]);
238 blink::WebLocalFrame* frame = render_frame->GetWebFrame();
240 // Parent must exist.
241 blink::WebFrame* parent_frame = frame->parent();
242 DCHECK(parent_frame);
243 DCHECK(parent_frame->isWebLocalFrame());
245 content::RenderFrame* embedder_parent_frame =
246 content::RenderFrame::FromWebFrame(parent_frame);
248 // Create a GuestViewContainer if it does not exist.
249 // An element instance ID uniquely identifies an IframeGuestViewContainer
250 // within a RenderView.
251 auto* guest_view_container =
252 static_cast<guest_view::IframeGuestViewContainer*>(
253 guest_view::GuestViewContainer::FromID(element_instance_id));
254 // This is the first time we hear about the |element_instance_id|.
255 DCHECK(!guest_view_container);
256 // The <webview> element's GC takes ownership of |guest_view_container|.
257 guest_view_container =
258 new guest_view::IframeGuestViewContainer(embedder_parent_frame);
259 guest_view_container->SetElementInstanceID(element_instance_id);
261 linked_ptr<guest_view::GuestViewRequest> request(
262 new guest_view::GuestViewAttachIframeRequest(
263 guest_view_container, render_frame->GetRoutingID(), guest_instance_id,
264 params.Pass(), args.Length() == (num_required_params + 1)
265 ? args[num_required_params].As<v8::Function>()
266 : v8::Local<v8::Function>(),
267 args.GetIsolate()));
268 guest_view_container->IssueRequest(request);
270 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
273 void GuestViewInternalCustomBindings::DestroyContainer(
274 const v8::FunctionCallbackInfo<v8::Value>& args) {
275 args.GetReturnValue().SetNull();
277 if (args.Length() != 1)
278 return;
280 // Element Instance ID.
281 if (!args[0]->IsInt32())
282 return;
284 int element_instance_id = args[0]->Int32Value();
285 auto* guest_view_container =
286 guest_view::GuestViewContainer::FromID(element_instance_id);
287 if (!guest_view_container)
288 return;
290 // Note: |guest_view_container| is deleted.
291 // GuestViewContainer::DidDestroyElement() currently also destroys
292 // a GuestViewContainer. That won't be necessary once GuestViewContainer
293 // always runs w/o plugin.
294 guest_view_container->Destroy(false /* embedder_frame_destroyed */);
297 void GuestViewInternalCustomBindings::GetContentWindow(
298 const v8::FunctionCallbackInfo<v8::Value>& args) {
299 // Default to returning null.
300 args.GetReturnValue().SetNull();
302 if (args.Length() != 1)
303 return;
305 // The routing ID for the RenderView.
306 if (!args[0]->IsInt32())
307 return;
309 int view_id = args[0]->Int32Value();
310 if (view_id == MSG_ROUTING_NONE)
311 return;
313 content::RenderView* view = content::RenderView::FromRoutingID(view_id);
314 if (!view)
315 return;
317 blink::WebFrame* frame = view->GetWebView()->mainFrame();
318 // TODO(lazyboy,nasko): The WebLocalFrame branch is not used when running
319 // on top of out-of-process iframes. Remove it once the code is converted.
320 v8::Local<v8::Value> window;
321 if (frame->isWebLocalFrame()) {
322 window = frame->mainWorldScriptContext()->Global();
323 } else {
324 window =
325 frame->toWebRemoteFrame()->deprecatedMainWorldScriptContext()->Global();
327 args.GetReturnValue().Set(window);
330 void GuestViewInternalCustomBindings::GetViewFromID(
331 const v8::FunctionCallbackInfo<v8::Value>& args) {
332 // Default to returning null.
333 args.GetReturnValue().SetNull();
334 // There is one argument.
335 CHECK(args.Length() == 1);
336 // The view ID.
337 CHECK(args[0]->IsInt32());
338 int view_id = args[0]->Int32Value();
340 ViewMap& view_map = weak_view_map.Get();
341 auto map_entry = view_map.find(view_id);
342 if (map_entry == view_map.end())
343 return;
345 auto return_object = v8::Handle<v8::Object>::New(args.GetIsolate(),
346 *map_entry->second);
347 args.GetReturnValue().Set(return_object);
350 void GuestViewInternalCustomBindings::RegisterDestructionCallback(
351 const v8::FunctionCallbackInfo<v8::Value>& args) {
352 // There are two parameters.
353 CHECK(args.Length() == 2);
354 // Element Instance ID.
355 CHECK(args[0]->IsInt32());
356 // Callback function.
357 CHECK(args[1]->IsFunction());
359 int element_instance_id = args[0]->Int32Value();
360 // An element instance ID uniquely identifies a GuestViewContainer within a
361 // RenderView.
362 auto* guest_view_container =
363 guest_view::GuestViewContainer::FromID(element_instance_id);
364 if (!guest_view_container)
365 return;
367 guest_view_container->RegisterDestructionCallback(args[1].As<v8::Function>(),
368 args.GetIsolate());
370 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
373 void GuestViewInternalCustomBindings::RegisterElementResizeCallback(
374 const v8::FunctionCallbackInfo<v8::Value>& args) {
375 // There are two parameters.
376 CHECK(args.Length() == 2);
377 // Element Instance ID.
378 CHECK(args[0]->IsInt32());
379 // Callback function.
380 CHECK(args[1]->IsFunction());
382 int element_instance_id = args[0]->Int32Value();
383 // An element instance ID uniquely identifies a ExtensionsGuestViewContainer
384 // within a RenderView.
385 auto guest_view_container = static_cast<ExtensionsGuestViewContainer*>(
386 guest_view::GuestViewContainer::FromID(element_instance_id));
387 if (!guest_view_container)
388 return;
390 guest_view_container->RegisterElementResizeCallback(
391 args[1].As<v8::Function>(), args.GetIsolate());
393 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
396 void GuestViewInternalCustomBindings::RegisterView(
397 const v8::FunctionCallbackInfo<v8::Value>& args) {
398 // There are three parameters.
399 CHECK(args.Length() == 3);
400 // View Instance ID.
401 CHECK(args[0]->IsInt32());
402 // View element.
403 CHECK(args[1]->IsObject());
404 // View type (e.g. "webview").
405 CHECK(args[2]->IsString());
407 // A reference to the view object is stored in |weak_view_map| using its view
408 // ID as the key. The reference is made weak so that it will not extend the
409 // lifetime of the object.
410 int view_instance_id = args[0]->Int32Value();
411 auto object =
412 new v8::Global<v8::Object>(args.GetIsolate(), args[1].As<v8::Object>());
413 weak_view_map.Get().insert(std::make_pair(view_instance_id, object));
415 // The |view_instance_id| is given to the SetWeak callback so that that view's
416 // entry in |weak_view_map| can be cleared when the view object is garbage
417 // collected.
418 object->SetWeak(new int(view_instance_id),
419 &GuestViewInternalCustomBindings::ResetMapEntry,
420 v8::WeakCallbackType::kParameter);
422 // Let the GuestViewManager know that a GuestView has been created.
423 const std::string& view_type = *v8::String::Utf8Value(args[2]);
424 content::RenderThread::Get()->Send(
425 new GuestViewHostMsg_ViewCreated(view_instance_id, view_type));
428 void GuestViewInternalCustomBindings::RunWithGesture(
429 const v8::FunctionCallbackInfo<v8::Value>& args) {
430 // Gesture is required to request fullscreen.
431 blink::WebScopedUserGesture user_gesture;
432 CHECK_EQ(args.Length(), 1);
433 CHECK(args[0]->IsFunction());
434 v8::Local<v8::Value> no_args;
435 context()->CallFunction(v8::Local<v8::Function>::Cast(args[0]), 0, &no_args);
438 } // namespace extensions