summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLPlugInElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/HTMLPlugInElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLPlugInElement.cpp265
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;
+}
+
}