// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/renderer/guest_view/guest_view_internal_custom_bindings.h" #include #include #include "base/bind.h" #include "components/guest_view/common/guest_view_constants.h" #include "components/guest_view/common/guest_view_messages.h" #include "components/guest_view/renderer/guest_view_request.h" #include "components/guest_view/renderer/iframe_guest_view_container.h" #include "components/guest_view/renderer/iframe_guest_view_request.h" #include "content/public/child/v8_value_converter.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "extensions/common/extension.h" #include "extensions/common/extension_messages.h" #include "extensions/common/guest_view/extensions_guest_view_messages.h" #include "extensions/renderer/guest_view/extensions_guest_view_container.h" #include "extensions/renderer/script_context.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebRemoteFrame.h" #include "third_party/WebKit/public/web/WebScopedUserGesture.h" #include "third_party/WebKit/public/web/WebView.h" #include "v8/include/v8.h" using content::V8ValueConverter; namespace { // A map from view instance ID to view object (stored via weak V8 reference). // Views are registered into this map via // GuestViewInternalCustomBindings::RegisterView(), and accessed via // GuestViewInternalCustomBindings::GetViewFromID(). using ViewMap = std::map*>; static base::LazyInstance weak_view_map = LAZY_INSTANCE_INITIALIZER; } // namespace namespace extensions { namespace { content::RenderFrame* GetRenderFrame(v8::Handle value) { v8::Local context = v8::Local::Cast(value)->CreationContext(); if (context.IsEmpty()) return nullptr; blink::WebLocalFrame* frame = blink::WebLocalFrame::frameForContext(context); if (!frame) return nullptr; return content::RenderFrame::FromWebFrame(frame); } } // namespace GuestViewInternalCustomBindings::GuestViewInternalCustomBindings( ScriptContext* context) : ObjectBackedNativeHandler(context) { RouteFunction("AttachGuest", base::Bind(&GuestViewInternalCustomBindings::AttachGuest, base::Unretained(this))); RouteFunction("DetachGuest", base::Bind(&GuestViewInternalCustomBindings::DetachGuest, base::Unretained(this))); RouteFunction("AttachIframeGuest", base::Bind(&GuestViewInternalCustomBindings::AttachIframeGuest, base::Unretained(this))); RouteFunction("DestroyContainer", base::Bind(&GuestViewInternalCustomBindings::DestroyContainer, base::Unretained(this))); RouteFunction("GetContentWindow", base::Bind(&GuestViewInternalCustomBindings::GetContentWindow, base::Unretained(this))); RouteFunction("GetViewFromID", base::Bind(&GuestViewInternalCustomBindings::GetViewFromID, base::Unretained(this))); RouteFunction( "RegisterDestructionCallback", base::Bind(&GuestViewInternalCustomBindings::RegisterDestructionCallback, base::Unretained(this))); RouteFunction( "RegisterElementResizeCallback", base::Bind( &GuestViewInternalCustomBindings::RegisterElementResizeCallback, base::Unretained(this))); RouteFunction("RegisterView", base::Bind(&GuestViewInternalCustomBindings::RegisterView, base::Unretained(this))); RouteFunction( "RunWithGesture", base::Bind(&GuestViewInternalCustomBindings::RunWithGesture, base::Unretained(this))); } GuestViewInternalCustomBindings::~GuestViewInternalCustomBindings() {} // static void GuestViewInternalCustomBindings::ResetMapEntry( const v8::WeakCallbackInfo& data) { int* param = data.GetParameter(); int view_instance_id = *param; delete param; ViewMap& view_map = weak_view_map.Get(); auto entry = view_map.find(view_instance_id); if (entry == view_map.end()) return; // V8 says we need to explicitly reset weak handles from their callbacks. // It is not implicit as one might expect. entry->second->Reset(); delete entry->second; view_map.erase(entry); // Let the GuestViewManager know that a GuestView has been garbage collected. content::RenderThread::Get()->Send( new GuestViewHostMsg_ViewGarbageCollected(view_instance_id)); } void GuestViewInternalCustomBindings::AttachGuest( const v8::FunctionCallbackInfo& args) { // Allow for an optional callback parameter. CHECK(args.Length() >= 3 && args.Length() <= 4); // Element Instance ID. CHECK(args[0]->IsInt32()); // Guest Instance ID. CHECK(args[1]->IsInt32()); // Attach Parameters. CHECK(args[2]->IsObject()); // Optional Callback Function. CHECK(args.Length() < 4 || args[3]->IsFunction()); int element_instance_id = args[0]->Int32Value(); // An element instance ID uniquely identifies a GuestViewContainer. auto guest_view_container = guest_view::GuestViewContainer::FromID(element_instance_id); // TODO(fsamuel): Should we be reporting an error if the element instance ID // is invalid? if (!guest_view_container) return; int guest_instance_id = args[1]->Int32Value(); scoped_ptr params; { scoped_ptr converter(V8ValueConverter::create()); scoped_ptr params_as_value( converter->FromV8Value(args[2], context()->v8_context())); params = base::DictionaryValue::From(std::move(params_as_value)); CHECK(params); } // Add flag to |params| to indicate that the element size is specified in // logical units. params->SetBoolean(guest_view::kElementSizeIsLogical, true); linked_ptr request( new guest_view::GuestViewAttachRequest( guest_view_container, guest_instance_id, std::move(params), args.Length() == 4 ? args[3].As() : v8::Local(), args.GetIsolate())); guest_view_container->IssueRequest(request); args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true)); } void GuestViewInternalCustomBindings::DetachGuest( const v8::FunctionCallbackInfo& args) { // Allow for an optional callback parameter. CHECK(args.Length() >= 1 && args.Length() <= 2); // Element Instance ID. CHECK(args[0]->IsInt32()); // Optional Callback Function. CHECK(args.Length() < 2 || args[1]->IsFunction()); int element_instance_id = args[0]->Int32Value(); // An element instance ID uniquely identifies a GuestViewContainer. auto guest_view_container = guest_view::GuestViewContainer::FromID(element_instance_id); // TODO(fsamuel): Should we be reporting an error if the element instance ID // is invalid? if (!guest_view_container) return; linked_ptr request( new guest_view::GuestViewDetachRequest( guest_view_container, args.Length() == 2 ? args[1].As() : v8::Local(), args.GetIsolate())); guest_view_container->IssueRequest(request); args.GetReturnValue().Set(v8::Boolean::New(context()->isolate(), true)); } void GuestViewInternalCustomBindings::AttachIframeGuest( const v8::FunctionCallbackInfo& args) { // Allow for an optional callback parameter. const int num_required_params = 4; CHECK(args.Length() >= num_required_params && args.Length() <= (num_required_params + 1)); // Element Instance ID. CHECK(args[0]->IsInt32()); // Guest Instance ID. CHECK(args[1]->IsInt32()); // Attach Parameters. CHECK(args[2]->IsObject()); //