| /** |
| * 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 "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h" |
| #include "third_party/blink/public/mojom/loader/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/css/style_change_reason.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/plugin_data.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/instrumentation/histogram.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.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/scheduler/public/frame_scheduler.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/scheduling_policy.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| 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(); |
| if (doc.GetScheduler()) { |
| doc.GetScheduler()->RegisterStickyFeature( |
| SchedulingPolicy::Feature::kContainsPlugins, |
| {SchedulingPolicy::RecordMetricsForBackForwardCache()}); |
| } |
| } |
| |
| 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::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); |
| |
| LayoutObject* layout_object = GetLayoutObject(); |
| if (!layout_object || 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; |
| } |
| |
| // This element may have been attached previously, and if we created a frame |
| // back then, re-use it now. We do not want to reload the frame if we don't |
| // have to, as that would cause us to lose any state changed after loading. |
| // Re-using the frame also matters if we have to re-attach for printing; we |
| // don't support reloading anything during printing (the frame would just show |
| // up blank then). |
| const Frame* content_frame = ContentFrame(); |
| if (content_frame && !dispose_view_) { |
| // We should only re-use the frame if we're actually re-attaching as |
| // LayoutEmbeddedContent. We may for instance have become an image, without |
| // having triggered a plugin reload, and that this layout object type change |
| // happens now "for free" for completely different reasons (e.g. CSS display |
| // type change). |
| if (layout_object->IsLayoutEmbeddedContent()) |
| SetEmbeddedContentView(content_frame->View()); |
| } else if (!IsImageType() && NeedsPluginUpdate() && |
| GetLayoutEmbeddedObject() && |
| !GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator() && |
| GetObjectContentType() != ObjectContentType::kPlugin && |
| !is_delaying_load_event_) { |
| is_delaying_load_event_ = true; |
| GetDocument().IncrementLoadEventDelayCount(); |
| GetDocument().LoadPluginsSoon(); |
| } |
| 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; |
| |
| dispose_view_ = false; |
| } |
| |
| void HTMLPlugInElement::IntrinsicSizingInfoChanged() { |
| if (auto* embedded_object = GetLayoutEmbeddedObject()) |
| embedded_object->IntrinsicSizeChanged(); |
| } |
| |
| void HTMLPlugInElement::UpdatePlugin() { |
| UpdatePluginInternal(); |
| if (is_delaying_load_event_) { |
| is_delaying_load_event_ = false; |
| GetDocument().DecrementLoadEventDelayCount(); |
| } |
| } |
| |
| void HTMLPlugInElement::RemovedFrom(ContainerNode& insertion_point) { |
| // Plugins can persist only through reattachment during a lifecycle |
| // update. This method shouldn't be called in that lifecycle phase. |
| DCHECK(!persisted_plugin_); |
| |
| 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( |
| mojom::FeaturePolicyFeature::kFullscreen, mojom::PolicyValueType::kBool); |
| container_policy.push_back(allowlist); |
| return container_policy; |
| } |
| |
| void HTMLPlugInElement::DetachLayoutTree(bool performing_reattach) { |
| // 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(); |
| } |
| |
| bool keep_plugin = performing_reattach && !dispose_view_; |
| |
| // Only try to persist a plugin we actually own. |
| WebPluginContainerImpl* plugin = OwnedPlugin(); |
| if (plugin && keep_plugin) { |
| SetPersistedPlugin(ToWebPluginContainerImpl(ReleaseEmbeddedContentView())); |
| } else { |
| // A persisted plugin isn't processed and hooked up immediately |
| // (synchronously) when attaching the layout object, so it's possible that |
| // it's still around. That's fine if we're allowed to keep it. Otherwise, |
| // get rid of it now. |
| if (persisted_plugin_ && !keep_plugin) |
| SetPersistedPlugin(nullptr); |
| |
| // Clear the plugin; will trigger disposal of it with Oilpan. |
| if (!persisted_plugin_) |
| SetEmbeddedContentView(nullptr); |
| } |
| |
| // We should attempt to use the same view afterwards, so that we don't lose |
| // state. But only if we're reattaching. Otherwise we need to throw it away, |
| // since there's no telling what's going to happen next, and it wouldn't be |
| // safe to keep it. |
| if (!performing_reattach) |
| SetDisposeView(); |
| |
| RemovePluginFromFrameView(plugin); |
| ResetInstance(); |
| |
| HTMLFrameOwnerElement::DetachLayoutTree(performing_reattach); |
| } |
| |
| LayoutObject* HTMLPlugInElement::CreateLayoutObject(const ComputedStyle& style, |
| LegacyLayout legacy) { |
| // 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, legacy); |
| |
| if (IsImageType()) { |
| LayoutImage* image = new LayoutImage(this); |
| image->SetImageResource(MakeGarbageCollected<LayoutImageResource>()); |
| return image; |
| } |
| |
| plugin_is_available_ = true; |
| return new LayoutEmbeddedObject(this); |
| } |
| |
| void HTMLPlugInElement::FinishParsingChildren() { |
| HTMLFrameOwnerElement::FinishParsingChildren(); |
| if (!UseFallbackContent()) |
| SetNeedsPluginUpdate(true); |
| } |
| |
| 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)); |
| } else { |
| // This step is intended for plugins with external handlers. This should |
| // checked after after calling PluginEmbeddedContentView(). Note that |
| // calling PluginEmbeddedContentView() leads to synchronously updating |
| // style and running post layout tasks, which ends up updating the plugin. |
| // It is after updating the plugin that we know whether or not the plugin |
| // is handled externally. Also note that it is possible to call |
| // PluginWrapper before the plugin has gone through the update phase(see |
| // https://crbug.com/946709). |
| plugin_wrapper_.Reset( |
| isolate, frame->Client()->GetScriptableObject(*this, 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 == html_names::kWidthAttr || name == html_names::kHeightAttr || |
| name == html_names::kVspaceAttr || name == html_names::kHspaceAttr || |
| name == html_names::kAlignAttr) |
| return true; |
| return HTMLFrameOwnerElement::IsPresentationAttribute(name); |
| } |
| |
| void HTMLPlugInElement::CollectStyleForPresentationAttribute( |
| const QualifiedName& name, |
| const AtomicString& value, |
| MutableCSSPropertyValueSet* style) { |
| if (name == html_names::kWidthAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kWidth, value); |
| } else if (name == html_names::kHeightAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kHeight, value); |
| } else if (name == html_names::kVspaceAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kMarginTop, value); |
| AddHTMLLengthToStyle(style, CSSPropertyID::kMarginBottom, value); |
| } else if (name == html_names::kHspaceAttr) { |
| AddHTMLLengthToStyle(style, CSSPropertyID::kMarginLeft, value); |
| AddHTMLLengthToStyle(style, CSSPropertyID::kMarginRight, value); |
| } else if (name == html_names::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().UpdateStyleAndLayout(); |
| if (auto* view = GetDocument().View()) |
| view->FlushAnyPendingPostLayoutTasks(); |
| |
| 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 (plugin_supports_mime_type && |
| plugin_data->IsExternalPluginMimeType(mime_type)) { |
| return ObjectContentType::kExternalPlugin; |
| } |
| |
| 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. |
| return ToLayoutEmbeddedObjectOrNull(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) { |
| 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; |
| |
| ObjectContentType object_type = GetObjectContentType(); |
| bool handled_externally = |
| object_type == ObjectContentType::kExternalPlugin && |
| AllowedToLoadPlugin(completed_url, service_type_) && |
| GetDocument().GetFrame()->Client()->IsPluginHandledExternally( |
| *this, completed_url, |
| service_type_.IsEmpty() ? GetMIMETypeFromURL(completed_url) |
| : service_type_); |
| if (handled_externally) |
| ResetInstance(); |
| if (object_type == ObjectContentType::kFrame || |
| object_type == ObjectContentType::kImage || handled_externally) { |
| if (object_type == ObjectContentType::kFrame) { |
| UseCounter::Count(GetDocument(), |
| WebFeature::kPluginElementLoadedDocument); |
| } else if (object_type == ObjectContentType::kImage) { |
| UseCounter::Count(GetDocument(), WebFeature::kPluginElementLoadedImage); |
| } else { |
| UseCounter::Count(GetDocument(), |
| WebFeature::kPluginElementLoadedExternal); |
| } |
| |
| if (ContentFrame() && ContentFrame()->IsRemoteFrame()) { |
| // During lazy reattaching, the plugin element loses EmbeddedContentView. |
| // Since the ContentFrame() is not torn down the options here are to |
| // either re-create a new RemoteFrameView or reuse the old one. The former |
| // approach requires CommitNavigation for OOPF to be sent back here in |
| // the parent process. It is easier to just reuse the current FrameView |
| // instead until plugin element issue are properly resolved (for context |
| // see https://crbug.com/781880). |
| DCHECK(!OwnedEmbeddedContentView()); |
| SetEmbeddedContentView(ContentFrame()->View()); |
| DCHECK(OwnedEmbeddedContentView()); |
| } |
| // 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::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_) { |
| auto* plugin = persisted_plugin_.Get(); |
| SetEmbeddedContentView(persisted_plugin_.Release()); |
| layout_object->GetFrameView()->AddPlugin(plugin); |
| } 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->ShowsUnavailablePluginIndicator()) { |
| plugin_is_available_ = false; |
| layout_object->SetPluginAvailability( |
| LayoutEmbeddedObject::kPluginMissing); |
| } |
| return false; |
| } |
| |
| SetEmbeddedContentView(plugin); |
| layout_object->GetFrameView()->AddPlugin(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, |
| ResourceRequest::RedirectStatus::kNoRedirect, url); |
| } |
| |
| bool HTMLPlugInElement::AllowedToLoadPlugin(const KURL& url, |
| const String& mime_type) { |
| if (GetDocument().IsSandboxed(WebSandboxFlags::kPlugins)) { |
| GetDocument().AddConsoleMessage( |
| ConsoleMessage::Create(mojom::ConsoleMessageSource::kSecurity, |
| mojom::ConsoleMessageLevel::kError, |
| "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::RemovePluginFromFrameView( |
| WebPluginContainerImpl* plugin) { |
| if (!plugin) |
| return; |
| |
| auto* layout_object = GetLayoutEmbeddedObject(); |
| if (!layout_object) |
| return; |
| |
| auto* frame_view = layout_object->GetFrameView(); |
| if (!frame_view) |
| return; |
| |
| if (!frame_view->Plugins().Contains(plugin)) |
| return; |
| |
| frame_view->RemovePlugin(plugin); |
| } |
| |
| void HTMLPlugInElement::DidAddUserAgentShadowRoot(ShadowRoot&) { |
| UserAgentShadowRoot()->AppendChild( |
| HTMLSlotElement::CreateUserAgentDefaultSlot(GetDocument())); |
| } |
| |
| bool HTMLPlugInElement::HasFallbackContent() const { |
| return false; |
| } |
| |
| bool HTMLPlugInElement::UseFallbackContent() const { |
| return false; |
| } |
| |
| void HTMLPlugInElement::ReattachOnPluginChangeIfNeeded() { |
| if (UseFallbackContent() || !NeedsPluginUpdate() || !GetLayoutObject()) |
| return; |
| |
| SetNeedsStyleRecalc( |
| kSubtreeStyleChange, |
| StyleChangeReasonForTracing::Create(style_change_reason::kPluginChanged)); |
| SetForceReattachLayoutTree(); |
| |
| // Make sure that we don't attempt to re-use the view through re-attachment. |
| SetDisposeView(); |
| } |
| |
| 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_ = MakeGarbageCollected<HTMLImageLoader>(this); |
| image_loader_->UpdateFromElement(); |
| } |
| return style; |
| } |
| |
| } // namespace blink |