// Copyright (c) 2012 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 "content/renderer/pepper/pepper_webplugin_impl.h" #include #include #include #include "base/debug/crash_logging.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "content/public/renderer/content_renderer_client.h" #include "content/renderer/pepper/message_channel.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/pepper/plugin_instance_throttler_impl.h" #include "content/renderer/pepper/plugin_module.h" #include "content/renderer/pepper/v8object_var.h" #include "content/renderer/render_frame_impl.h" #include "content/renderer/renderer_blink_platform_impl.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var_tracker.h" #include "third_party/WebKit/public/platform/WebClipboard.h" #include "third_party/WebKit/public/platform/WebCoalescedInputEvent.h" #include "third_party/WebKit/public/platform/WebPoint.h" #include "third_party/WebKit/public/platform/WebRect.h" #include "third_party/WebKit/public/platform/WebSize.h" #include "third_party/WebKit/public/web/WebAssociatedURLLoaderClient.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebPluginContainer.h" #include "third_party/WebKit/public/web/WebPluginParams.h" #include "third_party/WebKit/public/web/WebPrintParams.h" #include "third_party/WebKit/public/web/WebPrintPresetOptions.h" #include "third_party/WebKit/public/web/WebPrintScalingOption.h" #include "url/gurl.h" using ppapi::V8ObjectVar; using blink::WebCanvas; using blink::WebPlugin; using blink::WebPluginContainer; using blink::WebPluginParams; using blink::WebPoint; using blink::WebPrintParams; using blink::WebRect; using blink::WebSize; using blink::WebString; using blink::WebURL; using blink::WebVector; namespace content { struct PepperWebPluginImpl::InitData { scoped_refptr module; RenderFrameImpl* render_frame; std::vector arg_names; std::vector arg_values; GURL url; }; PepperWebPluginImpl::PepperWebPluginImpl( PluginModule* plugin_module, const WebPluginParams& params, RenderFrameImpl* render_frame, std::unique_ptr throttler) : init_data_(new InitData()), full_frame_(params.load_manually), throttler_(std::move(throttler)), instance_object_(PP_MakeUndefined()), container_(nullptr) { DCHECK(plugin_module); init_data_->module = plugin_module; init_data_->render_frame = render_frame; for (size_t i = 0; i < params.attribute_names.size(); ++i) { init_data_->arg_names.push_back(params.attribute_names[i].Utf8()); init_data_->arg_values.push_back(params.attribute_values[i].Utf8()); } init_data_->url = params.url; // Set subresource URL for crash reporting. static base::debug::CrashKeyString* subresource_url = base::debug::AllocateCrashKeyString("subresource_url", base::debug::CrashKeySize::Size256); base::debug::SetCrashKeyString(subresource_url, init_data_->url.spec()); if (throttler_) throttler_->SetWebPlugin(this); } PepperWebPluginImpl::~PepperWebPluginImpl() {} blink::WebPluginContainer* PepperWebPluginImpl::Container() const { return container_; } bool PepperWebPluginImpl::Initialize(WebPluginContainer* container) { DCHECK(container); DCHECK_EQ(this, container->Plugin()); container_ = container; // The plugin delegate may have gone away. instance_ = init_data_->module->CreateInstance( init_data_->render_frame, container, init_data_->url); if (!instance_) return false; if (!instance_->Initialize(init_data_->arg_names, init_data_->arg_values, full_frame_, std::move(throttler_))) { // If |container_| is nullptr, this object has already been synchronously // destroy()-ed during |instance_|'s Initialize call. In that case, we early // exit. We neither create a replacement plugin nor destroy() ourselves. if (!container_) return false; DCHECK(instance_); ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(instance_object_); instance_object_ = PP_MakeUndefined(); instance_->Delete(); instance_ = nullptr; blink::WebPlugin* replacement_plugin = GetContentClient()->renderer()->CreatePluginReplacement( init_data_->render_frame, init_data_->module->path()); if (!replacement_plugin) return false; // The replacement plugin, if it exists, must never fail to initialize. container->SetPlugin(replacement_plugin); CHECK(replacement_plugin->Initialize(container)); DCHECK(container->Plugin() == replacement_plugin); DCHECK(replacement_plugin->Container() == container); // Since the container now owns the replacement plugin instead of this // object, we must schedule ourselves for deletion. Destroy(); return true; } init_data_.reset(); return true; } void PepperWebPluginImpl::Destroy() { container_ = nullptr; if (instance_) { ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(instance_object_); instance_object_ = PP_MakeUndefined(); instance_->Delete(); instance_ = nullptr; } base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); } v8::Local PepperWebPluginImpl::V8ScriptableObject( v8::Isolate* isolate) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See e.g. crbug.com/503401. if (!instance_) return v8::Local(); // Call through the plugin to get its instance object. The plugin should pass // us a reference which we release in destroy(). if (instance_object_.type == PP_VARTYPE_UNDEFINED) instance_object_ = instance_->GetInstanceObject(isolate); // GetInstanceObject talked to the plugin which may have removed the instance // from the DOM, in which case instance_ would be nullptr now. if (!instance_) return v8::Local(); scoped_refptr object_var( V8ObjectVar::FromPPVar(instance_object_)); // If there's an InstanceObject, tell the Instance's MessageChannel to pass // any non-postMessage calls to it. if (object_var) { MessageChannel* message_channel = instance_->message_channel(); if (message_channel) message_channel->SetPassthroughObject(object_var->GetHandle()); } v8::Local result = instance_->GetMessageChannelObject(); return result; } void PepperWebPluginImpl::Paint(WebCanvas* canvas, const WebRect& rect) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_ && !instance_->FlashIsFullscreenOrPending()) instance_->Paint(canvas, plugin_rect_, rect); } void PepperWebPluginImpl::UpdateGeometry( const WebRect& window_rect, const WebRect& clip_rect, const WebRect& unobscured_rect, bool is_visible) { plugin_rect_ = window_rect; if (instance_ && !instance_->FlashIsFullscreenOrPending()) instance_->ViewChanged(plugin_rect_, clip_rect, unobscured_rect); } void PepperWebPluginImpl::UpdateFocus(bool focused, blink::WebFocusType focus_type) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->SetWebKitFocus(focused); } void PepperWebPluginImpl::UpdateVisibility(bool visible) {} blink::WebInputEventResult PepperWebPluginImpl::HandleInputEvent( const blink::WebCoalescedInputEvent& coalesced_event, blink::WebCursorInfo& cursor_info) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_ || instance_->FlashIsFullscreenOrPending()) return blink::WebInputEventResult::kNotHandled; return instance_->HandleCoalescedInputEvent(coalesced_event, &cursor_info) ? blink::WebInputEventResult::kHandledApplication : blink::WebInputEventResult::kNotHandled; } void PepperWebPluginImpl::DidReceiveResponse( const blink::WebURLResponse& response) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return; DCHECK(!instance_->document_loader()); instance_->HandleDocumentLoad(response); } void PepperWebPluginImpl::DidReceiveData(const char* data, int data_length) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return; blink::WebAssociatedURLLoaderClient* document_loader = instance_->document_loader(); if (document_loader) document_loader->DidReceiveData(data, data_length); } void PepperWebPluginImpl::DidFinishLoading() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return; blink::WebAssociatedURLLoaderClient* document_loader = instance_->document_loader(); if (document_loader) document_loader->DidFinishLoading(0.0); } void PepperWebPluginImpl::DidFailLoading(const blink::WebURLError& error) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return; blink::WebAssociatedURLLoaderClient* document_loader = instance_->document_loader(); if (document_loader) document_loader->DidFail(error); } bool PepperWebPluginImpl::HasSelection() const { return !SelectionAsText().IsEmpty(); } WebString PepperWebPluginImpl::SelectionAsText() const { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return WebString(); return WebString::FromUTF16(instance_->GetSelectedText(false)); } WebString PepperWebPluginImpl::SelectionAsMarkup() const { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return WebString(); return WebString::FromUTF16(instance_->GetSelectedText(true)); } bool PepperWebPluginImpl::CanEditText() const { return instance_ && instance_->CanEditText(); } bool PepperWebPluginImpl::ExecuteEditCommand(const blink::WebString& name) { return ExecuteEditCommand(name, WebString()); } bool PepperWebPluginImpl::ExecuteEditCommand(const blink::WebString& name, const blink::WebString& value) { if (!instance_) return false; if (name == "Cut") { if (!HasSelection() || !CanEditText()) return false; blink::Platform::Current()->Clipboard()->WriteHTML( SelectionAsMarkup(), WebURL(), SelectionAsText(), false); instance_->ReplaceSelection(""); return true; } // If the clipboard contains something other than text (e.g. an image), // WebClipboard::ReadPlainText() returns an empty string. The empty string is // then pasted, replacing any selected text. This behavior is consistent with // that of HTML text form fields. if (name == "Paste" || name == "PasteAndMatchStyle") { if (!CanEditText()) return false; blink::WebString text = blink::Platform::Current()->Clipboard()->ReadPlainText( blink::mojom::ClipboardBuffer::kStandard); instance_->ReplaceSelection(text.Utf8()); return true; } return false; } WebURL PepperWebPluginImpl::LinkAtPosition(const WebPoint& position) const { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return GURL(); return GURL(instance_->GetLinkAtPosition(position)); } bool PepperWebPluginImpl::StartFind(const blink::WebString& search_text, bool case_sensitive, int identifier) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return false; return instance_->StartFind(search_text.Utf8(), case_sensitive, identifier); } void PepperWebPluginImpl::SelectFindResult(bool forward, int identifier) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->SelectFindResult(forward, identifier); } void PepperWebPluginImpl::StopFind() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->StopFind(); } bool PepperWebPluginImpl::SupportsPaginatedPrint() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return false; return instance_->SupportsPrintInterface(); } bool PepperWebPluginImpl::IsPrintScalingDisabled() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return false; return instance_->IsPrintScalingDisabled(); } int PepperWebPluginImpl::PrintBegin(const WebPrintParams& print_params) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return 0; return instance_->PrintBegin(print_params); } void PepperWebPluginImpl::PrintPage(int page_number, blink::WebCanvas* canvas) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->PrintPage(page_number, canvas); } void PepperWebPluginImpl::PrintEnd() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->PrintEnd(); } bool PepperWebPluginImpl::GetPrintPresetOptionsFromDocument( blink::WebPrintPresetOptions* preset_options) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return false; return instance_->GetPrintPresetOptionsFromDocument(preset_options); } bool PepperWebPluginImpl::CanRotateView() { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (!instance_) return false; return instance_->CanRotateView(); } void PepperWebPluginImpl::RotateView(RotationType type) { // Re-entrancy may cause JS to try to execute script on the plugin before it // is fully initialized. See: crbug.com/715747. if (instance_) instance_->RotateView(type); } bool PepperWebPluginImpl::IsPlaceholder() { return false; } } // namespace content