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/input/EventHandler.h"
42 #include "core/layout/LayoutBlockFlow.h"
43 #include "core/layout/LayoutEmbeddedObject.h"
44 #include "core/layout/LayoutImage.h"
45 #include "core/layout/LayoutPart.h"
46 #include "core/loader/FrameLoaderClient.h"
47 #include "core/loader/MixedContentChecker.h"
48 #include "core/page/Page.h"
49 #include "core/page/scrolling/ScrollingCoordinator.h"
50 #include "core/plugins/PluginPlaceholder.h"
51 #include "core/plugins/PluginView.h"
52 #include "platform/Logging.h"
53 #include "platform/MIMETypeFromURL.h"
54 #include "platform/MIMETypeRegistry.h"
55 #include "platform/Widget.h"
56 #include "platform/plugins/PluginData.h"
57 #include "public/platform/WebURLRequest.h"
61 using namespace HTMLNames
;
63 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName
& tagName
, Document
& doc
, bool createdByParser
, PreferPlugInsForImagesOption preferPlugInsForImagesOption
)
64 : HTMLFrameOwnerElement(tagName
, doc
)
65 , m_isDelayingLoadEvent(false)
67 // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
68 // widget updates until after all children are parsed. For HTMLEmbedElement
69 // this delay is unnecessary, but it is simpler to make both classes share
70 // the same codepath in this class.
71 , m_needsWidgetUpdate(!createdByParser
)
72 , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption
== ShouldPreferPlugInsForImages
)
76 HTMLPlugInElement::~HTMLPlugInElement()
78 ASSERT(!m_pluginWrapper
); // cleared in detach()
79 ASSERT(!m_isDelayingLoadEvent
);
82 _NPN_ReleaseObject(m_NPObject
);
87 DEFINE_TRACE(HTMLPlugInElement
)
89 visitor
->trace(m_imageLoader
);
90 visitor
->trace(m_placeholder
);
91 visitor
->trace(m_persistedPluginWidget
);
92 HTMLFrameOwnerElement::trace(visitor
);
96 void HTMLPlugInElement::disconnectContentFrame()
98 if (m_persistedPluginWidget
) {
99 m_persistedPluginWidget
->dispose();
100 m_persistedPluginWidget
= nullptr;
102 HTMLFrameOwnerElement::disconnectContentFrame();
105 void HTMLPlugInElement::shouldDisposePlugin()
107 if (m_persistedPluginWidget
&& m_persistedPluginWidget
->isPluginView())
108 toPluginView(m_persistedPluginWidget
.get())->shouldDisposePlugin();
112 void HTMLPlugInElement::setPersistedPluginWidget(Widget
* widget
)
114 if (m_persistedPluginWidget
== widget
)
117 if (m_persistedPluginWidget
&& m_persistedPluginWidget
->isPluginView()) {
118 LocalFrame
* frame
= toPluginView(m_persistedPluginWidget
.get())->pluginFrame();
120 frame
->unregisterPluginElement(this);
122 if (widget
&& widget
->isPluginView()) {
123 LocalFrame
* frame
= toPluginView(widget
)->pluginFrame();
125 frame
->registerPluginElement(this);
128 m_persistedPluginWidget
= widget
;
131 bool HTMLPlugInElement::canProcessDrag() const
133 if (Widget
* widget
= existingPluginWidget())
134 return widget
->isPluginView() && toPluginView(widget
)->canProcessDrag();
138 bool HTMLPlugInElement::willRespondToMouseClickEvents()
140 if (isDisabledFormControl())
142 LayoutObject
* r
= layoutObject();
143 return r
&& (r
->isEmbeddedObject() || r
->isLayoutPart());
146 void HTMLPlugInElement::removeAllEventListeners()
148 HTMLFrameOwnerElement::removeAllEventListeners();
149 if (LayoutPart
* layoutObject
= existingLayoutPart()) {
150 if (Widget
* widget
= layoutObject
->widget())
151 widget
->eventListenersRemoved();
155 void HTMLPlugInElement::didMoveToNewDocument(Document
& oldDocument
)
158 m_imageLoader
->elementDidMoveToNewDocument();
159 HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument
);
162 void HTMLPlugInElement::attach(const AttachContext
& context
)
164 HTMLFrameOwnerElement::attach(context
);
166 if (!layoutObject() || useFallbackContent())
171 m_imageLoader
= HTMLImageLoader::create(this);
172 m_imageLoader
->updateFromElement();
173 } else if (needsWidgetUpdate()
174 && layoutEmbeddedObject()
175 && !layoutEmbeddedObject()->showsUnavailablePluginIndicator()
176 && !wouldLoadAsNetscapePlugin(m_url
, m_serviceType
)
177 && !m_isDelayingLoadEvent
) {
178 m_isDelayingLoadEvent
= true;
179 document().incrementLoadEventDelayCount();
180 document().loadPluginsSoon();
184 void HTMLPlugInElement::updateWidget()
186 RefPtrWillBeRawPtr
<HTMLPlugInElement
> protector(this);
187 updateWidgetInternal();
188 if (m_isDelayingLoadEvent
) {
189 m_isDelayingLoadEvent
= false;
190 document().decrementLoadEventDelayCount();
194 void HTMLPlugInElement::requestPluginCreationWithoutLayoutObjectIfPossible()
196 if (m_serviceType
.isEmpty())
199 if (!document().frame()
200 || !document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType
))
203 if (layoutObject() && layoutObject()->isLayoutPart())
206 createPluginWithoutLayoutObject();
209 void HTMLPlugInElement::createPluginWithoutLayoutObject()
211 ASSERT(document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType
));
214 Vector
<String
> paramNames
;
215 Vector
<String
> paramValues
;
217 paramNames
.append("type");
218 paramValues
.append(m_serviceType
);
220 bool useFallback
= false;
221 loadPlugin(url
, m_serviceType
, paramNames
, paramValues
, useFallback
, false);
224 bool HTMLPlugInElement::shouldAccelerate() const
226 if (Widget
* widget
= ownedWidget())
227 return widget
->isPluginView() && toPluginView(widget
)->platformLayer();
231 void HTMLPlugInElement::detach(const AttachContext
& context
)
233 // Update the widget the next time we attach (detaching destroys the plugin).
234 // FIXME: None of this "needsWidgetUpdate" related code looks right.
235 if (layoutObject() && !useFallbackContent())
236 setNeedsWidgetUpdate(true);
237 if (m_isDelayingLoadEvent
) {
238 m_isDelayingLoadEvent
= false;
239 document().decrementLoadEventDelayCount();
242 // Only try to persist a plugin widget we actually own.
243 Widget
* plugin
= ownedWidget();
244 if (plugin
&& plugin
->pluginShouldPersist())
245 setPersistedPluginWidget(plugin
);
248 // Clear the widget; will trigger disposal of it with Oilpan.
252 _NPN_ReleaseObject(m_NPObject
);
256 HTMLFrameOwnerElement::detach(context
);
259 LayoutObject
* HTMLPlugInElement::createLayoutObject(const ComputedStyle
& style
)
261 // Fallback content breaks the DOM->layoutObject class relationship of this
262 // class and all superclasses because createObject won't necessarily return
263 // a LayoutEmbeddedObject or LayoutPart.
264 if (useFallbackContent())
265 return LayoutObject::createObject(this, style
);
268 LayoutImage
* image
= new LayoutImage(this);
269 image
->setImageResource(LayoutImageResource::create());
273 if (usePlaceholderContent())
274 return new LayoutBlockFlow(this);
276 return new LayoutEmbeddedObject(this);
279 void HTMLPlugInElement::finishParsingChildren()
281 HTMLFrameOwnerElement::finishParsingChildren();
282 if (useFallbackContent())
285 setNeedsWidgetUpdate(true);
287 lazyReattachIfNeeded();
290 void HTMLPlugInElement::resetInstance()
292 m_pluginWrapper
.clear();
295 SharedPersistent
<v8::Object
>* HTMLPlugInElement::pluginWrapper()
297 LocalFrame
* frame
= document().frame();
301 // If the host dynamically turns off JavaScript (or Java) we will still
302 // return the cached allocated Bindings::Instance. Not supporting this
304 if (!m_pluginWrapper
) {
307 if (m_persistedPluginWidget
)
308 plugin
= m_persistedPluginWidget
.get();
310 plugin
= pluginWidgetForJSBindings();
313 m_pluginWrapper
= frame
->script().createPluginWrapper(plugin
);
315 return m_pluginWrapper
.get();
318 Widget
* HTMLPlugInElement::existingPluginWidget() const
320 if (LayoutPart
* layoutPart
= existingLayoutPart())
321 return layoutPart
->widget();
325 Widget
* HTMLPlugInElement::pluginWidgetForJSBindings()
327 if (LayoutPart
* layoutPart
= layoutPartForJSBindings())
328 return layoutPart
->widget();
332 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName
& name
) const
334 if (name
== widthAttr
|| name
== heightAttr
|| name
== vspaceAttr
|| name
== hspaceAttr
|| name
== alignAttr
)
336 return HTMLFrameOwnerElement::isPresentationAttribute(name
);
339 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
341 if (name
== widthAttr
) {
342 addHTMLLengthToStyle(style
, CSSPropertyWidth
, value
);
343 } else if (name
== heightAttr
) {
344 addHTMLLengthToStyle(style
, CSSPropertyHeight
, value
);
345 } else if (name
== vspaceAttr
) {
346 addHTMLLengthToStyle(style
, CSSPropertyMarginTop
, value
);
347 addHTMLLengthToStyle(style
, CSSPropertyMarginBottom
, value
);
348 } else if (name
== hspaceAttr
) {
349 addHTMLLengthToStyle(style
, CSSPropertyMarginLeft
, value
);
350 addHTMLLengthToStyle(style
, CSSPropertyMarginRight
, value
);
351 } else if (name
== alignAttr
) {
352 applyAlignmentAttributeToStyle(value
, style
);
354 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name
, value
, style
);
358 void HTMLPlugInElement::defaultEventHandler(Event
* event
)
360 // Firefox seems to use a fake event listener to dispatch events to plugin
361 // (tested with mouse events only). This is observable via different order
362 // of events - in Firefox, event listeners specified in HTML attributes
363 // fires first, then an event gets dispatched to plugin, and only then
364 // other event listeners fire. Hopefully, this difference does not matter in
367 // FIXME: Mouse down and scroll events are passed down to plugin via custom
368 // code in EventHandler; these code paths should be united.
370 LayoutObject
* r
= layoutObject();
371 if (!r
|| !r
->isLayoutPart())
373 if (r
->isEmbeddedObject()) {
374 if (toLayoutEmbeddedObject(r
)->showsUnavailablePluginIndicator())
377 RefPtrWillBeRawPtr
<Widget
> widget
= toLayoutPart(r
)->widget();
380 widget
->handleEvent(event
);
381 if (event
->defaultHandled())
383 HTMLFrameOwnerElement::defaultEventHandler(event
);
386 LayoutPart
* HTMLPlugInElement::layoutPartForJSBindings() const
388 // Needs to load the plugin immediatedly because this function is called
389 // when JavaScript code accesses the plugin.
390 // FIXME: Check if dispatching events here is safe.
391 document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously
);
392 return existingLayoutPart();
395 bool HTMLPlugInElement::isKeyboardFocusable() const
397 if (useFallbackContent() || usePlaceholderContent())
398 return HTMLElement::isKeyboardFocusable();
400 if (!document().isActive())
403 if (Widget
* widget
= existingPluginWidget())
404 return widget
->isPluginView() && toPluginView(widget
)->supportsKeyboardFocus();
409 bool HTMLPlugInElement::hasCustomFocusLogic() const
411 return !useFallbackContent() && !usePlaceholderContent();
414 bool HTMLPlugInElement::isPluginElement() const
419 bool HTMLPlugInElement::layoutObjectIsFocusable() const
421 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::layoutObjectIsFocusable())
424 if (useFallbackContent() || !layoutObject() || !layoutObject()->isEmbeddedObject())
426 return !toLayoutEmbeddedObject(layoutObject())->showsUnavailablePluginIndicator();
429 NPObject
* HTMLPlugInElement::getNPObject()
431 ASSERT(document().frame());
433 m_NPObject
= document().frame()->script().createScriptObjectForPluginElement(this);
437 void HTMLPlugInElement::setPluginFocus(bool focused
)
439 Widget
* focusedWidget
= existingPluginWidget();
440 // NPAPI flash requires to receive messages when web contents focus changes.
441 if (getNPObject() && focusedWidget
)
442 focusedWidget
->setFocus(focused
, WebFocusTypeNone
);
445 bool HTMLPlugInElement::isImageType()
447 if (m_serviceType
.isEmpty() && protocolIs(m_url
, "data"))
448 m_serviceType
= mimeTypeFromDataURL(m_url
);
450 if (LocalFrame
* frame
= document().frame()) {
451 KURL completedURL
= document().completeURL(m_url
);
452 return frame
->loader().client()->objectContentType(completedURL
, m_serviceType
, shouldPreferPlugInsForImages()) == ObjectContentImage
;
455 return Image::supportsType(m_serviceType
);
458 LayoutEmbeddedObject
* HTMLPlugInElement::layoutEmbeddedObject() const
460 // HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects
461 // when using fallback content.
462 if (!layoutObject() || !layoutObject()->isEmbeddedObject())
464 return toLayoutEmbeddedObject(layoutObject());
467 // We don't use m_url, as it may not be the final URL that the object loads,
468 // depending on <param> values.
469 bool HTMLPlugInElement::allowedToLoadFrameURL(const String
& url
)
471 KURL completeURL
= document().completeURL(url
);
472 if (contentFrame() && protocolIsJavaScript(completeURL
)
473 && !document().securityOrigin()->canAccess(contentFrame()->securityContext()->securityOrigin()))
475 return document().frame()->isURLAllowed(completeURL
);
478 // We don't use m_url, or m_serviceType as they may not be the final values
479 // that <object> uses depending on <param> values.
480 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String
& url
, const String
& serviceType
)
482 ASSERT(document().frame());
485 completedURL
= document().completeURL(url
);
486 return document().frame()->loader().client()->objectContentType(completedURL
, serviceType
, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin
;
489 bool HTMLPlugInElement::requestObject(const String
& url
, const String
& mimeType
, const Vector
<String
>& paramNames
, const Vector
<String
>& paramValues
)
491 if (url
.isEmpty() && mimeType
.isEmpty())
494 if (protocolIsJavaScript(url
))
497 KURL completedURL
= url
.isEmpty() ? KURL() : document().completeURL(url
);
498 if (!pluginIsLoadable(completedURL
, mimeType
))
502 if (shouldUsePlugin(completedURL
, mimeType
, hasFallbackContent(), useFallback
))
503 return loadPlugin(completedURL
, mimeType
, paramNames
, paramValues
, useFallback
, true);
505 // If the plugin element already contains a subframe,
506 // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
507 // frame and set it as the LayoutPart's widget, causing what was previously
508 // in the widget to be torn down.
509 return loadOrRedirectSubframe(completedURL
, getNameAttribute(), true);
512 bool HTMLPlugInElement::loadPlugin(const KURL
& url
, const String
& mimeType
, const Vector
<String
>& paramNames
, const Vector
<String
>& paramValues
, bool useFallback
, bool requireLayoutObject
)
514 LocalFrame
* frame
= document().frame();
516 if (!frame
->loader().allowPlugins(AboutToInstantiatePlugin
))
519 LayoutEmbeddedObject
* layoutObject
= layoutEmbeddedObject();
520 // FIXME: This code should not depend on layoutObject!
521 if ((!layoutObject
&& requireLayoutObject
) || useFallback
)
524 WTF_LOG(Plugins
, "%p Plugin URL: %s", this, m_url
.utf8().data());
525 WTF_LOG(Plugins
, " Loaded URL: %s", url
.string().utf8().data());
528 OwnPtrWillBeRawPtr
<PluginPlaceholder
> placeholder
= nullptr;
529 RefPtrWillBeRawPtr
<Widget
> widget
= m_persistedPluginWidget
;
531 bool loadManually
= document().isPluginDocument() && !document().containsPlugins();
532 placeholder
= frame
->loader().client()->createPluginPlaceholder(document(), url
, paramNames
, paramValues
, mimeType
, loadManually
);
534 FrameLoaderClient::DetachedPluginPolicy policy
= requireLayoutObject
? FrameLoaderClient::FailOnDetachedPlugin
: FrameLoaderClient::AllowDetachedPlugin
;
535 widget
= frame
->loader().client()->createPlugin(this, url
, paramNames
, paramValues
, mimeType
, loadManually
, policy
);
539 if (!placeholder
&& !widget
) {
540 if (layoutObject
&& !layoutObject
->showsUnavailablePluginIndicator())
541 layoutObject
->setPluginUnavailabilityReason(LayoutEmbeddedObject::PluginMissing
);
542 setPlaceholder(nullptr);
547 setPlaceholder(placeholder
.release());
553 setPersistedPluginWidget(nullptr);
555 setPersistedPluginWidget(widget
.get());
557 setPlaceholder(nullptr);
558 document().setContainsPlugins();
559 // TODO(esprehn): WebPluginContainerImpl::setWebLayer also schedules a compositing update, do we need both?
560 setNeedsCompositingUpdate();
561 // Make sure any input event handlers introduced by the plugin are taken into account.
562 if (Page
* page
= document().frame()->page()) {
563 if (ScrollingCoordinator
* scrollingCoordinator
= page
->scrollingCoordinator())
564 scrollingCoordinator
->notifyLayoutUpdated();
569 bool HTMLPlugInElement::shouldUsePlugin(const KURL
& url
, const String
& mimeType
, bool hasFallback
, bool& useFallback
)
571 // Allow other plugins to win over QuickTime because if the user has
572 // installed a plugin that can handle TIFF (which QuickTime can also
573 // handle) they probably intended to override QT.
574 if (document().frame()->page() && (mimeType
== "image/tiff" || mimeType
== "image/tif" || mimeType
== "image/x-tiff")) {
575 const PluginData
* pluginData
= document().frame()->page()->pluginData();
576 String pluginName
= pluginData
? pluginData
->pluginNameForMimeType(mimeType
) : String();
577 if (!pluginName
.isEmpty() && !pluginName
.contains("QuickTime", TextCaseInsensitive
))
581 ObjectContentType objectType
= document().frame()->loader().client()->objectContentType(url
, mimeType
, shouldPreferPlugInsForImages());
582 // If an object's content can't be handled and it has no fallback, let
583 // it be handled as a plugin to show the broken plugin icon.
584 useFallback
= objectType
== ObjectContentNone
&& hasFallback
;
585 return objectType
== ObjectContentNone
|| objectType
== ObjectContentNetscapePlugin
|| objectType
== ObjectContentOtherPlugin
;
588 void HTMLPlugInElement::setPlaceholder(PassOwnPtrWillBeRawPtr
<PluginPlaceholder
> placeholder
)
590 bool needsLazyReattach
= (!placeholder
) != (!m_placeholder
);
592 placeholder
->loadIntoContainer(ensureUserAgentShadowRoot());
593 m_placeholder
= placeholder
;
595 ShadowRoot
& shadowRoot
= ensureUserAgentShadowRoot();
596 shadowRoot
.removeChildren();
597 shadowRoot
.appendChild(HTMLContentElement::create(document()));
598 m_placeholder
.clear();
600 if (needsLazyReattach
)
601 lazyReattachIfAttached();
604 void HTMLPlugInElement::dispatchErrorEvent()
606 if (document().isPluginDocument() && document().ownerElement())
607 document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error
));
609 dispatchEvent(Event::create(EventTypeNames::error
));
612 bool HTMLPlugInElement::pluginIsLoadable(const KURL
& url
, const String
& mimeType
)
614 if (url
.isEmpty() && mimeType
.isEmpty())
617 LocalFrame
* frame
= document().frame();
618 Settings
* settings
= frame
->settings();
622 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType
) && !settings
->javaEnabled())
625 if (document().isSandboxed(SandboxPlugins
))
628 if (!document().securityOrigin()->canDisplay(url
)) {
629 FrameLoader::reportLocalLoadFailed(frame
, url
.string());
633 AtomicString declaredMimeType
= document().isPluginDocument() && document().ownerElement() ?
634 document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr
) :
635 fastGetAttribute(HTMLNames::typeAttr
);
636 if (!document().contentSecurityPolicy()->allowObjectFromSource(url
)
637 || !document().contentSecurityPolicy()->allowPluginTypeForDocument(document(), mimeType
, declaredMimeType
, url
)) {
638 layoutEmbeddedObject()->setPluginUnavailabilityReason(LayoutEmbeddedObject::PluginBlockedByContentSecurityPolicy
);
642 return (!mimeType
.isEmpty() && url
.isEmpty()) || !MixedContentChecker::shouldBlockFetch(frame
, WebURLRequest::RequestContextObject
, WebURLRequest::FrameTypeNone
, url
);
645 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot
&)
647 userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
650 void HTMLPlugInElement::willAddFirstAuthorShadowRoot()
652 lazyReattachIfAttached();
655 bool HTMLPlugInElement::hasFallbackContent() const
660 bool HTMLPlugInElement::useFallbackContent() const
662 return openShadowRoot();
665 void HTMLPlugInElement::lazyReattachIfNeeded()
667 if (!useFallbackContent() && !usePlaceholderContent() && needsWidgetUpdate() && layoutObject() && !isImageType())
668 lazyReattachIfAttached();