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/PluginView.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"
56 #include "public/platform/WebURLRequest.h"
60 using namespace HTMLNames
;
62 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName
& tagName
, Document
& doc
, bool createdByParser
, PreferPlugInsForImagesOption preferPlugInsForImagesOption
)
63 : HTMLFrameOwnerElement(tagName
, doc
)
64 , m_isDelayingLoadEvent(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 DEFINE_TRACE(HTMLPlugInElement
)
88 visitor
->trace(m_imageLoader
);
89 visitor
->trace(m_persistedPluginWidget
);
90 HTMLFrameOwnerElement::trace(visitor
);
94 void HTMLPlugInElement::disconnectContentFrame()
96 if (m_persistedPluginWidget
) {
97 m_persistedPluginWidget
->dispose();
98 m_persistedPluginWidget
= nullptr;
100 HTMLFrameOwnerElement::disconnectContentFrame();
103 void HTMLPlugInElement::shouldDisposePlugin()
105 if (m_persistedPluginWidget
&& m_persistedPluginWidget
->isPluginView())
106 toPluginView(m_persistedPluginWidget
.get())->shouldDisposePlugin();
110 void HTMLPlugInElement::setPersistedPluginWidget(Widget
* widget
)
112 if (m_persistedPluginWidget
== widget
)
115 if (m_persistedPluginWidget
&& m_persistedPluginWidget
->isPluginView()) {
116 LocalFrame
* frame
= toPluginView(m_persistedPluginWidget
.get())->pluginFrame();
118 frame
->unregisterPluginElement(this);
120 m_persistedPluginWidget
->dispose();
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 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
136 bool HTMLPlugInElement::willRespondToMouseClickEvents()
138 if (isDisabledFormControl())
140 LayoutObject
* r
= layoutObject();
141 return r
&& (r
->isEmbeddedObject() || r
->isLayoutPart());
144 void HTMLPlugInElement::removeAllEventListeners()
146 HTMLFrameOwnerElement::removeAllEventListeners();
147 if (LayoutPart
* layoutObject
= existingLayoutPart()) {
148 if (Widget
* widget
= layoutObject
->widget())
149 widget
->eventListenersRemoved();
153 void HTMLPlugInElement::didMoveToNewDocument(Document
& oldDocument
)
156 m_imageLoader
->elementDidMoveToNewDocument();
157 HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument
);
160 void HTMLPlugInElement::attach(const AttachContext
& context
)
162 HTMLFrameOwnerElement::attach(context
);
164 if (!layoutObject() || useFallbackContent()) {
165 // If we don't have a layoutObject we have to dispose of any plugins
166 // which we persisted over a reattach.
167 if (m_persistedPluginWidget
) {
168 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates
;
169 setPersistedPluginWidget(nullptr);
176 m_imageLoader
= HTMLImageLoader::create(this);
177 m_imageLoader
->updateFromElement();
178 } else if (needsWidgetUpdate()
179 && layoutEmbeddedObject()
180 && !layoutEmbeddedObject()->showsUnavailablePluginIndicator()
181 && !wouldLoadAsNetscapePlugin(m_url
, m_serviceType
)
182 && !m_isDelayingLoadEvent
) {
183 m_isDelayingLoadEvent
= true;
184 document().incrementLoadEventDelayCount();
185 document().loadPluginsSoon();
189 void HTMLPlugInElement::updateWidget()
191 RefPtrWillBeRawPtr
<HTMLPlugInElement
> protector(this);
192 updateWidgetInternal();
193 if (m_isDelayingLoadEvent
) {
194 m_isDelayingLoadEvent
= false;
195 document().decrementLoadEventDelayCount();
199 void HTMLPlugInElement::removedFrom(ContainerNode
* insertionPoint
)
201 // If we've persisted the plugin and we're removed from the tree then
202 // make sure we cleanup the persistance pointer.
203 if (m_persistedPluginWidget
) {
204 HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates
;
205 setPersistedPluginWidget(nullptr);
207 HTMLFrameOwnerElement::removedFrom(insertionPoint
);
210 void HTMLPlugInElement::requestPluginCreationWithoutLayoutObjectIfPossible()
212 if (m_serviceType
.isEmpty())
215 if (!document().frame()
216 || !document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType
))
219 if (layoutObject() && layoutObject()->isLayoutPart())
222 createPluginWithoutLayoutObject();
225 void HTMLPlugInElement::createPluginWithoutLayoutObject()
227 ASSERT(document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType
));
230 Vector
<String
> paramNames
;
231 Vector
<String
> paramValues
;
233 paramNames
.append("type");
234 paramValues
.append(m_serviceType
);
236 bool useFallback
= false;
237 loadPlugin(url
, m_serviceType
, paramNames
, paramValues
, useFallback
, false);
240 bool HTMLPlugInElement::shouldAccelerate() const
242 if (Widget
* widget
= ownedWidget())
243 return widget
->isPluginView() && toPluginView(widget
)->platformLayer();
247 void HTMLPlugInElement::detach(const AttachContext
& context
)
249 // Update the widget the next time we attach (detaching destroys the plugin).
250 // FIXME: None of this "needsWidgetUpdate" related code looks right.
251 if (layoutObject() && !useFallbackContent())
252 setNeedsWidgetUpdate(true);
253 if (m_isDelayingLoadEvent
) {
254 m_isDelayingLoadEvent
= false;
255 document().decrementLoadEventDelayCount();
258 // Only try to persist a plugin widget we actually own.
259 Widget
* plugin
= ownedWidget();
260 if (plugin
&& context
.performingReattach
)
261 setPersistedPluginWidget(plugin
);
264 // Clear the widget; will trigger disposal of it with Oilpan.
268 _NPN_ReleaseObject(m_NPObject
);
272 HTMLFrameOwnerElement::detach(context
);
275 LayoutObject
* HTMLPlugInElement::createLayoutObject(const ComputedStyle
& style
)
277 // Fallback content breaks the DOM->layoutObject class relationship of this
278 // class and all superclasses because createObject won't necessarily return
279 // a LayoutEmbeddedObject or LayoutPart.
280 if (useFallbackContent())
281 return LayoutObject::createObject(this, style
);
284 LayoutImage
* image
= new LayoutImage(this);
285 image
->setImageResource(LayoutImageResource::create());
290 return new LayoutEmbeddedObject(this);
293 void HTMLPlugInElement::finishParsingChildren()
295 HTMLFrameOwnerElement::finishParsingChildren();
296 if (useFallbackContent())
299 setNeedsWidgetUpdate(true);
301 lazyReattachIfNeeded();
304 void HTMLPlugInElement::resetInstance()
306 m_pluginWrapper
.clear();
309 SharedPersistent
<v8::Object
>* HTMLPlugInElement::pluginWrapper()
311 LocalFrame
* frame
= document().frame();
315 // If the host dynamically turns off JavaScript (or Java) we will still
316 // return the cached allocated Bindings::Instance. Not supporting this
318 if (!m_pluginWrapper
) {
321 if (m_persistedPluginWidget
)
322 plugin
= m_persistedPluginWidget
.get();
324 plugin
= pluginWidget();
327 m_pluginWrapper
= frame
->script().createPluginWrapper(plugin
);
329 return m_pluginWrapper
.get();
332 Widget
* HTMLPlugInElement::pluginWidget() const
334 if (LayoutPart
* layoutPart
= layoutPartForJSBindings())
335 return layoutPart
->widget();
339 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName
& name
) const
341 if (name
== widthAttr
|| name
== heightAttr
|| name
== vspaceAttr
|| name
== hspaceAttr
|| name
== alignAttr
)
343 return HTMLFrameOwnerElement::isPresentationAttribute(name
);
346 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
348 if (name
== widthAttr
) {
349 addHTMLLengthToStyle(style
, CSSPropertyWidth
, value
);
350 } else if (name
== heightAttr
) {
351 addHTMLLengthToStyle(style
, CSSPropertyHeight
, value
);
352 } else if (name
== vspaceAttr
) {
353 addHTMLLengthToStyle(style
, CSSPropertyMarginTop
, value
);
354 addHTMLLengthToStyle(style
, CSSPropertyMarginBottom
, value
);
355 } else if (name
== hspaceAttr
) {
356 addHTMLLengthToStyle(style
, CSSPropertyMarginLeft
, value
);
357 addHTMLLengthToStyle(style
, CSSPropertyMarginRight
, value
);
358 } else if (name
== alignAttr
) {
359 applyAlignmentAttributeToStyle(value
, style
);
361 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name
, value
, style
);
365 void HTMLPlugInElement::defaultEventHandler(Event
* event
)
367 // Firefox seems to use a fake event listener to dispatch events to plugin
368 // (tested with mouse events only). This is observable via different order
369 // of events - in Firefox, event listeners specified in HTML attributes
370 // fires first, then an event gets dispatched to plugin, and only then
371 // other event listeners fire. Hopefully, this difference does not matter in
374 // FIXME: Mouse down and scroll events are passed down to plugin via custom
375 // code in EventHandler; these code paths should be united.
377 LayoutObject
* r
= layoutObject();
378 if (!r
|| !r
->isLayoutPart())
380 if (r
->isEmbeddedObject()) {
381 if (toLayoutEmbeddedObject(r
)->showsUnavailablePluginIndicator())
384 RefPtrWillBeRawPtr
<Widget
> widget
= toLayoutPart(r
)->widget();
387 widget
->handleEvent(event
);
388 if (event
->defaultHandled())
390 HTMLFrameOwnerElement::defaultEventHandler(event
);
393 LayoutPart
* HTMLPlugInElement::layoutPartForJSBindings() const
395 // Needs to load the plugin immediatedly because this function is called
396 // when JavaScript code accesses the plugin.
397 // FIXME: Check if dispatching events here is safe.
398 document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously
);
399 return existingLayoutPart();
402 bool HTMLPlugInElement::isKeyboardFocusable() const
404 if (!document().isActive())
406 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
409 bool HTMLPlugInElement::hasCustomFocusLogic() const
411 return !useFallbackContent();
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 // NPAPI flash requires to receive messages when web contents focus changes.
440 if (getNPObject() && pluginWidget() && pluginWidget()->isPluginView())
441 toPluginView(pluginWidget())->setFocus(focused
, WebFocusTypeNone
);
444 bool HTMLPlugInElement::isImageType()
446 if (m_serviceType
.isEmpty() && protocolIs(m_url
, "data"))
447 m_serviceType
= mimeTypeFromDataURL(m_url
);
449 if (LocalFrame
* frame
= document().frame()) {
450 KURL completedURL
= document().completeURL(m_url
);
451 return frame
->loader().client()->objectContentType(completedURL
, m_serviceType
, shouldPreferPlugInsForImages()) == ObjectContentImage
;
454 return Image::supportsType(m_serviceType
);
457 LayoutEmbeddedObject
* HTMLPlugInElement::layoutEmbeddedObject() const
459 // HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects
460 // when using fallback content.
461 if (!layoutObject() || !layoutObject()->isEmbeddedObject())
463 return toLayoutEmbeddedObject(layoutObject());
466 // We don't use m_url, as it may not be the final URL that the object loads,
467 // depending on <param> values.
468 bool HTMLPlugInElement::allowedToLoadFrameURL(const String
& url
)
470 KURL completeURL
= document().completeURL(url
);
471 if (contentFrame() && protocolIsJavaScript(completeURL
)
472 && !document().securityOrigin()->canAccess(contentFrame()->securityContext()->securityOrigin()))
474 return document().frame()->isURLAllowed(completeURL
);
477 // We don't use m_url, or m_serviceType as they may not be the final values
478 // that <object> uses depending on <param> values.
479 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String
& url
, const String
& serviceType
)
481 ASSERT(document().frame());
484 completedURL
= document().completeURL(url
);
485 return document().frame()->loader().client()->objectContentType(completedURL
, serviceType
, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin
;
488 bool HTMLPlugInElement::requestObject(const String
& url
, const String
& mimeType
, const Vector
<String
>& paramNames
, const Vector
<String
>& paramValues
)
490 if (url
.isEmpty() && mimeType
.isEmpty())
493 if (protocolIsJavaScript(url
))
496 KURL completedURL
= url
.isEmpty() ? KURL() : document().completeURL(url
);
497 if (!pluginIsLoadable(completedURL
, mimeType
))
501 if (shouldUsePlugin(completedURL
, mimeType
, hasFallbackContent(), useFallback
))
502 return loadPlugin(completedURL
, mimeType
, paramNames
, paramValues
, useFallback
, true);
504 // If the plugin element already contains a subframe,
505 // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
506 // frame and set it as the LayoutPart's widget, causing what was previously
507 // in the widget to be torn down.
508 return loadOrRedirectSubframe(completedURL
, getNameAttribute(), true);
511 bool HTMLPlugInElement::loadPlugin(const KURL
& url
, const String
& mimeType
, const Vector
<String
>& paramNames
, const Vector
<String
>& paramValues
, bool useFallback
, bool requireLayoutObject
)
513 LocalFrame
* frame
= document().frame();
515 if (!frame
->loader().allowPlugins(AboutToInstantiatePlugin
))
518 LayoutEmbeddedObject
* layoutObject
= layoutEmbeddedObject();
519 // FIXME: This code should not depend on layoutObject!
520 if ((!layoutObject
&& requireLayoutObject
) || useFallback
)
523 WTF_LOG(Plugins
, "%p Plugin URL: %s", this, m_url
.utf8().data());
524 WTF_LOG(Plugins
, " Loaded URL: %s", url
.string().utf8().data());
527 RefPtrWillBeRawPtr
<Widget
> widget
= m_persistedPluginWidget
;
529 bool loadManually
= document().isPluginDocument() && !document().containsPlugins();
530 FrameLoaderClient::DetachedPluginPolicy policy
= requireLayoutObject
? FrameLoaderClient::FailOnDetachedPlugin
: FrameLoaderClient::AllowDetachedPlugin
;
531 widget
= frame
->loader().client()->createPlugin(this, url
, paramNames
, paramValues
, mimeType
, loadManually
, policy
);
535 if (layoutObject
&& !layoutObject
->showsUnavailablePluginIndicator())
536 layoutObject
->setPluginUnavailabilityReason(LayoutEmbeddedObject::PluginMissing
);
542 setPersistedPluginWidget(nullptr);
544 setPersistedPluginWidget(widget
.get());
546 document().setContainsPlugins();
547 // TODO(esprehn): WebPluginContainerImpl::setWebLayer also schedules a compositing update, do we need both?
548 setNeedsCompositingUpdate();
549 // Make sure any input event handlers introduced by the plugin are taken into account.
550 if (Page
* page
= document().frame()->page()) {
551 if (ScrollingCoordinator
* scrollingCoordinator
= page
->scrollingCoordinator())
552 scrollingCoordinator
->notifyLayoutUpdated();
557 bool HTMLPlugInElement::shouldUsePlugin(const KURL
& url
, const String
& mimeType
, bool hasFallback
, bool& useFallback
)
559 // Allow other plugins to win over QuickTime because if the user has
560 // installed a plugin that can handle TIFF (which QuickTime can also
561 // handle) they probably intended to override QT.
562 if (document().frame()->page() && (mimeType
== "image/tiff" || mimeType
== "image/tif" || mimeType
== "image/x-tiff")) {
563 const PluginData
* pluginData
= document().frame()->page()->pluginData();
564 String pluginName
= pluginData
? pluginData
->pluginNameForMimeType(mimeType
) : String();
565 if (!pluginName
.isEmpty() && !pluginName
.contains("QuickTime", TextCaseInsensitive
))
569 ObjectContentType objectType
= document().frame()->loader().client()->objectContentType(url
, mimeType
, shouldPreferPlugInsForImages());
570 // If an object's content can't be handled and it has no fallback, let
571 // it be handled as a plugin to show the broken plugin icon.
572 useFallback
= objectType
== ObjectContentNone
&& hasFallback
;
573 return objectType
== ObjectContentNone
|| objectType
== ObjectContentNetscapePlugin
|| objectType
== ObjectContentOtherPlugin
;
577 void HTMLPlugInElement::dispatchErrorEvent()
579 if (document().isPluginDocument() && document().ownerElement())
580 document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error
));
582 dispatchEvent(Event::create(EventTypeNames::error
));
585 bool HTMLPlugInElement::pluginIsLoadable(const KURL
& url
, const String
& mimeType
)
587 if (url
.isEmpty() && mimeType
.isEmpty())
590 LocalFrame
* frame
= document().frame();
591 Settings
* settings
= frame
->settings();
595 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType
))
598 if (document().isSandboxed(SandboxPlugins
))
601 if (!document().securityOrigin()->canDisplay(url
)) {
602 FrameLoader::reportLocalLoadFailed(frame
, url
.string());
606 AtomicString declaredMimeType
= document().isPluginDocument() && document().ownerElement() ?
607 document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr
) :
608 fastGetAttribute(HTMLNames::typeAttr
);
609 if (!document().contentSecurityPolicy()->allowObjectFromSource(url
)
610 || !document().contentSecurityPolicy()->allowPluginTypeForDocument(document(), mimeType
, declaredMimeType
, url
)) {
611 layoutEmbeddedObject()->setPluginUnavailabilityReason(LayoutEmbeddedObject::PluginBlockedByContentSecurityPolicy
);
615 return (!mimeType
.isEmpty() && url
.isEmpty()) || !MixedContentChecker::shouldBlockFetch(frame
, WebURLRequest::RequestContextObject
, WebURLRequest::FrameTypeNone
, url
);
618 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot
&)
620 userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
623 void HTMLPlugInElement::willAddFirstAuthorShadowRoot()
625 lazyReattachIfAttached();
628 bool HTMLPlugInElement::hasFallbackContent() const
633 bool HTMLPlugInElement::useFallbackContent() const
635 return openShadowRoot();
638 void HTMLPlugInElement::lazyReattachIfNeeded()
640 if (!useFallbackContent() && needsWidgetUpdate() && layoutObject() && !isImageType())
641 lazyReattachIfAttached();