diff options
Diffstat (limited to 'Source/WebCore/html/HTMLPlugInElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLPlugInElement.cpp | 265 |
1 files changed, 215 insertions, 50 deletions
diff --git a/Source/WebCore/html/HTMLPlugInElement.cpp b/Source/WebCore/html/HTMLPlugInElement.cpp index df93b3dae..67eac2a87 100644 --- a/Source/WebCore/html/HTMLPlugInElement.cpp +++ b/Source/WebCore/html/HTMLPlugInElement.cpp @@ -2,7 +2,7 @@ * 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. + * Copyright (C) 2004, 2005, 2006, 2014 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,7 +23,6 @@ #include "config.h" #include "HTMLPlugInElement.h" -#include "Attribute.h" #include "BridgeJSC.h" #include "Chrome.h" #include "ChromeClient.h" @@ -35,32 +34,46 @@ #include "FrameLoader.h" #include "FrameTree.h" #include "HTMLNames.h" +#include "Logging.h" +#include "MIMETypeRegistry.h" #include "Page.h" +#include "PluginData.h" +#include "PluginReplacement.h" #include "PluginViewBase.h" #include "RenderEmbeddedObject.h" #include "RenderSnapshottedPlugIn.h" #include "RenderWidget.h" +#include "RuntimeEnabledFeatures.h" #include "ScriptController.h" #include "Settings.h" +#include "ShadowRoot.h" +#include "SubframeLoader.h" #include "Widget.h" #if ENABLE(NETSCAPE_PLUGIN_API) #include "npruntime_impl.h" #endif +#if PLATFORM(COCOA) +#include "QuickTimePluginReplacement.h" +#include "YouTubePluginReplacement.h" +#endif + namespace WebCore { using namespace HTMLNames; -HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document* doc) - : HTMLFrameOwnerElement(tagName, doc) +HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& document) + : HTMLFrameOwnerElement(tagName, document) , m_inBeforeLoadEventHandler(false) + , m_swapRendererTimer(*this, &HTMLPlugInElement::swapRendererTimerFired) #if ENABLE(NETSCAPE_PLUGIN_API) , m_NPObject(0) #endif , m_isCapturingMouseEvents(false) , m_displayState(Playing) { + setHasCustomStyleResolveCallbacks(); } HTMLPlugInElement::~HTMLPlugInElement() @@ -77,7 +90,7 @@ HTMLPlugInElement::~HTMLPlugInElement() bool HTMLPlugInElement::canProcessDrag() const { - const PluginViewBase* plugin = pluginWidget() && pluginWidget()->isPluginViewBase() ? static_cast<const PluginViewBase*>(pluginWidget()) : 0; + const PluginViewBase* plugin = is<PluginViewBase>(pluginWidget()) ? downcast<PluginViewBase>(pluginWidget()) : nullptr; return plugin ? plugin->canProcessDrag() : false; } @@ -85,21 +98,17 @@ bool HTMLPlugInElement::willRespondToMouseClickEvents() { if (isDisabledFormControl()) return false; - RenderObject* r = renderer(); - if (!r) - return false; - if (!r->isEmbeddedObject() && !r->isWidget()) - return false; - return true; + auto renderer = this->renderer(); + return renderer && renderer->isWidget(); } -void HTMLPlugInElement::detach(const AttachContext& context) +void HTMLPlugInElement::willDetachRenderers() { - m_instance.clear(); + m_instance = nullptr; if (m_isCapturingMouseEvents) { - if (Frame* frame = document()->frame()) - frame->eventHandler()->setCapturingMouseEventsNode(0); + if (Frame* frame = document().frame()) + frame->eventHandler().setCapturingMouseEventsElement(nullptr); m_isCapturingMouseEvents = false; } @@ -109,18 +118,16 @@ void HTMLPlugInElement::detach(const AttachContext& context) m_NPObject = 0; } #endif - - HTMLFrameOwnerElement::detach(context); } void HTMLPlugInElement::resetInstance() { - m_instance.clear(); + m_instance = nullptr; } PassRefPtr<JSC::Bindings::Instance> HTMLPlugInElement::getInstance() { - Frame* frame = document()->frame(); + Frame* frame = document().frame(); if (!frame) return 0; @@ -130,7 +137,7 @@ PassRefPtr<JSC::Bindings::Instance> HTMLPlugInElement::getInstance() return m_instance; if (Widget* widget = pluginWidget()) - m_instance = frame->script()->createScriptInstanceForWidget(widget); + m_instance = frame->script().createScriptInstanceForWidget(widget); return m_instance; } @@ -150,17 +157,17 @@ bool HTMLPlugInElement::guardedDispatchBeforeLoadEvent(const String& sourceURL) return beforeLoadAllowedLoad; } -Widget* HTMLPlugInElement::pluginWidget() const +Widget* HTMLPlugInElement::pluginWidget(PluginLoadingPolicy loadPolicy) const { if (m_inBeforeLoadEventHandler) { // The plug-in hasn't loaded yet, and it makes no sense to try to load if beforeload handler happened to touch the plug-in element. // That would recursively call beforeload for the same element. - return 0; + return nullptr; } - RenderWidget* renderWidget = renderWidgetForJSBindings(); + RenderWidget* renderWidget = loadPolicy == PluginLoadingPolicy::Load ? renderWidgetLoadingPlugin() : this->renderWidget(); if (!renderWidget) - return 0; + return nullptr; return renderWidget->widget(); } @@ -172,7 +179,7 @@ bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const return HTMLFrameOwnerElement::isPresentationAttribute(name); } -void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) +void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style) { if (name == widthAttr) addHTMLLengthToStyle(style, CSSPropertyWidth, value); @@ -198,15 +205,18 @@ void HTMLPlugInElement::defaultEventHandler(Event* event) // FIXME: Mouse down and scroll events are passed down to plug-in via custom code in EventHandler; these code paths should be united. - RenderObject* r = renderer(); - if (r && r->isEmbeddedObject()) { - if (toRenderEmbeddedObject(r)->isPluginUnavailable()) { - toRenderEmbeddedObject(r)->handleUnavailablePluginIndicatorEvent(event); + auto renderer = this->renderer(); + if (!is<RenderWidget>(renderer)) + return; + + if (is<RenderEmbeddedObject>(*renderer)) { + if (downcast<RenderEmbeddedObject>(*renderer).isPluginUnavailable()) { + downcast<RenderEmbeddedObject>(*renderer).handleUnavailablePluginIndicatorEvent(event); return; } - if (r->isSnapshottedPlugIn() && displayState() < Restarting) { - toRenderSnapshottedPlugIn(r)->handleEvent(event); + if (is<RenderSnapshottedPlugIn>(*renderer) && displayState() < Restarting) { + downcast<RenderSnapshottedPlugIn>(*renderer).handleEvent(event); HTMLFrameOwnerElement::defaultEventHandler(event); return; } @@ -215,28 +225,29 @@ void HTMLPlugInElement::defaultEventHandler(Event* event) return; } - if (!r || !r->isWidget()) - return; - RefPtr<Widget> widget = toRenderWidget(r)->widget(); - if (!widget) - return; - widget->handleEvent(event); - if (event->defaultHandled()) - return; + // Don't keep the widget alive over the defaultEventHandler call, since that can do things like navigate. + { + RefPtr<Widget> widget = downcast<RenderWidget>(*renderer).widget(); + if (!widget) + return; + widget->handleEvent(event); + if (event->defaultHandled()) + return; + } HTMLFrameOwnerElement::defaultEventHandler(event); } -bool HTMLPlugInElement::isKeyboardFocusable(KeyboardEvent* event) const +bool HTMLPlugInElement::isKeyboardFocusable(KeyboardEvent*) const { - UNUSED_PARAM(event); - if (!document()->page()) + // FIXME: Why is this check needed? + if (!document().page()) return false; - const PluginViewBase* plugin = pluginWidget() && pluginWidget()->isPluginViewBase() ? static_cast<const PluginViewBase*>(pluginWidget()) : 0; - if (plugin) - return plugin->supportsKeyboardFocus(); + Widget* widget = pluginWidget(); + if (!is<PluginViewBase>(widget)) + return false; - return false; + return downcast<PluginViewBase>(*widget).supportsKeyboardFocus(); } bool HTMLPlugInElement::isPluginElement() const @@ -244,26 +255,180 @@ bool HTMLPlugInElement::isPluginElement() const return true; } +bool HTMLPlugInElement::isUserObservable() const +{ + // No widget - can't be anything to see or hear here. + Widget* widget = pluginWidget(PluginLoadingPolicy::DoNotLoad); + if (!is<PluginViewBase>(widget)) + return false; + + PluginViewBase& pluginView = downcast<PluginViewBase>(*widget); + + // If audio is playing (or might be) then the plugin is detectable. + if (pluginView.audioHardwareActivity() != AudioHardwareActivityType::IsInactive) + return true; + + // If the plugin is visible and not vanishingly small in either dimension it is detectable. + return pluginView.isVisible() && pluginView.width() > 2 && pluginView.height() > 2; +} + bool HTMLPlugInElement::supportsFocus() const { if (HTMLFrameOwnerElement::supportsFocus()) return true; - if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject()) + if (useFallbackContent() || !is<RenderEmbeddedObject>(renderer())) return false; - return !toRenderEmbeddedObject(renderer())->isPluginUnavailable(); + return !downcast<RenderEmbeddedObject>(*renderer()).isPluginUnavailable(); } #if ENABLE(NETSCAPE_PLUGIN_API) NPObject* HTMLPlugInElement::getNPObject() { - ASSERT(document()->frame()); + ASSERT(document().frame()); if (!m_NPObject) - m_NPObject = document()->frame()->script()->createScriptObjectForPluginElement(this); + m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this); return m_NPObject; } #endif /* ENABLE(NETSCAPE_PLUGIN_API) */ +RenderPtr<RenderElement> HTMLPlugInElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition& insertionPosition) +{ + if (m_pluginReplacement && m_pluginReplacement->willCreateRenderer()) + return m_pluginReplacement->createElementRenderer(*this, WTFMove(style), insertionPosition); + + return createRenderer<RenderEmbeddedObject>(*this, WTFMove(style)); +} + +void HTMLPlugInElement::swapRendererTimerFired() +{ + ASSERT(displayState() == PreparingPluginReplacement || displayState() == DisplayingSnapshot); + if (userAgentShadowRoot()) + return; + + // Create a shadow root, which will trigger the code to add a snapshot container + // and reattach, thus making a new Renderer. + ensureUserAgentShadowRoot(); +} + +void HTMLPlugInElement::setDisplayState(DisplayState state) +{ + m_displayState = state; + + if ((state == DisplayingSnapshot || displayState() == PreparingPluginReplacement) && !m_swapRendererTimer.isActive()) + m_swapRendererTimer.startOneShot(0); +} + +void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot* root) +{ + if (!m_pluginReplacement || !document().page() || displayState() != PreparingPluginReplacement) + return; + + root->setResetStyleInheritance(true); + if (m_pluginReplacement->installReplacement(root)) { + setDisplayState(DisplayingPluginReplacement); + setNeedsStyleRecalc(ReconstructRenderTree); + } +} + +#if PLATFORM(COCOA) +static void registrar(const ReplacementPlugin&); +#endif + +static Vector<ReplacementPlugin*>& registeredPluginReplacements() +{ + static NeverDestroyed<Vector<ReplacementPlugin*>> registeredReplacements; + static bool enginesQueried = false; + + if (enginesQueried) + return registeredReplacements; + enginesQueried = true; + +#if PLATFORM(COCOA) + QuickTimePluginReplacement::registerPluginReplacement(registrar); + YouTubePluginReplacement::registerPluginReplacement(registrar); +#endif + + return registeredReplacements; +} + +#if PLATFORM(COCOA) +static void registrar(const ReplacementPlugin& replacement) +{ + registeredPluginReplacements().append(new ReplacementPlugin(replacement)); +} +#endif + +static ReplacementPlugin* pluginReplacementForType(const URL& url, const String& mimeType) +{ + Vector<ReplacementPlugin*>& replacements = registeredPluginReplacements(); + if (replacements.isEmpty()) + return nullptr; + + String extension; + String lastPathComponent = url.lastPathComponent(); + size_t dotOffset = lastPathComponent.reverseFind('.'); + if (dotOffset != notFound) + extension = lastPathComponent.substring(dotOffset + 1); + + String type = mimeType; + if (type.isEmpty() && url.protocolIsData()) + type = mimeTypeFromDataURL(url.string()); + + if (type.isEmpty() && !extension.isEmpty()) { + for (auto* replacement : replacements) { + if (replacement->supportsFileExtension(extension) && replacement->supportsURL(url)) + return replacement; + } + } + + if (type.isEmpty()) { + if (extension.isEmpty()) + return nullptr; + type = MIMETypeRegistry::getMediaMIMETypeForExtension(extension); + } + + if (type.isEmpty()) + return nullptr; + + for (auto* replacement : replacements) { + if (replacement->supportsType(type) && replacement->supportsURL(url)) + return replacement; + } + + return nullptr; +} + +bool HTMLPlugInElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) +{ + if (!RuntimeEnabledFeatures::sharedFeatures().pluginReplacementEnabled()) + return false; + + if (m_pluginReplacement) + return true; + + URL completedURL; + if (!url.isEmpty()) + completedURL = document().completeURL(url); + + ReplacementPlugin* replacement = pluginReplacementForType(completedURL, mimeType); + if (!replacement) + return false; + + LOG(Plugins, "%p - Found plug-in replacement for %s.", this, completedURL.string().utf8().data()); + + m_pluginReplacement = replacement->create(*this, paramNames, paramValues); + setDisplayState(PreparingPluginReplacement); + return true; +} + +JSC::JSObject* HTMLPlugInElement::scriptObjectForPluginReplacement() +{ + if (m_pluginReplacement) + return m_pluginReplacement->scriptObject(); + return nullptr; +} + } |