Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLPlugInElement.cpp
blobcde74055127060bfdae14c9d5a8a6a459bdfa1dc
1 /**
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.
23 #include "config.h"
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"
58 namespace blink {
60 using namespace HTMLNames;
62 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& doc, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
63 : HTMLFrameOwnerElement(tagName, doc)
64 , m_isDelayingLoadEvent(false)
65 , m_NPObject(0)
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);
80 if (m_NPObject) {
81 _NPN_ReleaseObject(m_NPObject);
82 m_NPObject = 0;
86 DEFINE_TRACE(HTMLPlugInElement)
88 visitor->trace(m_imageLoader);
89 visitor->trace(m_persistedPluginWidget);
90 HTMLFrameOwnerElement::trace(visitor);
93 #if ENABLE(OILPAN)
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();
108 #endif
110 void HTMLPlugInElement::setPersistedPluginWidget(Widget* widget)
112 if (m_persistedPluginWidget == widget)
113 return;
114 #if ENABLE(OILPAN)
115 if (m_persistedPluginWidget && m_persistedPluginWidget->isPluginView()) {
116 LocalFrame* frame = toPluginView(m_persistedPluginWidget.get())->pluginFrame();
117 ASSERT(frame);
118 frame->unregisterPluginElement(this);
119 if (!widget)
120 m_persistedPluginWidget->dispose();
122 if (widget && widget->isPluginView()) {
123 LocalFrame* frame = toPluginView(widget)->pluginFrame();
124 ASSERT(frame);
125 frame->registerPluginElement(this);
127 #endif
128 m_persistedPluginWidget = widget;
131 bool HTMLPlugInElement::canProcessDrag() const
133 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
136 bool HTMLPlugInElement::willRespondToMouseClickEvents()
138 if (isDisabledFormControl())
139 return false;
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)
155 if (m_imageLoader)
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);
171 return;
174 if (isImageType()) {
175 if (!m_imageLoader)
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())
213 return;
215 if (!document().frame()
216 || !document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType))
217 return;
219 if (layoutObject() && layoutObject()->isLayoutPart())
220 return;
222 createPluginWithoutLayoutObject();
225 void HTMLPlugInElement::createPluginWithoutLayoutObject()
227 ASSERT(document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType));
229 KURL url;
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();
244 return false;
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);
263 resetInstance();
264 // Clear the widget; will trigger disposal of it with Oilpan.
265 setWidget(nullptr);
267 if (m_NPObject) {
268 _NPN_ReleaseObject(m_NPObject);
269 m_NPObject = 0;
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);
283 if (isImageType()) {
284 LayoutImage* image = new LayoutImage(this);
285 image->setImageResource(LayoutImageResource::create());
286 return image;
290 return new LayoutEmbeddedObject(this);
293 void HTMLPlugInElement::finishParsingChildren()
295 HTMLFrameOwnerElement::finishParsingChildren();
296 if (useFallbackContent())
297 return;
299 setNeedsWidgetUpdate(true);
300 if (inDocument())
301 lazyReattachIfNeeded();
304 void HTMLPlugInElement::resetInstance()
306 m_pluginWrapper.clear();
309 SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper()
311 LocalFrame* frame = document().frame();
312 if (!frame)
313 return nullptr;
315 // If the host dynamically turns off JavaScript (or Java) we will still
316 // return the cached allocated Bindings::Instance. Not supporting this
317 // edge-case is OK.
318 if (!m_pluginWrapper) {
319 Widget* plugin;
321 if (m_persistedPluginWidget)
322 plugin = m_persistedPluginWidget.get();
323 else
324 plugin = pluginWidget();
326 if (plugin)
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();
336 return nullptr;
339 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const
341 if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr)
342 return true;
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);
360 } else {
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
372 // practice.
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())
379 return;
380 if (r->isEmbeddedObject()) {
381 if (toLayoutEmbeddedObject(r)->showsUnavailablePluginIndicator())
382 return;
384 RefPtrWillBeRawPtr<Widget> widget = toLayoutPart(r)->widget();
385 if (!widget)
386 return;
387 widget->handleEvent(event);
388 if (event->defaultHandled())
389 return;
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())
405 return false;
406 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
409 bool HTMLPlugInElement::hasCustomFocusLogic() const
411 return !useFallbackContent();
414 bool HTMLPlugInElement::isPluginElement() const
416 return true;
419 bool HTMLPlugInElement::layoutObjectIsFocusable() const
421 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::layoutObjectIsFocusable())
422 return true;
424 if (useFallbackContent() || !layoutObject() || !layoutObject()->isEmbeddedObject())
425 return false;
426 return !toLayoutEmbeddedObject(layoutObject())->showsUnavailablePluginIndicator();
429 NPObject* HTMLPlugInElement::getNPObject()
431 ASSERT(document().frame());
432 if (!m_NPObject)
433 m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this);
434 return m_NPObject;
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())
462 return nullptr;
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()))
473 return false;
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());
482 KURL completedURL;
483 if (!url.isEmpty())
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())
491 return false;
493 if (protocolIsJavaScript(url))
494 return false;
496 KURL completedURL = url.isEmpty() ? KURL() : document().completeURL(url);
497 if (!pluginIsLoadable(completedURL, mimeType))
498 return false;
500 bool useFallback;
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))
516 return false;
518 LayoutEmbeddedObject* layoutObject = layoutEmbeddedObject();
519 // FIXME: This code should not depend on layoutObject!
520 if ((!layoutObject && requireLayoutObject) || useFallback)
521 return false;
523 WTF_LOG(Plugins, "%p Plugin URL: %s", this, m_url.utf8().data());
524 WTF_LOG(Plugins, " Loaded URL: %s", url.string().utf8().data());
525 m_loadedUrl = url;
527 RefPtrWillBeRawPtr<Widget> widget = m_persistedPluginWidget;
528 if (!widget) {
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);
534 if (!widget) {
535 if (layoutObject && !layoutObject->showsUnavailablePluginIndicator())
536 layoutObject->setPluginUnavailabilityReason(LayoutEmbeddedObject::PluginMissing);
537 return false;
540 if (layoutObject) {
541 setWidget(widget);
542 setPersistedPluginWidget(nullptr);
543 } else {
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();
554 return true;
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))
566 return true;
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));
581 else
582 dispatchEvent(Event::create(EventTypeNames::error));
585 bool HTMLPlugInElement::pluginIsLoadable(const KURL& url, const String& mimeType)
587 if (url.isEmpty() && mimeType.isEmpty())
588 return false;
590 LocalFrame* frame = document().frame();
591 Settings* settings = frame->settings();
592 if (!settings)
593 return false;
595 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType))
596 return false;
598 if (document().isSandboxed(SandboxPlugins))
599 return false;
601 if (!document().securityOrigin()->canDisplay(url)) {
602 FrameLoader::reportLocalLoadFailed(frame, url.string());
603 return false;
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);
612 return false;
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
630 return false;
633 bool HTMLPlugInElement::useFallbackContent() const
635 return openShadowRoot();
638 void HTMLPlugInElement::lazyReattachIfNeeded()
640 if (!useFallbackContent() && needsWidgetUpdate() && layoutObject() && !isImageType())
641 lazyReattachIfAttached();