2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2000 Stefan Schimanski (1Stein@gmx.de)
5 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 #include "core/html/HTMLPlugInElement.h"
26 #include "bindings/core/v8/ScriptController.h"
27 #include "bindings/core/v8/npruntime_impl.h"
28 #include "core/CSSPropertyNames.h"
29 #include "core/HTMLNames.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/Node.h"
32 #include "core/dom/shadow/ShadowRoot.h"
33 #include "core/events/Event.h"
34 #include "core/frame/FrameView.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/Settings.h"
37 #include "core/frame/csp/ContentSecurityPolicy.h"
38 #include "core/html/HTMLContentElement.h"
39 #include "core/html/HTMLImageLoader.h"
40 #include "core/html/PluginDocument.h"
41 #include "core/loader/FrameLoaderClient.h"
42 #include "core/page/EventHandler.h"
43 #include "core/page/Page.h"
44 #include "core/page/scrolling/ScrollingCoordinator.h"
45 #include "core/plugins/PluginPlaceholder.h"
46 #include "core/plugins/PluginView.h"
47 #include "core/rendering/RenderBlockFlow.h"
48 #include "core/rendering/RenderEmbeddedObject.h"
49 #include "core/rendering/RenderImage.h"
50 #include "core/rendering/RenderPart.h"
51 #include "platform/Logging.h"
52 #include "platform/MIMETypeFromURL.h"
53 #include "platform/MIMETypeRegistry.h"
54 #include "platform/Widget.h"
55 #include "platform/plugins/PluginData.h"
59 using namespace HTMLNames;
61 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& doc, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
62 : HTMLFrameOwnerElement(tagName, doc)
63 , m_isDelayingLoadEvent(false)
65 , m_isCapturingMouseEvents(false)
66 // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
67 // widget updates until after all children are parsed. For HTMLEmbedElement
68 // this delay is unnecessary, but it is simpler to make both classes share
69 // the same codepath in this class.
70 , m_needsWidgetUpdate(!createdByParser)
71 , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
75 HTMLPlugInElement::~HTMLPlugInElement()
77 ASSERT(!m_pluginWrapper); // cleared in detach()
78 ASSERT(!m_isDelayingLoadEvent);
81 _NPN_ReleaseObject(m_NPObject);
86 void HTMLPlugInElement::trace(Visitor* visitor)
88 visitor->trace(m_imageLoader);
89 visitor->trace(m_placeholder);
90 visitor->trace(m_persistedPluginWidget);
91 HTMLFrameOwnerElement::trace(visitor);
95 void HTMLPlugInElement::disconnectContentFrame()
97 if (m_persistedPluginWidget) {
98 m_persistedPluginWidget->dispose();
99 m_persistedPluginWidget = nullptr;
101 HTMLFrameOwnerElement::disconnectContentFrame();
104 void HTMLPlugInElement::shouldDisposePlugin()
106 if (m_persistedPluginWidget && m_persistedPluginWidget->isPluginView())
107 toPluginView(m_persistedPluginWidget.get())->shouldDisposePlugin();
111 void HTMLPlugInElement::setPersistedPluginWidget(Widget* widget)
113 if (m_persistedPluginWidget == widget)
116 if (m_persistedPluginWidget && m_persistedPluginWidget->isPluginView()) {
117 LocalFrame* frame = toPluginView(m_persistedPluginWidget.get())->pluginFrame();
119 frame->unregisterPluginElement(this);
121 if (widget && widget->isPluginView()) {
122 LocalFrame* frame = toPluginView(widget)->pluginFrame();
124 frame->registerPluginElement(this);
127 m_persistedPluginWidget = widget;
130 bool HTMLPlugInElement::canProcessDrag() const
132 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
135 bool HTMLPlugInElement::willRespondToMouseClickEvents()
137 if (isDisabledFormControl())
139 RenderObject* r = renderer();
140 return r && (r->isEmbeddedObject() || r->isRenderPart());
143 void HTMLPlugInElement::removeAllEventListeners()
145 HTMLFrameOwnerElement::removeAllEventListeners();
146 if (RenderPart* renderer = existingRenderPart()) {
147 if (Widget* widget = renderer->widget())
148 widget->eventListenersRemoved();
152 void HTMLPlugInElement::didMoveToNewDocument(Document& oldDocument)
155 m_imageLoader->elementDidMoveToNewDocument();
156 HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument);
159 void HTMLPlugInElement::attach(const AttachContext& context)
161 HTMLFrameOwnerElement::attach(context);
163 if (!renderer() || useFallbackContent())
168 m_imageLoader = HTMLImageLoader::create(this);
169 m_imageLoader->updateFromElement();
170 } else if (needsWidgetUpdate()
171 && renderEmbeddedObject()
172 && !renderEmbeddedObject()->showsUnavailablePluginIndicator()
173 && !wouldLoadAsNetscapePlugin(m_url, m_serviceType)
174 && !m_isDelayingLoadEvent) {
175 m_isDelayingLoadEvent = true;
176 document().incrementLoadEventDelayCount();
177 document().loadPluginsSoon();
181 void HTMLPlugInElement::updateWidget()
183 RefPtrWillBeRawPtr<HTMLPlugInElement> protector(this);
184 updateWidgetInternal();
185 if (m_isDelayingLoadEvent) {
186 m_isDelayingLoadEvent = false;
187 document().decrementLoadEventDelayCount();
191 void HTMLPlugInElement::requestPluginCreationWithoutRendererIfPossible()
193 if (m_serviceType.isEmpty())
196 if (!document().frame()
197 || !document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType))
200 if (renderer() && renderer()->isRenderPart())
203 createPluginWithoutRenderer();
206 void HTMLPlugInElement::createPluginWithoutRenderer()
208 ASSERT(document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType));
211 Vector<String> paramNames;
212 Vector<String> paramValues;
214 paramNames.append("type");
215 paramValues.append(m_serviceType);
217 bool useFallback = false;
218 loadPlugin(url, m_serviceType, paramNames, paramValues, useFallback, false);
221 bool HTMLPlugInElement::shouldAccelerate() const
223 if (Widget* widget = ownedWidget())
224 return widget->isPluginView() && toPluginView(widget)->platformLayer();
228 void HTMLPlugInElement::detach(const AttachContext& context)
230 // Update the widget the next time we attach (detaching destroys the plugin).
231 // FIXME: None of this "needsWidgetUpdate" related code looks right.
232 if (renderer() && !useFallbackContent())
233 setNeedsWidgetUpdate(true);
234 if (m_isDelayingLoadEvent) {
235 m_isDelayingLoadEvent = false;
236 document().decrementLoadEventDelayCount();
239 // Only try to persist a plugin widget we actually own.
240 Widget* plugin = ownedWidget();
241 if (plugin && plugin->pluginShouldPersist())
242 setPersistedPluginWidget(plugin);
245 // Clear the widget; will trigger disposal of it with Oilpan.
248 if (m_isCapturingMouseEvents) {
249 if (LocalFrame* frame = document().frame())
250 frame->eventHandler().setCapturingMouseEventsNode(nullptr);
251 m_isCapturingMouseEvents = false;
255 _NPN_ReleaseObject(m_NPObject);
259 HTMLFrameOwnerElement::detach(context);
262 RenderObject* HTMLPlugInElement::createRenderer(RenderStyle* style)
264 // Fallback content breaks the DOM->Renderer class relationship of this
265 // class and all superclasses because createObject won't necessarily return
266 // a RenderEmbeddedObject or RenderPart.
267 if (useFallbackContent())
268 return RenderObject::createObject(this, style);
271 RenderImage* image = new RenderImage(this);
272 image->setImageResource(RenderImageResource::create());
276 if (usePlaceholderContent())
277 return new RenderBlockFlow(this);
279 return new RenderEmbeddedObject(this);
282 void HTMLPlugInElement::finishParsingChildren()
284 HTMLFrameOwnerElement::finishParsingChildren();
285 if (useFallbackContent())
288 setNeedsWidgetUpdate(true);
290 lazyReattachIfNeeded();
293 void HTMLPlugInElement::resetInstance()
295 m_pluginWrapper.clear();
298 SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper()
300 LocalFrame* frame = document().frame();
304 // If the host dynamically turns off JavaScript (or Java) we will still
305 // return the cached allocated Bindings::Instance. Not supporting this
307 if (!m_pluginWrapper) {
310 if (m_persistedPluginWidget)
311 plugin = m_persistedPluginWidget.get();
313 plugin = pluginWidget();
316 m_pluginWrapper = frame->script().createPluginWrapper(plugin);
318 return m_pluginWrapper.get();
321 Widget* HTMLPlugInElement::pluginWidget() const
323 if (RenderPart* renderPart = renderPartForJSBindings())
324 return renderPart->widget();
328 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const
330 if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr)
332 return HTMLFrameOwnerElement::isPresentationAttribute(name);
335 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
337 if (name == widthAttr) {
338 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
339 } else if (name == heightAttr) {
340 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
341 } else if (name == vspaceAttr) {
342 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
343 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
344 } else if (name == hspaceAttr) {
345 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
346 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
347 } else if (name == alignAttr) {
348 applyAlignmentAttributeToStyle(value, style);
350 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name, value, style);
354 void HTMLPlugInElement::defaultEventHandler(Event* event)
356 // Firefox seems to use a fake event listener to dispatch events to plug-in
357 // (tested with mouse events only). This is observable via different order
358 // of events - in Firefox, event listeners specified in HTML attributes
359 // fires first, then an event gets dispatched to plug-in, and only then
360 // other event listeners fire. Hopefully, this difference does not matter in
363 // FIXME: Mouse down and scroll events are passed down to plug-in via custom
364 // code in EventHandler; these code paths should be united.
366 RenderObject* r = renderer();
367 if (!r || !r->isRenderPart())
369 if (r->isEmbeddedObject()) {
370 if (toRenderEmbeddedObject(r)->showsUnavailablePluginIndicator())
373 RefPtrWillBeRawPtr<Widget> widget = toRenderPart(r)->widget();
376 widget->handleEvent(event);
377 if (event->defaultHandled())
379 HTMLFrameOwnerElement::defaultEventHandler(event);
382 RenderPart* HTMLPlugInElement::renderPartForJSBindings() const
384 // Needs to load the plugin immediatedly because this function is called
385 // when JavaScript code accesses the plugin.
386 // FIXME: Check if dispatching events here is safe.
387 document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously);
388 return existingRenderPart();
391 bool HTMLPlugInElement::isKeyboardFocusable() const
393 if (!document().isActive())
395 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
398 bool HTMLPlugInElement::hasCustomFocusLogic() const
400 return !hasAuthorShadowRoot();
403 bool HTMLPlugInElement::isPluginElement() const
408 bool HTMLPlugInElement::rendererIsFocusable() const
410 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::rendererIsFocusable())
413 if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject())
415 return !toRenderEmbeddedObject(renderer())->showsUnavailablePluginIndicator();
418 NPObject* HTMLPlugInElement::getNPObject()
420 ASSERT(document().frame());
422 m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this);
426 bool HTMLPlugInElement::isImageType()
428 if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
429 m_serviceType = mimeTypeFromDataURL(m_url);
431 if (LocalFrame* frame = document().frame()) {
432 KURL completedURL = document().completeURL(m_url);
433 return frame->loader().client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
436 return Image::supportsType(m_serviceType);
439 RenderEmbeddedObject* HTMLPlugInElement::renderEmbeddedObject() const
441 // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
442 // when using fallback content.
443 if (!renderer() || !renderer()->isEmbeddedObject())
445 return toRenderEmbeddedObject(renderer());
448 // We don't use m_url, as it may not be the final URL that the object loads,
449 // depending on <param> values.
450 bool HTMLPlugInElement::allowedToLoadFrameURL(const String& url)
452 KURL completeURL = document().completeURL(url);
453 if (contentFrame() && protocolIsJavaScript(completeURL)
454 && !document().securityOrigin()->canAccess(contentDocument()->securityOrigin()))
456 return document().frame()->isURLAllowed(completeURL);
459 // We don't use m_url, or m_serviceType as they may not be the final values
460 // that <object> uses depending on <param> values.
461 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
463 ASSERT(document().frame());
466 completedURL = document().completeURL(url);
467 return document().frame()->loader().client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin;
470 bool HTMLPlugInElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
472 if (url.isEmpty() && mimeType.isEmpty())
475 if (protocolIsJavaScript(url))
478 // FIXME: None of this code should use renderers!
479 RenderEmbeddedObject* renderer = renderEmbeddedObject();
484 KURL completedURL = document().completeURL(url);
485 if (!pluginIsLoadable(completedURL, mimeType))
489 bool requireRenderer = true;
490 if (shouldUsePlugin(completedURL, mimeType, hasFallbackContent(), useFallback))
491 return loadPlugin(completedURL, mimeType, paramNames, paramValues, useFallback, requireRenderer);
493 // If the plug-in element already contains a subframe,
494 // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
495 // frame and set it as the RenderPart's widget, causing what was previously
496 // in the widget to be torn down.
497 return loadOrRedirectSubframe(completedURL, getNameAttribute(), true);
500 bool HTMLPlugInElement::loadPlugin(const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback, bool requireRenderer)
502 LocalFrame* frame = document().frame();
504 if (!frame->loader().allowPlugins(AboutToInstantiatePlugin))
507 RenderEmbeddedObject* renderer = renderEmbeddedObject();
508 // FIXME: This code should not depend on renderer!
509 if ((!renderer && requireRenderer) || useFallback)
512 WTF_LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
513 WTF_LOG(Plugins, " Loaded URL: %s", url.string().utf8().data());
516 OwnPtrWillBeRawPtr<PluginPlaceholder> placeholder = nullptr;
517 RefPtrWillBeRawPtr<Widget> widget = m_persistedPluginWidget;
519 bool loadManually = document().isPluginDocument() && !document().containsPlugins();
520 placeholder = frame->loader().client()->createPluginPlaceholder(document(), url, paramNames, paramValues, mimeType, loadManually);
522 FrameLoaderClient::DetachedPluginPolicy policy = requireRenderer ? FrameLoaderClient::FailOnDetachedPlugin : FrameLoaderClient::AllowDetachedPlugin;
523 widget = frame->loader().client()->createPlugin(this, url, paramNames, paramValues, mimeType, loadManually, policy);
527 if (!placeholder && !widget) {
528 if (renderer && !renderer->showsUnavailablePluginIndicator())
529 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
530 setPlaceholder(nullptr);
535 setPlaceholder(placeholder.release());
541 setPersistedPluginWidget(nullptr);
543 setPersistedPluginWidget(widget.get());
545 setPlaceholder(nullptr);
546 document().setContainsPlugins();
547 scheduleSVGFilterLayerUpdateHack();
548 // Make sure any input event handlers introduced by the plugin are taken into account.
549 if (Page* page = document().frame()->page()) {
550 if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator())
551 scrollingCoordinator->notifyLayoutUpdated();
556 bool HTMLPlugInElement::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
558 // Allow other plug-ins to win over QuickTime because if the user has
559 // installed a plug-in that can handle TIFF (which QuickTime can also
560 // handle) they probably intended to override QT.
561 if (document().frame()->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
562 const PluginData* pluginData = document().frame()->page()->pluginData();
563 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
564 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
568 ObjectContentType objectType = document().frame()->loader().client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages());
569 // If an object's content can't be handled and it has no fallback, let
570 // it be handled as a plugin to show the broken plugin icon.
571 useFallback = objectType == ObjectContentNone && hasFallback;
572 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
575 void HTMLPlugInElement::setPlaceholder(PassOwnPtrWillBeRawPtr<PluginPlaceholder> placeholder)
577 bool needsLazyReattach = (!placeholder) != (!m_placeholder);
579 placeholder->loadIntoContainer(ensureUserAgentShadowRoot());
580 m_placeholder = placeholder;
582 ShadowRoot& shadowRoot = ensureUserAgentShadowRoot();
583 shadowRoot.removeChildren();
584 shadowRoot.appendChild(HTMLContentElement::create(document()));
585 m_placeholder.clear();
587 if (needsLazyReattach)
588 lazyReattachIfAttached();
591 void HTMLPlugInElement::dispatchErrorEvent()
593 if (document().isPluginDocument() && document().ownerElement())
594 document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error));
596 dispatchEvent(Event::create(EventTypeNames::error));
599 bool HTMLPlugInElement::pluginIsLoadable(const KURL& url, const String& mimeType)
601 LocalFrame* frame = document().frame();
602 Settings* settings = frame->settings();
606 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType) && !settings->javaEnabled())
609 if (document().isSandboxed(SandboxPlugins))
612 if (!document().securityOrigin()->canDisplay(url)) {
613 FrameLoader::reportLocalLoadFailed(frame, url.string());
617 AtomicString declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
618 document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
619 fastGetAttribute(HTMLNames::typeAttr);
620 if (!document().contentSecurityPolicy()->allowObjectFromSource(url)
621 || !document().contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
622 renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
626 return frame->loader().mixedContentChecker()->canRunInsecureContent(document().securityOrigin(), url);
629 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot&)
631 userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
634 void HTMLPlugInElement::willAddFirstAuthorShadowRoot()
636 lazyReattachIfAttached();
639 bool HTMLPlugInElement::hasFallbackContent() const
644 bool HTMLPlugInElement::useFallbackContent() const
646 return hasAuthorShadowRoot();
649 void HTMLPlugInElement::lazyReattachIfNeeded()
651 if (!useFallbackContent() && !usePlaceholderContent() && needsWidgetUpdate() && renderer() && !isImageType())
652 lazyReattachIfAttached();