Add scoped_ptr-safe base::Value to Dictionary/List conversion functions.
[chromium-blink-merge.git] / extensions / renderer / guest_view / guest_view_internal_custom_bindings.cc
blob7eb129236551ca24185357a5af7054915f95d7a7
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/WebScopedUserGesture.h"
28 #include "third_party/WebKit/public/web/WebView.h"
29 #include "v8/include/v8.h"
31 using content::V8ValueConverter;
33 namespace {
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;
42 } // namespace
44 namespace extensions {
46 namespace {
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())
52 return nullptr;
53 blink::WebLocalFrame* frame = blink::WebLocalFrame::frameForContext(context);
54 if (!frame)
55 return nullptr;
56 return content::RenderFrame::FromWebFrame(frame);
59 } // namespace
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)));
82 RouteFunction(
83 "RegisterDestructionCallback",
84 base::Bind(&GuestViewInternalCustomBindings::RegisterDestructionCallback,
85 base::Unretained(this)));
86 RouteFunction(
87 "RegisterElementResizeCallback",
88 base::Bind(
89 &GuestViewInternalCustomBindings::RegisterElementResizeCallback,
90 base::Unretained(this)));
91 RouteFunction("RegisterView",
92 base::Bind(&GuestViewInternalCustomBindings::RegisterView,
93 base::Unretained(this)));
94 RouteFunction(
95 "RunWithGesture",
96 base::Bind(&GuestViewInternalCustomBindings::RunWithGesture,
97 base::Unretained(this)));
100 GuestViewInternalCustomBindings::~GuestViewInternalCustomBindings() {}
102 // static
103 void GuestViewInternalCustomBindings::ResetMapEntry(
104 const v8::WeakCallbackInfo<int>& data) {
105 int* param = data.GetParameter();
106 int view_instance_id = *param;
107 delete param;
108 ViewMap& view_map = weak_view_map.Get();
109 auto entry = view_map.find(view_instance_id);
110 if (entry == view_map.end())
111 return;
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
143 // is invalid?
144 if (!guest_view_container)
145 return;
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 params = base::DictionaryValue::From(params_as_value.Pass());
155 CHECK(params);
158 // Add flag to |params| to indicate that the element size is specified in
159 // logical units.
160 params->SetBoolean(guest_view::kElementSizeIsLogical, true);
162 linked_ptr<guest_view::GuestViewRequest> request(
163 new guest_view::GuestViewAttachRequest(
164 guest_view_container, guest_instance_id, params.Pass(),
165 args.Length() == 4 ? args[3].As<v8::Function>()
166 : v8::Local<v8::Function>(),
167 args.GetIsolate()));
168 guest_view_container->IssueRequest(request);
170 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
173 void GuestViewInternalCustomBindings::DetachGuest(
174 const v8::FunctionCallbackInfo<v8::Value>& args) {
175 // Allow for an optional callback parameter.
176 CHECK(args.Length() >= 1 && args.Length() <= 2);
177 // Element Instance ID.
178 CHECK(args[0]->IsInt32());
179 // Optional Callback Function.
180 CHECK(args.Length() < 2 || args[1]->IsFunction());
182 int element_instance_id = args[0]->Int32Value();
183 // An element instance ID uniquely identifies a GuestViewContainer.
184 auto guest_view_container =
185 guest_view::GuestViewContainer::FromID(element_instance_id);
187 // TODO(fsamuel): Should we be reporting an error if the element instance ID
188 // is invalid?
189 if (!guest_view_container)
190 return;
192 linked_ptr<guest_view::GuestViewRequest> request(
193 new guest_view::GuestViewDetachRequest(
194 guest_view_container, args.Length() == 2 ? args[1].As<v8::Function>()
195 : v8::Local<v8::Function>(),
196 args.GetIsolate()));
197 guest_view_container->IssueRequest(request);
199 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
202 void GuestViewInternalCustomBindings::AttachIframeGuest(
203 const v8::FunctionCallbackInfo<v8::Value>& args) {
204 // Allow for an optional callback parameter.
205 const int num_required_params = 4;
206 CHECK(args.Length() >= num_required_params &&
207 args.Length() <= (num_required_params + 1));
208 // Element Instance ID.
209 CHECK(args[0]->IsInt32());
210 // Guest Instance ID.
211 CHECK(args[1]->IsInt32());
212 // Attach Parameters.
213 CHECK(args[2]->IsObject());
214 // <iframe>.contentWindow.
215 CHECK(args[3]->IsObject());
216 // Optional Callback Function.
217 CHECK(args.Length() <= num_required_params ||
218 args[num_required_params]->IsFunction());
220 int element_instance_id = args[0]->Int32Value();
221 int guest_instance_id = args[1]->Int32Value();
223 scoped_ptr<base::DictionaryValue> params;
225 scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
226 scoped_ptr<base::Value> params_as_value(
227 converter->FromV8Value(args[2], context()->v8_context()));
228 params = base::DictionaryValue::From(params_as_value.Pass());
229 CHECK(params);
232 // Add flag to |params| to indicate that the element size is specified in
233 // logical units.
234 params->SetBoolean(guest_view::kElementSizeIsLogical, true);
236 content::RenderFrame* render_frame = GetRenderFrame(args[3]);
237 blink::WebLocalFrame* frame = render_frame->GetWebFrame();
239 // Parent must exist.
240 blink::WebFrame* parent_frame = frame->parent();
241 DCHECK(parent_frame);
242 DCHECK(parent_frame->isWebLocalFrame());
244 content::RenderFrame* embedder_parent_frame =
245 content::RenderFrame::FromWebFrame(parent_frame);
247 // Create a GuestViewContainer if it does not exist.
248 // An element instance ID uniquely identifies an IframeGuestViewContainer
249 // within a RenderView.
250 auto* guest_view_container =
251 static_cast<guest_view::IframeGuestViewContainer*>(
252 guest_view::GuestViewContainer::FromID(element_instance_id));
253 // This is the first time we hear about the |element_instance_id|.
254 DCHECK(!guest_view_container);
255 // The <webview> element's GC takes ownership of |guest_view_container|.
256 guest_view_container =
257 new guest_view::IframeGuestViewContainer(embedder_parent_frame);
258 guest_view_container->SetElementInstanceID(element_instance_id);
260 linked_ptr<guest_view::GuestViewRequest> request(
261 new guest_view::GuestViewAttachIframeRequest(
262 guest_view_container, render_frame->GetRoutingID(), guest_instance_id,
263 params.Pass(), args.Length() == (num_required_params + 1)
264 ? args[num_required_params].As<v8::Function>()
265 : v8::Local<v8::Function>(),
266 args.GetIsolate()));
267 guest_view_container->IssueRequest(request);
269 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
272 void GuestViewInternalCustomBindings::DestroyContainer(
273 const v8::FunctionCallbackInfo<v8::Value>& args) {
274 args.GetReturnValue().SetNull();
276 if (args.Length() != 1)
277 return;
279 // Element Instance ID.
280 if (!args[0]->IsInt32())
281 return;
283 int element_instance_id = args[0]->Int32Value();
284 auto* guest_view_container =
285 guest_view::GuestViewContainer::FromID(element_instance_id);
286 if (!guest_view_container)
287 return;
289 // Note: |guest_view_container| is deleted.
290 // GuestViewContainer::DidDestroyElement() currently also destroys
291 // a GuestViewContainer. That won't be necessary once GuestViewContainer
292 // always runs w/o plugin.
293 guest_view_container->Destroy(false /* embedder_frame_destroyed */);
296 void GuestViewInternalCustomBindings::GetContentWindow(
297 const v8::FunctionCallbackInfo<v8::Value>& args) {
298 // Default to returning null.
299 args.GetReturnValue().SetNull();
301 if (args.Length() != 1)
302 return;
304 // The routing ID for the RenderView.
305 if (!args[0]->IsInt32())
306 return;
308 int view_id = args[0]->Int32Value();
309 if (view_id == MSG_ROUTING_NONE)
310 return;
312 content::RenderView* view = content::RenderView::FromRoutingID(view_id);
313 if (!view)
314 return;
316 blink::WebFrame* frame = view->GetWebView()->mainFrame();
317 v8::Local<v8::Value> window = frame->mainWorldScriptContext()->Global();
318 args.GetReturnValue().Set(window);
321 void GuestViewInternalCustomBindings::GetViewFromID(
322 const v8::FunctionCallbackInfo<v8::Value>& args) {
323 // Default to returning null.
324 args.GetReturnValue().SetNull();
325 // There is one argument.
326 CHECK(args.Length() == 1);
327 // The view ID.
328 CHECK(args[0]->IsInt32());
329 int view_id = args[0]->Int32Value();
331 ViewMap& view_map = weak_view_map.Get();
332 auto map_entry = view_map.find(view_id);
333 if (map_entry == view_map.end())
334 return;
336 auto return_object = v8::Handle<v8::Object>::New(args.GetIsolate(),
337 *map_entry->second);
338 args.GetReturnValue().Set(return_object);
341 void GuestViewInternalCustomBindings::RegisterDestructionCallback(
342 const v8::FunctionCallbackInfo<v8::Value>& args) {
343 // There are two parameters.
344 CHECK(args.Length() == 2);
345 // Element Instance ID.
346 CHECK(args[0]->IsInt32());
347 // Callback function.
348 CHECK(args[1]->IsFunction());
350 int element_instance_id = args[0]->Int32Value();
351 // An element instance ID uniquely identifies a GuestViewContainer within a
352 // RenderView.
353 auto* guest_view_container =
354 guest_view::GuestViewContainer::FromID(element_instance_id);
355 if (!guest_view_container)
356 return;
358 guest_view_container->RegisterDestructionCallback(args[1].As<v8::Function>(),
359 args.GetIsolate());
361 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
364 void GuestViewInternalCustomBindings::RegisterElementResizeCallback(
365 const v8::FunctionCallbackInfo<v8::Value>& args) {
366 // There are two parameters.
367 CHECK(args.Length() == 2);
368 // Element Instance ID.
369 CHECK(args[0]->IsInt32());
370 // Callback function.
371 CHECK(args[1]->IsFunction());
373 int element_instance_id = args[0]->Int32Value();
374 // An element instance ID uniquely identifies a ExtensionsGuestViewContainer
375 // within a RenderView.
376 auto guest_view_container = static_cast<ExtensionsGuestViewContainer*>(
377 guest_view::GuestViewContainer::FromID(element_instance_id));
378 if (!guest_view_container)
379 return;
381 guest_view_container->RegisterElementResizeCallback(
382 args[1].As<v8::Function>(), args.GetIsolate());
384 args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true));
387 void GuestViewInternalCustomBindings::RegisterView(
388 const v8::FunctionCallbackInfo<v8::Value>& args) {
389 // There are three parameters.
390 CHECK(args.Length() == 3);
391 // View Instance ID.
392 CHECK(args[0]->IsInt32());
393 // View element.
394 CHECK(args[1]->IsObject());
395 // View type (e.g. "webview").
396 CHECK(args[2]->IsString());
398 // A reference to the view object is stored in |weak_view_map| using its view
399 // ID as the key. The reference is made weak so that it will not extend the
400 // lifetime of the object.
401 int view_instance_id = args[0]->Int32Value();
402 auto object =
403 new v8::Global<v8::Object>(args.GetIsolate(), args[1].As<v8::Object>());
404 weak_view_map.Get().insert(std::make_pair(view_instance_id, object));
406 // The |view_instance_id| is given to the SetWeak callback so that that view's
407 // entry in |weak_view_map| can be cleared when the view object is garbage
408 // collected.
409 object->SetWeak(new int(view_instance_id),
410 &GuestViewInternalCustomBindings::ResetMapEntry,
411 v8::WeakCallbackType::kParameter);
413 // Let the GuestViewManager know that a GuestView has been created.
414 const std::string& view_type = *v8::String::Utf8Value(args[2]);
415 content::RenderThread::Get()->Send(
416 new GuestViewHostMsg_ViewCreated(view_instance_id, view_type));
419 void GuestViewInternalCustomBindings::RunWithGesture(
420 const v8::FunctionCallbackInfo<v8::Value>& args) {
421 // Gesture is required to request fullscreen.
422 blink::WebScopedUserGesture user_gesture;
423 CHECK_EQ(args.Length(), 1);
424 CHECK(args[0]->IsFunction());
425 v8::Local<v8::Value> no_args;
426 context()->CallFunction(v8::Local<v8::Function>::Cast(args[0]), 0, &no_args);
429 } // namespace extensions