| /** |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2000 Stefan Schimanski (1Stein@gmx.de) |
| * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "third_party/blink/renderer/core/html/html_plugin_element.h" |
| |
| #include "services/network/public/mojom/request_context_frame_type.mojom-blink.h" |
| #include "third_party/blink/public/platform/web_url_request.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" |
| #include "third_party/blink/renderer/core/css/css_property_names.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/events/event.h" |
| #include "third_party/blink/renderer/core/dom/node.h" |
| #include "third_party/blink/renderer/core/dom/shadow_root.h" |
| #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" |
| #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_client.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/html/html_image_loader.h" |
| #include "third_party/blink/renderer/core/html/html_slot_element.h" |
| #include "third_party/blink/renderer/core/html/plugin_document.h" |
| #include "third_party/blink/renderer/core/html_names.h" |
| #include "third_party/blink/renderer/core/input/event_handler.h" |
| #include "third_party/blink/renderer/core/inspector/console_message.h" |
| #include "third_party/blink/renderer/core/layout/layout_embedded_content.h" |
| #include "third_party/blink/renderer/core/layout/layout_embedded_object.h" |
| #include "third_party/blink/renderer/core/layout/layout_image.h" |
| #include "third_party/blink/renderer/core/loader/mixed_content_checker.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h" |
| #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h" |
| #include "third_party/blink/renderer/platform/histogram.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" |
| #include "third_party/blink/renderer/platform/network/mime/mime_type_from_url.h" |
| #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" |
| #include "third_party/blink/renderer/platform/plugins/plugin_data.h" |
| |
| namespace blink { |
| |
| using namespace html_names; |
| |
| namespace { |
| |
| // Used for histograms, do not change the order. |
| enum PluginRequestObjectResult { |
| kPluginRequestObjectResultFailure = 0, |
| kPluginRequestObjectResultSuccess = 1, |
| // Keep at the end. |
| kPluginRequestObjectResultMax |
| }; |
| |
| String GetMIMETypeFromURL(const KURL& url) { |
| String filename = url.LastPathComponent(); |
| int extension_pos = filename.ReverseFind('.'); |
| if (extension_pos >= 0) { |
| String extension = filename.Substring(extension_pos + 1); |
| return MIMETypeRegistry::GetWellKnownMIMETypeForExtension(extension); |
| } |
| return String(); |
| } |
| |
| } // anonymous namespace |
| |
| const Vector<String>& PluginParameters::Names() const { |
| return names_; |
| } |
| |
| const Vector<String>& PluginParameters::Values() const { |
| return values_; |
| } |
| |
| void PluginParameters::AppendAttribute(const Attribute& attribute) { |
| names_.push_back(attribute.LocalName().GetString()); |
| values_.push_back(attribute.Value().GetString()); |
| } |
| void PluginParameters::AppendNameWithValue(const String& name, |
| const String& value) { |
| names_.push_back(name); |
| values_.push_back(value); |
| } |
| |
| int PluginParameters::FindStringInNames(const String& str) { |
| for (wtf_size_t i = 0; i < names_.size(); ++i) { |
| if (DeprecatedEqualIgnoringCase(names_[i], str)) |
| return i; |
| } |
| return -1; |
| } |
| |
| HTMLPlugInElement::HTMLPlugInElement( |
| const QualifiedName& tag_name, |
| Document& doc, |
| const CreateElementFlags flags, |
| PreferPlugInsForImagesOption prefer_plug_ins_for_images_option) |
| : HTMLFrameOwnerElement(tag_name, doc), |
| is_delaying_load_event_(false), |
| // needs_plugin_update_(!IsCreatedByParser) allows HTMLObjectElement to |
| // delay EmbeddedContentView updates until after all children are |
| // parsed. For HTMLEmbedElement this delay is unnecessary, but it is |
| // simpler to make both classes share the same codepath in this class. |
| needs_plugin_update_(!flags.IsCreatedByParser()), |
| should_prefer_plug_ins_for_images_(prefer_plug_ins_for_images_option == |
| kShouldPreferPlugInsForImages) { |
| SetHasCustomStyleCallbacks(); |
| } |
| |
| HTMLPlugInElement::~HTMLPlugInElement() { |
| DCHECK(plugin_wrapper_.IsEmpty()); // cleared in detachLayoutTree() |
| DCHECK(!is_delaying_load_event_); |
| } |
| |
| void HTMLPlugInElement::Trace(Visitor* visitor) { |
| visitor->Trace(image_loader_); |
| visitor->Trace(persisted_plugin_); |
| HTMLFrameOwnerElement::Trace(visitor); |
| } |
| |
| bool HTMLPlugInElement::HasPendingActivity() const { |
| return image_loader_ && image_loader_->HasPendingActivity(); |
| } |
| |
| void HTMLPlugInElement::SetPersistedPlugin(WebPluginContainerImpl* plugin) { |
| if (persisted_plugin_ == plugin) |
| return; |
| if (persisted_plugin_) { |
| persisted_plugin_->Hide(); |
| DisposePluginSoon(persisted_plugin_.Release()); |
| } |
| persisted_plugin_ = plugin; |
| } |
| |
| void HTMLPlugInElement::SetFocused(bool focused, WebFocusType focus_type) { |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| if (plugin) |
| plugin->SetFocused(focused, focus_type); |
| HTMLFrameOwnerElement::SetFocused(focused, focus_type); |
| } |
| |
| bool HTMLPlugInElement::RequestObjectInternal( |
| const PluginParameters& plugin_params) { |
| if (handled_externally_) { |
| // TODO(ekaramad): Fix this once we know what to do with frames inside |
| // plugins (https://crbug.com/776510). |
| return true; |
| } |
| |
| if (url_.IsEmpty() && service_type_.IsEmpty()) |
| return false; |
| |
| if (ProtocolIsJavaScript(url_)) |
| return false; |
| |
| KURL completed_url = |
| url_.IsEmpty() ? KURL() : GetDocument().CompleteURL(url_); |
| if (!AllowedToLoadObject(completed_url, service_type_)) |
| return false; |
| |
| handled_externally_ = |
| GetDocument().GetFrame()->Client()->IsPluginHandledExternally( |
| *this, completed_url, |
| service_type_.IsEmpty() ? GetMIMETypeFromURL(completed_url) |
| : service_type_); |
| if (handled_externally_) { |
| // This is a temporary placeholder and the logic around |
| // |handled_externally_| might change as MimeHandlerView is moving towards |
| // depending on OOPIFs instead of WebPlugin (https://crbug.com/659750). |
| completed_url = BlankURL(); |
| } |
| ObjectContentType object_type = GetObjectContentType(); |
| if (object_type == ObjectContentType::kFrame || |
| object_type == ObjectContentType::kImage || handled_externally_) { |
| // If the plugin element already contains a subframe, |
| // loadOrRedirectSubframe will re-use it. Otherwise, it will create a |
| // new frame and set it as the LayoutEmbeddedContent's EmbeddedContentView, |
| // causing what was previously in the EmbeddedContentView to be torn down. |
| return LoadOrRedirectSubframe(completed_url, GetNameAttribute(), true); |
| } |
| |
| // If an object's content can't be handled and it has no fallback, let |
| // it be handled as a plugin to show the broken plugin icon. |
| bool use_fallback = |
| object_type == ObjectContentType::kNone && HasFallbackContent(); |
| return LoadPlugin(completed_url, service_type_, plugin_params, use_fallback); |
| } |
| |
| bool HTMLPlugInElement::CanProcessDrag() const { |
| return PluginEmbeddedContentView() && |
| PluginEmbeddedContentView()->CanProcessDrag(); |
| } |
| |
| bool HTMLPlugInElement::CanStartSelection() const { |
| return UseFallbackContent() && Node::CanStartSelection(); |
| } |
| |
| bool HTMLPlugInElement::WillRespondToMouseClickEvents() { |
| if (IsDisabledFormControl()) |
| return false; |
| LayoutObject* r = GetLayoutObject(); |
| return r && (r->IsEmbeddedObject() || r->IsLayoutEmbeddedContent()); |
| } |
| |
| void HTMLPlugInElement::RemoveAllEventListeners() { |
| HTMLFrameOwnerElement::RemoveAllEventListeners(); |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| if (plugin) |
| plugin->EventListenersRemoved(); |
| } |
| |
| void HTMLPlugInElement::DidMoveToNewDocument(Document& old_document) { |
| if (image_loader_) |
| image_loader_->ElementDidMoveToNewDocument(); |
| HTMLFrameOwnerElement::DidMoveToNewDocument(old_document); |
| } |
| |
| void HTMLPlugInElement::AttachLayoutTree(AttachContext& context) { |
| HTMLFrameOwnerElement::AttachLayoutTree(context); |
| |
| if (!GetLayoutObject() || UseFallbackContent()) { |
| // If we don't have a layoutObject we have to dispose of any plugins |
| // which we persisted over a reattach. |
| if (persisted_plugin_) { |
| HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose; |
| SetPersistedPlugin(nullptr); |
| } |
| return; |
| } |
| |
| if (!IsImageType() && NeedsPluginUpdate() && GetLayoutEmbeddedObject() && |
| !GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator() && |
| GetObjectContentType() != ObjectContentType::kPlugin && |
| !is_delaying_load_event_) { |
| is_delaying_load_event_ = true; |
| GetDocument().IncrementLoadEventDelayCount(); |
| GetDocument().LoadPluginsSoon(); |
| } |
| if (LayoutObject* layout_object = GetLayoutObject()) { |
| if (image_loader_ && layout_object->IsLayoutImage()) { |
| LayoutImageResource* image_resource = |
| ToLayoutImage(layout_object)->ImageResource(); |
| image_resource->SetImageResource(image_loader_->GetContent()); |
| } |
| if (!layout_object->IsFloatingOrOutOfFlowPositioned()) |
| context.previous_in_flow = layout_object; |
| } |
| } |
| |
| void HTMLPlugInElement::IntrinsicSizingInfoChanged() { |
| if (auto* layout_object = GetLayoutObject()) { |
| layout_object->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation( |
| layout_invalidation_reason::kUnknown); |
| } |
| } |
| |
| void HTMLPlugInElement::UpdatePlugin() { |
| UpdatePluginInternal(); |
| if (is_delaying_load_event_) { |
| is_delaying_load_event_ = false; |
| GetDocument().DecrementLoadEventDelayCount(); |
| } |
| } |
| |
| void HTMLPlugInElement::RemovedFrom(ContainerNode& insertion_point) { |
| // If we've persisted the plugin and we're removed from the tree then |
| // make sure we cleanup the persistance pointer. |
| if (persisted_plugin_) { |
| // TODO(dcheng): This PluginDisposeSuspendScope doesn't seem to provide |
| // much; investigate removing it. |
| HTMLFrameOwnerElement::PluginDisposeSuspendScope suspend_plugin_dispose; |
| SetPersistedPlugin(nullptr); |
| } |
| HTMLFrameOwnerElement::RemovedFrom(insertion_point); |
| } |
| |
| bool HTMLPlugInElement::ShouldAccelerate() const { |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| return plugin && plugin->CcLayer(); |
| } |
| |
| ParsedFeaturePolicy HTMLPlugInElement::ConstructContainerPolicy( |
| Vector<String>*) const { |
| // Plugin elements (<object> and <embed>) are not allowed to enable the |
| // fullscreen feature. Add an empty allowlist for the fullscreen feature so |
| // that the nested browsing context is unable to use the API, regardless of |
| // origin. |
| // https://fullscreen.spec.whatwg.org/#model |
| ParsedFeaturePolicy container_policy; |
| ParsedFeaturePolicyDeclaration allowlist; |
| allowlist.feature = mojom::FeaturePolicyFeature::kFullscreen; |
| allowlist.matches_all_origins = false; |
| container_policy.push_back(allowlist); |
| return container_policy; |
| } |
| |
| void HTMLPlugInElement::DetachLayoutTree(const AttachContext& context) { |
| // Update the EmbeddedContentView the next time we attach (detaching destroys |
| // the plugin). |
| // FIXME: None of this "needsPluginUpdate" related code looks right. |
| if (GetLayoutObject() && !UseFallbackContent()) |
| SetNeedsPluginUpdate(true); |
| |
| if (is_delaying_load_event_) { |
| is_delaying_load_event_ = false; |
| GetDocument().DecrementLoadEventDelayCount(); |
| } |
| |
| // Only try to persist a plugin we actually own. |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| if (plugin && context.performing_reattach) { |
| SetPersistedPlugin(ToWebPluginContainerImpl(ReleaseEmbeddedContentView())); |
| } else { |
| // Clear the plugin; will trigger disposal of it with Oilpan. |
| SetEmbeddedContentView(nullptr); |
| } |
| |
| ResetInstance(); |
| |
| HTMLFrameOwnerElement::DetachLayoutTree(context); |
| } |
| |
| LayoutObject* HTMLPlugInElement::CreateLayoutObject( |
| const ComputedStyle& style) { |
| // Fallback content breaks the DOM->layoutObject class relationship of this |
| // class and all superclasses because createObject won't necessarily return |
| // a LayoutEmbeddedObject or LayoutEmbeddedContent. |
| if (UseFallbackContent()) |
| return LayoutObject::CreateObject(this, style); |
| |
| if (IsImageType()) { |
| LayoutImage* image = new LayoutImage(this); |
| image->SetImageResource(LayoutImageResource::Create()); |
| return image; |
| } |
| |
| plugin_is_available_ = true; |
| return new LayoutEmbeddedObject(this); |
| } |
| |
| void HTMLPlugInElement::FinishParsingChildren() { |
| HTMLFrameOwnerElement::FinishParsingChildren(); |
| if (UseFallbackContent()) |
| return; |
| |
| SetNeedsPluginUpdate(true); |
| if (isConnected()) |
| LazyReattachIfNeeded(); |
| } |
| |
| void HTMLPlugInElement::ResetInstance() { |
| plugin_wrapper_.Reset(); |
| } |
| |
| v8::Local<v8::Object> HTMLPlugInElement::PluginWrapper() { |
| LocalFrame* frame = GetDocument().GetFrame(); |
| if (!frame) |
| return v8::Local<v8::Object>(); |
| |
| // If the host dynamically turns off JavaScript (or Java) we will still |
| // return the cached allocated Bindings::Instance. Not supporting this |
| // edge-case is OK. |
| v8::Isolate* isolate = V8PerIsolateData::MainThreadIsolate(); |
| if (plugin_wrapper_.IsEmpty()) { |
| WebPluginContainerImpl* plugin; |
| |
| if (persisted_plugin_) |
| plugin = persisted_plugin_; |
| else |
| plugin = PluginEmbeddedContentView(); |
| |
| if (plugin) |
| plugin_wrapper_.Reset(isolate, plugin->ScriptableObject(isolate)); |
| } |
| return plugin_wrapper_.Get(isolate); |
| } |
| |
| WebPluginContainerImpl* HTMLPlugInElement::PluginEmbeddedContentView() const { |
| if (LayoutEmbeddedContent* layout_embedded_content = |
| LayoutEmbeddedContentForJSBindings()) |
| return layout_embedded_content->Plugin(); |
| return nullptr; |
| } |
| |
| WebPluginContainerImpl* HTMLPlugInElement::OwnedPlugin() const { |
| EmbeddedContentView* view = OwnedEmbeddedContentView(); |
| if (view && view->IsPluginView()) |
| return ToWebPluginContainerImpl(view); |
| return nullptr; |
| } |
| |
| bool HTMLPlugInElement::IsPresentationAttribute( |
| const QualifiedName& name) const { |
| if (name == kWidthAttr || name == kHeightAttr || name == kVspaceAttr || |
| name == kHspaceAttr || name == kAlignAttr) |
| return true; |
| return HTMLFrameOwnerElement::IsPresentationAttribute(name); |
| } |
| |
| void HTMLPlugInElement::CollectStyleForPresentationAttribute( |
| const QualifiedName& name, |
| const AtomicString& value, |
| MutableCSSPropertyValueSet* style) { |
| if (name == kWidthAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyWidth, value); |
| } else if (name == kHeightAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyHeight, value); |
| } else if (name == kVspaceAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyMarginTop, value); |
| AddHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); |
| } else if (name == kHspaceAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); |
| AddHTMLLengthToStyle(style, CSSPropertyMarginRight, value); |
| } else if (name == kAlignAttr) { |
| ApplyAlignmentAttributeToStyle(value, style); |
| } else { |
| HTMLFrameOwnerElement::CollectStyleForPresentationAttribute(name, value, |
| style); |
| } |
| } |
| |
| void HTMLPlugInElement::DefaultEventHandler(Event& event) { |
| // Firefox seems to use a fake event listener to dispatch events to plugin |
| // (tested with mouse events only). This is observable via different order |
| // of events - in Firefox, event listeners specified in HTML attributes |
| // fires first, then an event gets dispatched to plugin, and only then |
| // other event listeners fire. Hopefully, this difference does not matter in |
| // practice. |
| |
| // FIXME: Mouse down and scroll events are passed down to plugin via custom |
| // code in EventHandler; these code paths should be united. |
| |
| LayoutObject* r = GetLayoutObject(); |
| if (!r || !r->IsLayoutEmbeddedContent()) |
| return; |
| if (r->IsEmbeddedObject()) { |
| if (ToLayoutEmbeddedObject(r)->ShowsUnavailablePluginIndicator()) |
| return; |
| } |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| if (!plugin) |
| return; |
| plugin->HandleEvent(event); |
| if (event.DefaultHandled()) |
| return; |
| HTMLFrameOwnerElement::DefaultEventHandler(event); |
| } |
| |
| LayoutEmbeddedContent* HTMLPlugInElement::LayoutEmbeddedContentForJSBindings() |
| const { |
| // Needs to load the plugin immediatedly because this function is called |
| // when JavaScript code accesses the plugin. |
| // FIXME: Check if dispatching events here is safe. |
| GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets( |
| Document::kRunPostLayoutTasksSynchronously); |
| return ExistingLayoutEmbeddedContent(); |
| } |
| |
| bool HTMLPlugInElement::IsKeyboardFocusable() const { |
| if (HTMLFrameOwnerElement::IsKeyboardFocusable()) |
| return true; |
| return GetDocument().IsActive() && PluginEmbeddedContentView() && |
| PluginEmbeddedContentView()->SupportsKeyboardFocus(); |
| } |
| |
| bool HTMLPlugInElement::HasCustomFocusLogic() const { |
| return !UseFallbackContent(); |
| } |
| |
| bool HTMLPlugInElement::IsPluginElement() const { |
| return true; |
| } |
| |
| bool HTMLPlugInElement::IsErrorplaceholder() { |
| if (PluginEmbeddedContentView() && |
| PluginEmbeddedContentView()->IsErrorplaceholder()) |
| return true; |
| return false; |
| } |
| |
| void HTMLPlugInElement::DisconnectContentFrame() { |
| HTMLFrameOwnerElement::DisconnectContentFrame(); |
| SetPersistedPlugin(nullptr); |
| } |
| |
| bool HTMLPlugInElement::IsFocusableStyle() const { |
| if (HTMLFrameOwnerElement::SupportsFocus() && |
| HTMLFrameOwnerElement::IsFocusableStyle()) |
| return true; |
| |
| if (UseFallbackContent() || !HTMLFrameOwnerElement::IsFocusableStyle()) |
| return false; |
| return plugin_is_available_; |
| } |
| |
| HTMLPlugInElement::ObjectContentType HTMLPlugInElement::GetObjectContentType() |
| const { |
| String mime_type = service_type_; |
| KURL url = GetDocument().CompleteURL(url_); |
| if (mime_type.IsEmpty()) { |
| // Try to guess the MIME type based off the extension. |
| mime_type = GetMIMETypeFromURL(url); |
| if (mime_type.IsEmpty()) |
| return ObjectContentType::kFrame; |
| } |
| |
| // If Chrome is started with the --disable-plugins switch, pluginData is 0. |
| PluginData* plugin_data = GetDocument().GetFrame()->GetPluginData(); |
| bool plugin_supports_mime_type = |
| plugin_data && plugin_data->SupportsMimeType(mime_type); |
| |
| if (MIMETypeRegistry::IsSupportedImageMIMEType(mime_type)) { |
| return should_prefer_plug_ins_for_images_ && plugin_supports_mime_type |
| ? ObjectContentType::kPlugin |
| : ObjectContentType::kImage; |
| } |
| |
| if (plugin_supports_mime_type) |
| return ObjectContentType::kPlugin; |
| if (MIMETypeRegistry::IsSupportedNonImageMIMEType(mime_type)) |
| return ObjectContentType::kFrame; |
| return ObjectContentType::kNone; |
| } |
| |
| bool HTMLPlugInElement::IsImageType() const { |
| if (GetDocument().GetFrame()) |
| return GetObjectContentType() == ObjectContentType::kImage; |
| return MIMETypeRegistry::IsSupportedImageResourceMIMEType(service_type_); |
| } |
| |
| LayoutEmbeddedObject* HTMLPlugInElement::GetLayoutEmbeddedObject() const { |
| // HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects |
| // when using fallback content. |
| if (!GetLayoutObject() || !GetLayoutObject()->IsEmbeddedObject()) |
| return nullptr; |
| return ToLayoutEmbeddedObject(GetLayoutObject()); |
| } |
| |
| // We don't use url_, as it may not be the final URL that the object loads, |
| // depending on <param> values. |
| bool HTMLPlugInElement::AllowedToLoadFrameURL(const String& url) { |
| KURL complete_url = GetDocument().CompleteURL(url); |
| return !(ContentFrame() && complete_url.ProtocolIsJavaScript() && |
| !GetDocument().GetSecurityOrigin()->CanAccess( |
| ContentFrame()->GetSecurityContext()->GetSecurityOrigin())); |
| } |
| |
| bool HTMLPlugInElement::RequestObject(const PluginParameters& plugin_params) { |
| bool result = RequestObjectInternal(plugin_params); |
| |
| DEFINE_STATIC_LOCAL( |
| EnumerationHistogram, result_histogram, |
| ("Plugin.RequestObjectResult", kPluginRequestObjectResultMax)); |
| result_histogram.Count(result ? kPluginRequestObjectResultSuccess |
| : kPluginRequestObjectResultFailure); |
| |
| return result; |
| } |
| |
| bool HTMLPlugInElement::LoadPlugin(const KURL& url, |
| const String& mime_type, |
| const PluginParameters& plugin_params, |
| bool use_fallback) { |
| if (!AllowedToLoadPlugin(url, mime_type)) |
| return false; |
| |
| LocalFrame* frame = GetDocument().GetFrame(); |
| if (!frame->Loader().AllowPlugins(kAboutToInstantiatePlugin)) |
| return false; |
| |
| auto* layout_object = GetLayoutEmbeddedObject(); |
| // FIXME: This code should not depend on layoutObject! |
| if (!layout_object || use_fallback) |
| return false; |
| |
| VLOG(1) << this << " Plugin URL: " << url_; |
| VLOG(1) << "Loaded URL: " << url.GetString(); |
| loaded_url_ = url; |
| |
| if (persisted_plugin_) { |
| SetEmbeddedContentView(persisted_plugin_.Release()); |
| } else { |
| bool load_manually = |
| GetDocument().IsPluginDocument() && !GetDocument().ContainsPlugins(); |
| WebPluginContainerImpl* plugin = frame->Client()->CreatePlugin( |
| *this, url, plugin_params.Names(), plugin_params.Values(), mime_type, |
| load_manually); |
| if (!plugin) { |
| if (layout_object && !layout_object->ShowsUnavailablePluginIndicator()) { |
| plugin_is_available_ = false; |
| layout_object->SetPluginAvailability( |
| LayoutEmbeddedObject::kPluginMissing); |
| } |
| return false; |
| } |
| |
| if (layout_object) { |
| SetEmbeddedContentView(plugin); |
| layout_object->GetFrameView()->AddPlugin(plugin); |
| } else { |
| SetPersistedPlugin(plugin); |
| } |
| } |
| |
| GetDocument().SetContainsPlugins(); |
| // TODO(esprehn): WebPluginContainerImpl::SetCcLayer() also schedules a |
| // compositing update, do we need both? |
| SetNeedsCompositingUpdate(); |
| // Make sure any input event handlers introduced by the plugin are taken into |
| // account. |
| if (Page* page = GetDocument().GetFrame()->GetPage()) { |
| if (ScrollingCoordinator* scrolling_coordinator = |
| page->GetScrollingCoordinator()) { |
| LocalFrameView* frame_view = GetDocument().GetFrame()->View(); |
| scrolling_coordinator->NotifyGeometryChanged(frame_view); |
| } |
| } |
| return true; |
| } |
| |
| void HTMLPlugInElement::DispatchErrorEvent() { |
| if (GetDocument().IsPluginDocument() && GetDocument().LocalOwner()) { |
| GetDocument().LocalOwner()->DispatchEvent( |
| *Event::Create(event_type_names::kError)); |
| } else { |
| DispatchEvent(*Event::Create(event_type_names::kError)); |
| } |
| } |
| |
| bool HTMLPlugInElement::AllowedToLoadObject(const KURL& url, |
| const String& mime_type) { |
| if (url.IsEmpty() && mime_type.IsEmpty()) |
| return false; |
| |
| LocalFrame* frame = GetDocument().GetFrame(); |
| Settings* settings = frame->GetSettings(); |
| if (!settings) |
| return false; |
| |
| if (MIMETypeRegistry::IsJavaAppletMIMEType(mime_type)) |
| return false; |
| |
| AtomicString declared_mime_type = FastGetAttribute(html_names::kTypeAttr); |
| if (!GetDocument().GetContentSecurityPolicy()->AllowObjectFromSource(url) || |
| !GetDocument().GetContentSecurityPolicy()->AllowPluginTypeForDocument( |
| GetDocument(), mime_type, declared_mime_type, url)) { |
| if (auto* layout_object = GetLayoutEmbeddedObject()) { |
| plugin_is_available_ = false; |
| layout_object->SetPluginAvailability( |
| LayoutEmbeddedObject::kPluginBlockedByContentSecurityPolicy); |
| } |
| return false; |
| } |
| // If the URL is empty, a plugin could still be instantiated if a MIME-type |
| // is specified. |
| return (!mime_type.IsEmpty() && url.IsEmpty()) || |
| !MixedContentChecker::ShouldBlockFetch( |
| frame, mojom::RequestContextType::OBJECT, |
| network::mojom::RequestContextFrameType::kNone, |
| ResourceRequest::RedirectStatus::kNoRedirect, url); |
| } |
| |
| bool HTMLPlugInElement::AllowedToLoadPlugin(const KURL& url, |
| const String& mime_type) { |
| if (GetDocument().IsSandboxed(kSandboxPlugins)) { |
| GetDocument().AddConsoleMessage( |
| ConsoleMessage::Create(kSecurityMessageSource, kErrorMessageLevel, |
| "Failed to load '" + url.ElidedString() + |
| "' as a plugin, because the " |
| "frame into which the plugin " |
| "is loading is sandboxed.")); |
| return false; |
| } |
| return true; |
| } |
| |
| void HTMLPlugInElement::DidAddUserAgentShadowRoot(ShadowRoot&) { |
| UserAgentShadowRoot()->AppendChild( |
| HTMLSlotElement::CreateUserAgentDefaultSlot(GetDocument())); |
| } |
| |
| bool HTMLPlugInElement::HasFallbackContent() const { |
| return false; |
| } |
| |
| bool HTMLPlugInElement::UseFallbackContent() const { |
| return false; |
| } |
| |
| void HTMLPlugInElement::LazyReattachIfNeeded() { |
| if (!UseFallbackContent() && NeedsPluginUpdate() && GetLayoutObject() && |
| !IsImageType()) { |
| LazyReattachIfAttached(); |
| SetPersistedPlugin(nullptr); |
| } |
| } |
| |
| void HTMLPlugInElement::UpdateServiceTypeIfEmpty() { |
| if (service_type_.IsEmpty() && ProtocolIs(url_, "data")) { |
| service_type_ = MimeTypeFromDataURL(url_); |
| } |
| } |
| |
| scoped_refptr<ComputedStyle> HTMLPlugInElement::CustomStyleForLayoutObject() { |
| scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject(); |
| if (IsImageType() && !GetLayoutObject() && style && |
| LayoutObjectIsNeeded(*style)) { |
| if (!image_loader_) |
| image_loader_ = HTMLImageLoader::Create(this); |
| image_loader_->UpdateFromElement(); |
| } |
| return style; |
| } |
| |
| } // namespace blink |