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 "CSSPropertyNames.h"
27 #include "HTMLNames.h"
28 #include "bindings/v8/ScriptController.h"
29 #include "bindings/v8/npruntime_impl.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/LocalFrame.h"
35 #include "core/frame/csp/ContentSecurityPolicy.h"
36 #include "core/html/HTMLContentElement.h"
37 #include "core/html/HTMLImageLoader.h"
38 #include "core/html/PluginDocument.h"
39 #include "core/loader/FrameLoaderClient.h"
40 #include "core/page/EventHandler.h"
41 #include "core/page/Page.h"
42 #include "core/frame/Settings.h"
43 #include "core/plugins/PluginView.h"
44 #include "core/rendering/RenderEmbeddedObject.h"
45 #include "core/rendering/RenderImage.h"
46 #include "core/rendering/RenderWidget.h"
47 #include "platform/Logging.h"
48 #include "platform/MIMETypeFromURL.h"
49 #include "platform/MIMETypeRegistry.h"
50 #include "platform/Widget.h"
51 #include "platform/plugins/PluginData.h"
55 using namespace HTMLNames;
57 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& doc, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
58 : HTMLFrameOwnerElement(tagName, doc)
59 , m_isDelayingLoadEvent(false)
61 , m_isCapturingMouseEvents(false)
62 // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
63 // widget updates until after all children are parsed. For HTMLEmbedElement
64 // this delay is unnecessary, but it is simpler to make both classes share
65 // the same codepath in this class.
66 , m_needsWidgetUpdate(!createdByParser)
67 , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
68 , m_displayState(Playing)
70 setHasCustomStyleCallbacks();
73 HTMLPlugInElement::~HTMLPlugInElement()
75 ASSERT(!m_pluginWrapper); // cleared in detach()
76 ASSERT(!m_isDelayingLoadEvent);
79 _NPN_ReleaseObject(m_NPObject);
84 bool HTMLPlugInElement::canProcessDrag() const
86 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
89 bool HTMLPlugInElement::willRespondToMouseClickEvents()
91 if (isDisabledFormControl())
93 RenderObject* r = renderer();
94 return r && (r->isEmbeddedObject() || r->isWidget());
97 void HTMLPlugInElement::removeAllEventListeners()
99 HTMLFrameOwnerElement::removeAllEventListeners();
100 if (RenderWidget* renderer = existingRenderWidget()) {
101 if (Widget* widget = renderer->widget())
102 widget->eventListenersRemoved();
106 void HTMLPlugInElement::didMoveToNewDocument(Document& oldDocument)
109 m_imageLoader->elementDidMoveToNewDocument();
110 HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument);
113 void HTMLPlugInElement::attach(const AttachContext& context)
115 HTMLFrameOwnerElement::attach(context);
117 if (!renderer() || useFallbackContent())
121 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
122 m_imageLoader->updateFromElement();
123 } else if (needsWidgetUpdate()
124 && renderEmbeddedObject()
125 && !renderEmbeddedObject()->showsUnavailablePluginIndicator()
126 && !wouldLoadAsNetscapePlugin(m_url, m_serviceType)
127 && !m_isDelayingLoadEvent) {
128 m_isDelayingLoadEvent = true;
129 document().incrementLoadEventDelayCount();
130 document().loadPluginsSoon();
134 void HTMLPlugInElement::updateWidget()
136 RefPtr<HTMLPlugInElement> protector(this);
137 updateWidgetInternal();
138 if (m_isDelayingLoadEvent) {
139 m_isDelayingLoadEvent = false;
140 document().decrementLoadEventDelayCount();
144 void HTMLPlugInElement::detach(const AttachContext& context)
146 // Update the widget the next time we attach (detaching destroys the plugin).
147 // FIXME: None of this "needsWidgetUpdate" related code looks right.
148 if (renderer() && !useFallbackContent())
149 setNeedsWidgetUpdate(true);
150 if (m_isDelayingLoadEvent) {
151 m_isDelayingLoadEvent = false;
152 document().decrementLoadEventDelayCount();
157 if (m_isCapturingMouseEvents) {
158 if (LocalFrame* frame = document().frame())
159 frame->eventHandler().setCapturingMouseEventsNode(nullptr);
160 m_isCapturingMouseEvents = false;
164 _NPN_ReleaseObject(m_NPObject);
168 HTMLFrameOwnerElement::detach(context);
171 RenderObject* HTMLPlugInElement::createRenderer(RenderStyle* style)
173 // Fallback content breaks the DOM->Renderer class relationship of this
174 // class and all superclasses because createObject won't necessarily
175 // return a RenderEmbeddedObject, RenderPart or even RenderWidget.
176 if (useFallbackContent())
177 return RenderObject::createObject(this, style);
180 RenderImage* image = new RenderImage(this);
181 image->setImageResource(RenderImageResource::create());
185 return new RenderEmbeddedObject(this);
188 void HTMLPlugInElement::willRecalcStyle(StyleRecalcChange)
190 // FIXME: Why is this necessary? Manual re-attach is almost always wrong.
191 if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType())
195 void HTMLPlugInElement::finishParsingChildren()
197 HTMLFrameOwnerElement::finishParsingChildren();
198 if (useFallbackContent())
201 setNeedsWidgetUpdate(true);
203 setNeedsStyleRecalc(SubtreeStyleChange);
206 void HTMLPlugInElement::resetInstance()
208 m_pluginWrapper.clear();
211 SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper()
213 LocalFrame* frame = document().frame();
217 // If the host dynamically turns off JavaScript (or Java) we will still
218 // return the cached allocated Bindings::Instance. Not supporting this
220 if (!m_pluginWrapper) {
221 if (Widget* widget = pluginWidget())
222 m_pluginWrapper = frame->script().createPluginWrapper(widget);
224 return m_pluginWrapper.get();
227 Widget* HTMLPlugInElement::pluginWidget() const
229 if (RenderWidget* renderWidget = renderWidgetForJSBindings())
230 return renderWidget->widget();
234 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const
236 if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr)
238 return HTMLFrameOwnerElement::isPresentationAttribute(name);
241 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
243 if (name == widthAttr) {
244 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
245 } else if (name == heightAttr) {
246 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
247 } else if (name == vspaceAttr) {
248 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
249 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
250 } else if (name == hspaceAttr) {
251 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
252 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
253 } else if (name == alignAttr) {
254 applyAlignmentAttributeToStyle(value, style);
256 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name, value, style);
260 void HTMLPlugInElement::defaultEventHandler(Event* event)
262 // Firefox seems to use a fake event listener to dispatch events to plug-in
263 // (tested with mouse events only). This is observable via different order
264 // of events - in Firefox, event listeners specified in HTML attributes
265 // fires first, then an event gets dispatched to plug-in, and only then
266 // other event listeners fire. Hopefully, this difference does not matter in
269 // FIXME: Mouse down and scroll events are passed down to plug-in via custom
270 // code in EventHandler; these code paths should be united.
272 RenderObject* r = renderer();
273 if (!r || !r->isWidget())
275 if (r->isEmbeddedObject()) {
276 if (toRenderEmbeddedObject(r)->showsUnavailablePluginIndicator())
278 if (displayState() < Playing)
281 RefPtr<Widget> widget = toRenderWidget(r)->widget();
284 widget->handleEvent(event);
285 if (event->defaultHandled())
287 HTMLFrameOwnerElement::defaultEventHandler(event);
290 RenderWidget* HTMLPlugInElement::renderWidgetForJSBindings() const
292 // Needs to load the plugin immediatedly because this function is called
293 // when JavaScript code accesses the plugin.
294 // FIXME: Check if dispatching events here is safe.
295 document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously);
296 return existingRenderWidget();
299 bool HTMLPlugInElement::isKeyboardFocusable() const
301 if (!document().isActive())
303 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
306 bool HTMLPlugInElement::hasCustomFocusLogic() const
308 return !hasAuthorShadowRoot();
311 bool HTMLPlugInElement::isPluginElement() const
316 bool HTMLPlugInElement::rendererIsFocusable() const
318 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::rendererIsFocusable())
321 if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject())
323 return !toRenderEmbeddedObject(renderer())->showsUnavailablePluginIndicator();
326 NPObject* HTMLPlugInElement::getNPObject()
328 ASSERT(document().frame());
330 m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this);
334 bool HTMLPlugInElement::isImageType()
336 if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
337 m_serviceType = mimeTypeFromDataURL(m_url);
339 if (LocalFrame* frame = document().frame()) {
340 KURL completedURL = document().completeURL(m_url);
341 return frame->loader().client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
344 return Image::supportsType(m_serviceType);
347 const String HTMLPlugInElement::loadedMimeType() const
349 String mimeType = m_serviceType;
350 if (mimeType.isEmpty())
351 mimeType = mimeTypeFromURL(m_loadedUrl);
355 RenderEmbeddedObject* HTMLPlugInElement::renderEmbeddedObject() const
357 // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
358 // when using fallback content.
359 if (!renderer() || !renderer()->isEmbeddedObject())
361 return toRenderEmbeddedObject(renderer());
364 // We don't use m_url, as it may not be the final URL that the object loads,
365 // depending on <param> values.
366 bool HTMLPlugInElement::allowedToLoadFrameURL(const String& url)
368 KURL completeURL = document().completeURL(url);
369 if (contentFrame() && protocolIsJavaScript(completeURL)
370 && !document().securityOrigin()->canAccess(contentDocument()->securityOrigin()))
372 return document().frame()->isURLAllowed(completeURL);
375 // We don't use m_url, or m_serviceType as they may not be the final values
376 // that <object> uses depending on <param> values.
377 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
379 ASSERT(document().frame());
382 completedURL = document().completeURL(url);
383 return document().frame()->loader().client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin;
386 bool HTMLPlugInElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
388 if (url.isEmpty() && mimeType.isEmpty())
391 // FIXME: None of this code should use renderers!
392 RenderEmbeddedObject* renderer = renderEmbeddedObject();
397 KURL completedURL = document().completeURL(url);
398 if (!pluginIsLoadable(completedURL, mimeType))
402 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback))
403 return loadPlugin(completedURL, mimeType, paramNames, paramValues, useFallback);
405 // If the plug-in element already contains a subframe,
406 // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
407 // frame and set it as the RenderPart's widget, causing what was previously
408 // in the widget to be torn down.
409 return loadOrRedirectSubframe(completedURL, getNameAttribute(), true);
412 bool HTMLPlugInElement::loadPlugin(const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
414 LocalFrame* frame = document().frame();
416 if (!frame->loader().allowPlugins(AboutToInstantiatePlugin))
419 RenderEmbeddedObject* renderer = renderEmbeddedObject();
420 // FIXME: This code should not depend on renderer!
421 if (!renderer || useFallback)
424 WTF_LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
425 WTF_LOG(Plugins, " Loaded URL: %s", url.string().utf8().data());
428 bool loadManually = document().isPluginDocument() && !document().containsPlugins() && toPluginDocument(document()).shouldLoadPluginManually();
429 RefPtr<Widget> widget = frame->loader().client()->createPlugin(this, url, paramNames, paramValues, mimeType, loadManually, FrameLoaderClient::FailOnDetachedPlugin);
432 if (!renderer->showsUnavailablePluginIndicator())
433 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
437 renderer->setWidget(widget);
438 document().setContainsPlugins();
439 scheduleLayerUpdate();
443 bool HTMLPlugInElement::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
445 // Allow other plug-ins to win over QuickTime because if the user has
446 // installed a plug-in that can handle TIFF (which QuickTime can also
447 // handle) they probably intended to override QT.
448 if (document().frame()->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
449 const PluginData* pluginData = document().frame()->page()->pluginData();
450 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
451 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
455 ObjectContentType objectType = document().frame()->loader().client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages());
456 // If an object's content can't be handled and it has no fallback, let
457 // it be handled as a plugin to show the broken plugin icon.
458 useFallback = objectType == ObjectContentNone && hasFallback;
459 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
463 void HTMLPlugInElement::dispatchErrorEvent()
465 if (document().isPluginDocument() && document().ownerElement())
466 document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error));
468 dispatchEvent(Event::create(EventTypeNames::error));
471 bool HTMLPlugInElement::pluginIsLoadable(const KURL& url, const String& mimeType)
473 LocalFrame* frame = document().frame();
474 Settings* settings = frame->settings();
478 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType) && !settings->javaEnabled())
481 if (document().isSandboxed(SandboxPlugins))
484 if (!document().securityOrigin()->canDisplay(url)) {
485 FrameLoader::reportLocalLoadFailed(frame, url.string());
489 AtomicString declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
490 document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
491 fastGetAttribute(HTMLNames::typeAttr);
492 if (!document().contentSecurityPolicy()->allowObjectFromSource(url)
493 || !document().contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
494 renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
498 return frame->loader().mixedContentChecker()->canRunInsecureContent(document().securityOrigin(), url);
501 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot&)
503 userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
506 void HTMLPlugInElement::didAddShadowRoot(ShadowRoot& root)
508 if (root.isOldestAuthorShadowRoot())
509 lazyReattachIfAttached();
512 bool HTMLPlugInElement::useFallbackContent() const
514 return hasAuthorShadowRoot();