blob: 02f9782ab4f00d13f287577c07a5b96650126245 [file] [log] [blame]
/**
* 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/feature_policy/policy_value.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.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/display_lock/display_lock_utilities.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/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/heap/heap.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);
}
void PluginParameters::MapDataParamToSrc() {
auto* src = std::find_if(names_.begin(), names_.end(), [](auto name) {
return EqualIgnoringASCIICase(name, "src");
});
if (src != names_.end()) {
return;
}
auto* data = std::find_if(names_.begin(), names_.end(), [](auto name) {
return EqualIgnoringASCIICase(name, "data");
});
if (data != names_.end()) {
AppendNameWithValue("src", values_[data - names_.begin()]);
}
}
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 (auto* context = doc.GetExecutionContext()) {
context->GetScheduler()->RegisterStickyFeature(
SchedulingPolicy::Feature::kContainsPlugins,
{SchedulingPolicy::DisableBackForwardCache()});
}
}
HTMLPlugInElement::~HTMLPlugInElement() {
DCHECK(plugin_wrapper_.IsEmpty()); // cleared in detachLayoutTree()
DCHECK(!is_delaying_load_event_);
}
void HTMLPlugInElement::Trace(Visitor* visitor) const {
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,
mojom::blink::FocusType 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_) {
// If we're in a content-visibility subtree that can prevent layout, then
// add our layout object to the frame view's update list. This is typically
// done during layout, but if we're blocking layout, we will never update
// the plugin and thus delay the load event indefinitely.
if (DisplayLockUtilities::NearestLockedExclusiveAncestor(*this)) {
auto* embedded_object = GetLayoutEmbeddedObject();
if (auto* frame_view = embedded_object->GetFrameView())
frame_view->AddPartToUpdate(*embedded_object);
}
is_delaying_load_event_ = true;
GetDocument().IncrementLoadEventDelayCount();
GetDocument().LoadPluginsSoon();
}
if (image_loader_ && layout_object->IsLayoutImage()) {
LayoutImageResource* image_resource =
To<LayoutImage>(layout_object)->ImageResource();
image_resource->SetImageResource(image_loader_->GetContent());
}
if (layout_object->AffectsWhitespaceSiblings())
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() 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::blink::FeaturePolicyFeature::kFullscreen);
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(
To<WebPluginContainerImpl>(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 To<WebPluginContainerImpl>(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 (auto* embedded_object = DynamicTo<LayoutEmbeddedObject>(r)) {
if (embedded_object->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(DocumentUpdateReason::kJavaScript);
if (auto* view = GetDocument().View())
view->FlushAnyPendingPostLayoutTasks();
return ExistingLayoutEmbeddedContent();
}
bool HTMLPlugInElement::IsKeyboardFocusable() const {
if (HTMLFrameOwnerElement::IsKeyboardFocusable())
return true;
return GetDocument().IsActive() && PluginEmbeddedContentView() &&
PluginEmbeddedContentView()->SupportsKeyboardFocus() && IsFocusable();
}
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 DynamicTo<LayoutEmbeddedObject>(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() &&
!GetExecutionContext()->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())
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 =
IsA<PluginDocument>(GetDocument()) && !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();
return true;
}
void HTMLPlugInElement::DispatchErrorEvent() {
if (IsA<PluginDocument>(GetDocument()) && 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);
auto* csp = GetExecutionContext()->GetContentSecurityPolicy();
if (!csp->AllowObjectFromSource(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::blink::RequestContextType::OBJECT, url,
ResourceRequest::RedirectStatus::kNoRedirect, url,
/* devtools_id= */ base::nullopt, ReportingDisposition::kReport,
GetDocument().Loader()->GetContentSecurityNotifier());
}
bool HTMLPlugInElement::AllowedToLoadPlugin(const KURL& url,
const String& mime_type) {
if (GetExecutionContext()->IsSandboxed(
network::mojom::blink::WebSandboxFlags::kPlugins)) {
GetExecutionContext()->AddConsoleMessage(
MakeGarbageCollected<ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kSecurity,
mojom::blink::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(
const StyleRecalcContext& style_recalc_context) {
scoped_refptr<ComputedStyle> style =
OriginalStyleForLayoutObject(style_recalc_context);
if (IsImageType() && !GetLayoutObject() && style &&
LayoutObjectIsNeeded(*style)) {
if (!image_loader_)
image_loader_ = MakeGarbageCollected<HTMLImageLoader>(this);
image_loader_->UpdateFromElement();
}
return style;
}
} // namespace blink