| /** |
| * 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 "core/html/HTMLPlugInElement.h" |
| |
| #include "bindings/core/v8/ScriptController.h" |
| #include "core/CSSPropertyNames.h" |
| #include "core/HTMLNames.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/Node.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/events/Event.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/csp/ContentSecurityPolicy.h" |
| #include "core/html/HTMLContentElement.h" |
| #include "core/html/HTMLImageLoader.h" |
| #include "core/html/PluginDocument.h" |
| #include "core/input/EventHandler.h" |
| #include "core/inspector/ConsoleMessage.h" |
| #include "core/layout/LayoutImage.h" |
| #include "core/layout/LayoutPart.h" |
| #include "core/layout/api/LayoutEmbeddedItem.h" |
| #include "core/loader/FrameLoaderClient.h" |
| #include "core/loader/MixedContentChecker.h" |
| #include "core/page/Page.h" |
| #include "core/page/scrolling/ScrollingCoordinator.h" |
| #include "core/plugins/PluginView.h" |
| #include "platform/Histogram.h" |
| #include "platform/MIMETypeFromURL.h" |
| #include "platform/MIMETypeRegistry.h" |
| #include "platform/Widget.h" |
| #include "platform/network/ResourceRequest.h" |
| #include "platform/plugins/PluginData.h" |
| #include "public/platform/WebURLRequest.h" |
| |
| namespace blink { |
| |
| using namespace HTMLNames; |
| |
| namespace { |
| |
| // Used for histograms, do not change the order. |
| enum PluginRequestObjectResult { |
| PluginRequestObjectResultFailure = 0, |
| PluginRequestObjectResultSuccess = 1, |
| // Keep at the end. |
| PluginRequestObjectResultMax |
| }; |
| |
| } // anonymous namespace |
| |
| HTMLPlugInElement::HTMLPlugInElement( |
| const QualifiedName& tagName, |
| Document& doc, |
| bool createdByParser, |
| PreferPlugInsForImagesOption preferPlugInsForImagesOption) |
| : HTMLFrameOwnerElement(tagName, doc), |
| m_isDelayingLoadEvent(false), |
| // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay |
| // widget 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. |
| m_needsWidgetUpdate(!createdByParser), |
| m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == |
| ShouldPreferPlugInsForImages) {} |
| |
| HTMLPlugInElement::~HTMLPlugInElement() { |
| DCHECK(!m_pluginWrapper); // cleared in detachLayoutTree() |
| DCHECK(!m_isDelayingLoadEvent); |
| } |
| |
| DEFINE_TRACE(HTMLPlugInElement) { |
| visitor->trace(m_imageLoader); |
| visitor->trace(m_persistedPluginWidget); |
| HTMLFrameOwnerElement::trace(visitor); |
| } |
| |
| void HTMLPlugInElement::setPersistedPluginWidget(Widget* widget) { |
| if (m_persistedPluginWidget == widget) |
| return; |
| if (m_persistedPluginWidget) { |
| if (m_persistedPluginWidget->isPluginView()) { |
| m_persistedPluginWidget->hide(); |
| disposeWidgetSoon(m_persistedPluginWidget.release()); |
| } else { |
| DCHECK(m_persistedPluginWidget->isFrameView() || |
| m_persistedPluginWidget->isRemoteFrameView()); |
| } |
| } |
| m_persistedPluginWidget = widget; |
| } |
| |
| bool HTMLPlugInElement::requestObjectInternal( |
| const String& url, |
| const String& mimeType, |
| const Vector<String>& paramNames, |
| const Vector<String>& paramValues) { |
| if (url.isEmpty() && mimeType.isEmpty()) |
| return false; |
| |
| if (protocolIsJavaScript(url)) |
| return false; |
| |
| KURL completedURL = url.isEmpty() ? KURL() : document().completeURL(url); |
| if (!allowedToLoadObject(completedURL, mimeType)) |
| return false; |
| |
| bool useFallback; |
| if (!shouldUsePlugin(completedURL, mimeType, hasFallbackContent(), |
| useFallback)) { |
| // 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 LayoutPart's widget, causing what was |
| // previously in the widget to be torn down. |
| return loadOrRedirectSubframe(completedURL, getNameAttribute(), true); |
| } |
| |
| return loadPlugin(completedURL, mimeType, paramNames, paramValues, |
| useFallback, true); |
| } |
| |
| bool HTMLPlugInElement::canProcessDrag() const { |
| return pluginWidget() && pluginWidget()->isPluginView() && |
| toPluginView(pluginWidget())->canProcessDrag(); |
| } |
| |
| bool HTMLPlugInElement::canStartSelection() const { |
| return useFallbackContent() && Node::canStartSelection(); |
| } |
| |
| bool HTMLPlugInElement::willRespondToMouseClickEvents() { |
| if (isDisabledFormControl()) |
| return false; |
| LayoutObject* r = layoutObject(); |
| return r && (r->isEmbeddedObject() || r->isLayoutPart()); |
| } |
| |
| void HTMLPlugInElement::removeAllEventListeners() { |
| HTMLFrameOwnerElement::removeAllEventListeners(); |
| if (LayoutPart* layoutObject = existingLayoutPart()) { |
| if (Widget* widget = layoutObject->widget()) |
| widget->eventListenersRemoved(); |
| } |
| } |
| |
| void HTMLPlugInElement::didMoveToNewDocument(Document& oldDocument) { |
| if (m_imageLoader) |
| m_imageLoader->elementDidMoveToNewDocument(); |
| HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument); |
| } |
| |
| void HTMLPlugInElement::attachLayoutTree(const AttachContext& context) { |
| HTMLFrameOwnerElement::attachLayoutTree(context); |
| |
| if (!layoutObject() || useFallbackContent()) { |
| // If we don't have a layoutObject we have to dispose of any plugins |
| // which we persisted over a reattach. |
| if (m_persistedPluginWidget) { |
| HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates; |
| setPersistedPluginWidget(nullptr); |
| } |
| return; |
| } |
| |
| if (isImageType()) { |
| if (!m_imageLoader) |
| m_imageLoader = HTMLImageLoader::create(this); |
| m_imageLoader->updateFromElement(); |
| } else if (needsWidgetUpdate() && !layoutEmbeddedItem().isNull() && |
| !layoutEmbeddedItem().showsUnavailablePluginIndicator() && |
| !wouldLoadAsNetscapePlugin(m_url, m_serviceType) && |
| !m_isDelayingLoadEvent) { |
| m_isDelayingLoadEvent = true; |
| document().incrementLoadEventDelayCount(); |
| document().loadPluginsSoon(); |
| } |
| } |
| |
| void HTMLPlugInElement::updateWidget() { |
| updateWidgetInternal(); |
| if (m_isDelayingLoadEvent) { |
| m_isDelayingLoadEvent = false; |
| document().decrementLoadEventDelayCount(); |
| } |
| } |
| |
| void HTMLPlugInElement::removedFrom(ContainerNode* insertionPoint) { |
| // If we've persisted the plugin and we're removed from the tree then |
| // make sure we cleanup the persistance pointer. |
| if (m_persistedPluginWidget) { |
| HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates; |
| setPersistedPluginWidget(nullptr); |
| } |
| HTMLFrameOwnerElement::removedFrom(insertionPoint); |
| } |
| |
| void HTMLPlugInElement::requestPluginCreationWithoutLayoutObjectIfPossible() { |
| if (m_serviceType.isEmpty()) |
| return; |
| |
| if (!document().frame() || |
| !document().frame()->loader().client()->canCreatePluginWithoutRenderer( |
| m_serviceType)) |
| return; |
| |
| if (layoutObject() && layoutObject()->isLayoutPart()) |
| return; |
| |
| createPluginWithoutLayoutObject(); |
| } |
| |
| void HTMLPlugInElement::createPluginWithoutLayoutObject() { |
| DCHECK(document().frame()->loader().client()->canCreatePluginWithoutRenderer( |
| m_serviceType)); |
| |
| KURL url; |
| // CSP can block src-less objects. |
| if (!allowedToLoadObject(url, m_serviceType)) |
| return; |
| |
| Vector<String> paramNames; |
| Vector<String> paramValues; |
| |
| paramNames.append("type"); |
| paramValues.append(m_serviceType); |
| |
| bool useFallback = false; |
| loadPlugin(url, m_serviceType, paramNames, paramValues, useFallback, false); |
| } |
| |
| bool HTMLPlugInElement::shouldAccelerate() const { |
| if (Widget* widget = ownedWidget()) |
| return widget->isPluginView() && toPluginView(widget)->platformLayer(); |
| return false; |
| } |
| |
| void HTMLPlugInElement::detachLayoutTree(const AttachContext& context) { |
| // Update the widget the next time we attach (detaching destroys the plugin). |
| // FIXME: None of this "needsWidgetUpdate" related code looks right. |
| if (layoutObject() && !useFallbackContent()) |
| setNeedsWidgetUpdate(true); |
| if (m_isDelayingLoadEvent) { |
| m_isDelayingLoadEvent = false; |
| document().decrementLoadEventDelayCount(); |
| } |
| |
| // Only try to persist a plugin widget we actually own. |
| Widget* plugin = ownedWidget(); |
| if (plugin && context.performingReattach) { |
| setPersistedPluginWidget(releaseWidget()); |
| } else { |
| // Clear the widget; will trigger disposal of it with Oilpan. |
| setWidget(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 LayoutPart. |
| if (useFallbackContent()) |
| return LayoutObject::createObject(this, style); |
| |
| if (isImageType()) { |
| LayoutImage* image = new LayoutImage(this); |
| image->setImageResource(LayoutImageResource::create()); |
| return image; |
| } |
| |
| m_pluginIsAvailable = true; |
| return new LayoutEmbeddedObject(this); |
| } |
| |
| void HTMLPlugInElement::finishParsingChildren() { |
| HTMLFrameOwnerElement::finishParsingChildren(); |
| if (useFallbackContent()) |
| return; |
| |
| setNeedsWidgetUpdate(true); |
| if (isConnected()) |
| lazyReattachIfNeeded(); |
| } |
| |
| void HTMLPlugInElement::resetInstance() { |
| m_pluginWrapper.clear(); |
| } |
| |
| SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper() { |
| LocalFrame* frame = document().frame(); |
| if (!frame) |
| return nullptr; |
| |
| // 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. |
| if (!m_pluginWrapper) { |
| Widget* plugin; |
| |
| if (m_persistedPluginWidget) |
| plugin = m_persistedPluginWidget.get(); |
| else |
| plugin = pluginWidget(); |
| |
| if (plugin) |
| m_pluginWrapper = frame->script().createPluginWrapper(plugin); |
| } |
| return m_pluginWrapper.get(); |
| } |
| |
| Widget* HTMLPlugInElement::pluginWidget() const { |
| if (LayoutPart* layoutPart = layoutPartForJSBindings()) |
| return layoutPart->widget(); |
| return nullptr; |
| } |
| |
| bool HTMLPlugInElement::isPresentationAttribute( |
| const QualifiedName& name) const { |
| if (name == widthAttr || name == heightAttr || name == vspaceAttr || |
| name == hspaceAttr || name == alignAttr) |
| return true; |
| return HTMLFrameOwnerElement::isPresentationAttribute(name); |
| } |
| |
| void HTMLPlugInElement::collectStyleForPresentationAttribute( |
| const QualifiedName& name, |
| const AtomicString& value, |
| MutableStylePropertySet* style) { |
| if (name == widthAttr) { |
| addHTMLLengthToStyle(style, CSSPropertyWidth, value); |
| } else if (name == heightAttr) { |
| addHTMLLengthToStyle(style, CSSPropertyHeight, value); |
| } else if (name == vspaceAttr) { |
| addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); |
| addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); |
| } else if (name == hspaceAttr) { |
| addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); |
| addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); |
| } else if (name == alignAttr) { |
| 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 = layoutObject(); |
| if (!r || !r->isLayoutPart()) |
| return; |
| if (r->isEmbeddedObject()) { |
| if (LayoutEmbeddedItem(toLayoutEmbeddedObject(r)) |
| .showsUnavailablePluginIndicator()) |
| return; |
| } |
| Widget* widget = toLayoutPart(r)->widget(); |
| if (!widget) |
| return; |
| widget->handleEvent(event); |
| if (event->defaultHandled()) |
| return; |
| HTMLFrameOwnerElement::defaultEventHandler(event); |
| } |
| |
| LayoutPart* HTMLPlugInElement::layoutPartForJSBindings() 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. |
| document().updateStyleAndLayoutIgnorePendingStylesheets( |
| Document::RunPostLayoutTasksSynchronously); |
| return existingLayoutPart(); |
| } |
| |
| bool HTMLPlugInElement::isKeyboardFocusable() const { |
| if (HTMLFrameOwnerElement::isKeyboardFocusable()) |
| return true; |
| return document().isActive() && pluginWidget() && |
| pluginWidget()->isPluginView() && |
| toPluginView(pluginWidget())->supportsKeyboardFocus(); |
| } |
| |
| bool HTMLPlugInElement::hasCustomFocusLogic() const { |
| return !useFallbackContent(); |
| } |
| |
| bool HTMLPlugInElement::isPluginElement() const { |
| return true; |
| } |
| |
| bool HTMLPlugInElement::layoutObjectIsFocusable() const { |
| if (HTMLFrameOwnerElement::supportsFocus() && |
| HTMLFrameOwnerElement::layoutObjectIsFocusable()) |
| return true; |
| |
| if (useFallbackContent() || !HTMLFrameOwnerElement::layoutObjectIsFocusable()) |
| return false; |
| return m_pluginIsAvailable; |
| } |
| |
| bool HTMLPlugInElement::isImageType() { |
| if (m_serviceType.isEmpty() && protocolIs(m_url, "data")) |
| m_serviceType = mimeTypeFromDataURL(m_url); |
| |
| if (LocalFrame* frame = document().frame()) { |
| KURL completedURL = document().completeURL(m_url); |
| return frame->loader().client()->getObjectContentType( |
| completedURL, m_serviceType, shouldPreferPlugInsForImages()) == |
| ObjectContentImage; |
| } |
| |
| return Image::supportsType(m_serviceType); |
| } |
| |
| LayoutEmbeddedItem HTMLPlugInElement::layoutEmbeddedItem() const { |
| // HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects |
| // when using fallback content. |
| if (!layoutObject() || !layoutObject()->isEmbeddedObject()) |
| return LayoutEmbeddedItem(nullptr); |
| return LayoutEmbeddedItem(toLayoutEmbeddedObject(layoutObject())); |
| } |
| |
| // We don't use m_url, as it may not be the final URL that the object loads, |
| // depending on <param> values. |
| bool HTMLPlugInElement::allowedToLoadFrameURL(const String& url) { |
| KURL completeURL = document().completeURL(url); |
| if (contentFrame() && protocolIsJavaScript(completeURL) && |
| !document().getSecurityOrigin()->canAccess( |
| contentFrame()->securityContext()->getSecurityOrigin())) |
| return false; |
| return document().frame()->isURLAllowed(completeURL); |
| } |
| |
| // We don't use m_url, or m_serviceType as they may not be the final values |
| // that <object> uses depending on <param> values. |
| bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String& url, |
| const String& serviceType) { |
| DCHECK(document().frame()); |
| KURL completedURL; |
| if (!url.isEmpty()) |
| completedURL = document().completeURL(url); |
| return document().frame()->loader().client()->getObjectContentType( |
| completedURL, serviceType, shouldPreferPlugInsForImages()) == |
| ObjectContentNetscapePlugin; |
| } |
| |
| bool HTMLPlugInElement::requestObject(const String& url, |
| const String& mimeType, |
| const Vector<String>& paramNames, |
| const Vector<String>& paramValues) { |
| bool result = requestObjectInternal(url, mimeType, paramNames, paramValues); |
| |
| DEFINE_STATIC_LOCAL( |
| EnumerationHistogram, resultHistogram, |
| ("Plugin.RequestObjectResult", PluginRequestObjectResultMax)); |
| resultHistogram.count(result ? PluginRequestObjectResultSuccess |
| : PluginRequestObjectResultFailure); |
| |
| return result; |
| } |
| |
| bool HTMLPlugInElement::loadPlugin(const KURL& url, |
| const String& mimeType, |
| const Vector<String>& paramNames, |
| const Vector<String>& paramValues, |
| bool useFallback, |
| bool requireLayoutObject) { |
| if (!allowedToLoadPlugin(url, mimeType)) |
| return false; |
| |
| LocalFrame* frame = document().frame(); |
| if (!frame->loader().allowPlugins(AboutToInstantiatePlugin)) |
| return false; |
| |
| LayoutEmbeddedItem layoutItem = layoutEmbeddedItem(); |
| // FIXME: This code should not depend on layoutObject! |
| if ((layoutItem.isNull() && requireLayoutObject) || useFallback) |
| return false; |
| |
| VLOG(1) << this << " Plugin URL: " << m_url; |
| VLOG(1) << "Loaded URL: " << url.getString(); |
| m_loadedUrl = url; |
| |
| if (m_persistedPluginWidget) { |
| setWidget(m_persistedPluginWidget.release()); |
| } else { |
| bool loadManually = |
| document().isPluginDocument() && !document().containsPlugins(); |
| FrameLoaderClient::DetachedPluginPolicy policy = |
| requireLayoutObject ? FrameLoaderClient::FailOnDetachedPlugin |
| : FrameLoaderClient::AllowDetachedPlugin; |
| Widget* widget = frame->loader().client()->createPlugin( |
| this, url, paramNames, paramValues, mimeType, loadManually, policy); |
| if (!widget) { |
| if (!layoutItem.isNull() && |
| !layoutItem.showsUnavailablePluginIndicator()) { |
| m_pluginIsAvailable = false; |
| layoutItem.setPluginAvailability(LayoutEmbeddedObject::PluginMissing); |
| } |
| return false; |
| } |
| |
| if (!layoutItem.isNull()) |
| setWidget(widget); |
| else |
| setPersistedPluginWidget(widget); |
| } |
| |
| document().setContainsPlugins(); |
| // TODO(esprehn): WebPluginContainerImpl::setWebLayer 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 = document().frame()->page()) { |
| if (ScrollingCoordinator* scrollingCoordinator = |
| page->scrollingCoordinator()) |
| scrollingCoordinator->notifyGeometryChanged(); |
| } |
| return true; |
| } |
| |
| bool HTMLPlugInElement::shouldUsePlugin(const KURL& url, |
| const String& mimeType, |
| bool hasFallback, |
| bool& useFallback) { |
| ObjectContentType objectType = |
| document().frame()->loader().client()->getObjectContentType( |
| url, mimeType, shouldPreferPlugInsForImages()); |
| // 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. |
| useFallback = objectType == ObjectContentNone && hasFallback; |
| return objectType == ObjectContentNone || |
| objectType == ObjectContentNetscapePlugin; |
| } |
| |
| void HTMLPlugInElement::dispatchErrorEvent() { |
| if (document().isPluginDocument() && document().localOwner()) |
| document().localOwner()->dispatchEvent( |
| Event::create(EventTypeNames::error)); |
| else |
| dispatchEvent(Event::create(EventTypeNames::error)); |
| } |
| |
| bool HTMLPlugInElement::allowedToLoadObject(const KURL& url, |
| const String& mimeType) { |
| if (url.isEmpty() && mimeType.isEmpty()) |
| return false; |
| |
| LocalFrame* frame = document().frame(); |
| Settings* settings = frame->settings(); |
| if (!settings) |
| return false; |
| |
| if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) |
| return false; |
| |
| if (!document().getSecurityOrigin()->canDisplay(url)) { |
| FrameLoader::reportLocalLoadFailed(frame, url.getString()); |
| return false; |
| } |
| |
| AtomicString declaredMimeType = fastGetAttribute(HTMLNames::typeAttr); |
| if (!document().contentSecurityPolicy()->allowObjectFromSource(url) || |
| !document().contentSecurityPolicy()->allowPluginTypeForDocument( |
| document(), mimeType, declaredMimeType, url)) { |
| if (LayoutEmbeddedItem layoutItem = layoutEmbeddedItem()) { |
| m_pluginIsAvailable = false; |
| layoutItem.setPluginAvailability( |
| LayoutEmbeddedObject::PluginBlockedByContentSecurityPolicy); |
| } |
| return false; |
| } |
| // If the URL is empty, a plugin could still be instantiated if a MIME-type |
| // is specified. |
| return (!mimeType.isEmpty() && url.isEmpty()) || |
| !MixedContentChecker::shouldBlockFetch( |
| frame, WebURLRequest::RequestContextObject, |
| WebURLRequest::FrameTypeNone, |
| ResourceRequest::RedirectStatus::NoRedirect, url); |
| } |
| |
| bool HTMLPlugInElement::allowedToLoadPlugin(const KURL& url, |
| const String& mimeType) { |
| if (document().isSandboxed(SandboxPlugins)) { |
| document().addConsoleMessage(ConsoleMessage::create( |
| SecurityMessageSource, ErrorMessageLevel, |
| "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(HTMLContentElement::create(document())); |
| } |
| |
| bool HTMLPlugInElement::hasFallbackContent() const { |
| return false; |
| } |
| |
| bool HTMLPlugInElement::useFallbackContent() const { |
| return false; |
| } |
| |
| void HTMLPlugInElement::lazyReattachIfNeeded() { |
| if (!useFallbackContent() && needsWidgetUpdate() && layoutObject() && |
| !isImageType()) { |
| lazyReattachIfAttached(); |
| setPersistedPluginWidget(nullptr); |
| } |
| } |
| |
| } // namespace blink |