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/PostAttachCallbacks.h"
33 #include "core/dom/shadow/ShadowRoot.h"
34 #include "core/events/Event.h"
35 #include "core/frame/ContentSecurityPolicy.h"
36 #include "core/frame/Frame.h"
37 #include "core/html/HTMLImageLoader.h"
38 #include "core/html/PluginDocument.h"
39 #include "core/html/shadow/HTMLContentElement.h"
40 #include "core/loader/FrameLoaderClient.h"
41 #include "core/page/EventHandler.h"
42 #include "core/page/Page.h"
43 #include "core/frame/Settings.h"
44 #include "core/plugins/PluginView.h"
45 #include "core/rendering/RenderEmbeddedObject.h"
46 #include "core/rendering/RenderImage.h"
47 #include "core/rendering/RenderWidget.h"
48 #include "platform/Logging.h"
49 #include "platform/MIMETypeFromURL.h"
50 #include "platform/MIMETypeRegistry.h"
51 #include "platform/Widget.h"
52 #include "platform/plugins/PluginData.h"
56 using namespace HTMLNames;
58 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& doc, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
59 : HTMLFrameOwnerElement(tagName, doc)
60 , m_isDelayingLoadEvent(false)
62 , m_isCapturingMouseEvents(false)
63 , m_inBeforeLoadEventHandler(false)
64 // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
65 // widget updates until after all children are parsed. For HTMLEmbedElement
66 // this delay is unnecessary, but it is simpler to make both classes share
67 // the same codepath in this class.
68 , m_needsWidgetUpdate(!createdByParser)
69 , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
70 , m_displayState(Playing)
72 setHasCustomStyleCallbacks();
75 HTMLPlugInElement::~HTMLPlugInElement()
77 ASSERT(!m_pluginWrapper); // cleared in detach()
78 ASSERT(!m_isDelayingLoadEvent);
81 _NPN_ReleaseObject(m_NPObject);
86 bool HTMLPlugInElement::canProcessDrag() const
88 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
91 bool HTMLPlugInElement::willRespondToMouseClickEvents()
93 if (isDisabledFormControl())
95 RenderObject* r = renderer();
96 return r && (r->isEmbeddedObject() || r->isWidget());
99 void HTMLPlugInElement::removeAllEventListeners()
101 HTMLFrameOwnerElement::removeAllEventListeners();
102 if (RenderWidget* renderer = existingRenderWidget()) {
103 if (Widget* widget = renderer->widget())
104 widget->eventListenersRemoved();
108 void HTMLPlugInElement::didMoveToNewDocument(Document& oldDocument)
111 m_imageLoader->elementDidMoveToNewDocument();
112 HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument);
115 void HTMLPlugInElement::attach(const AttachContext& context)
117 HTMLFrameOwnerElement::attach(context);
119 if (!renderer() || useFallbackContent())
123 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
124 m_imageLoader->updateFromElement();
125 } else if (needsWidgetUpdate()
126 && renderEmbeddedObject()
127 && !renderEmbeddedObject()->showsUnavailablePluginIndicator()
128 && !wouldLoadAsNetscapePlugin(m_url, m_serviceType)
129 && !m_isDelayingLoadEvent) {
130 m_isDelayingLoadEvent = true;
131 document().incrementLoadEventDelayCount();
132 document().loadPluginsSoon();
136 void HTMLPlugInElement::updateWidget()
138 RefPtr<HTMLPlugInElement> protector(this);
139 updateWidgetInternal();
140 if (m_isDelayingLoadEvent) {
141 m_isDelayingLoadEvent = false;
142 document().decrementLoadEventDelayCount();
146 void HTMLPlugInElement::detach(const AttachContext& context)
148 // Update the widget the next time we attach (detaching destroys the plugin).
149 // FIXME: None of this "needsWidgetUpdate" related code looks right.
150 if (renderer() && !useFallbackContent())
151 setNeedsWidgetUpdate(true);
152 if (m_isDelayingLoadEvent) {
153 m_isDelayingLoadEvent = false;
154 document().decrementLoadEventDelayCount();
159 if (m_isCapturingMouseEvents) {
160 if (Frame* frame = document().frame())
161 frame->eventHandler().setCapturingMouseEventsNode(0);
162 m_isCapturingMouseEvents = false;
166 _NPN_ReleaseObject(m_NPObject);
170 HTMLFrameOwnerElement::detach(context);
173 RenderObject* HTMLPlugInElement::createRenderer(RenderStyle* style)
175 // Fallback content breaks the DOM->Renderer class relationship of this
176 // class and all superclasses because createObject won't necessarily
177 // return a RenderEmbeddedObject, RenderPart or even RenderWidget.
178 if (useFallbackContent())
179 return RenderObject::createObject(this, style);
182 RenderImage* image = new RenderImage(this);
183 image->setImageResource(RenderImageResource::create());
187 return new RenderEmbeddedObject(this);
190 void HTMLPlugInElement::willRecalcStyle(StyleRecalcChange)
192 // FIXME: Why is this necessary? Manual re-attach is almost always wrong.
193 if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType())
197 void HTMLPlugInElement::finishParsingChildren()
199 HTMLFrameOwnerElement::finishParsingChildren();
200 if (useFallbackContent())
203 setNeedsWidgetUpdate(true);
205 setNeedsStyleRecalc(SubtreeStyleChange);
208 void HTMLPlugInElement::resetInstance()
210 m_pluginWrapper.clear();
213 SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper()
215 Frame* frame = document().frame();
219 // If the host dynamically turns off JavaScript (or Java) we will still
220 // return the cached allocated Bindings::Instance. Not supporting this
222 if (!m_pluginWrapper) {
223 if (Widget* widget = pluginWidget())
224 m_pluginWrapper = frame->script().createPluginWrapper(widget);
226 return m_pluginWrapper.get();
229 bool HTMLPlugInElement::dispatchBeforeLoadEvent(const String& sourceURL)
231 // FIXME: Our current plug-in loading design can't guarantee the following
232 // assertion is true, since plug-in loading can be initiated during layout,
233 // and synchronous layout can be initiated in a beforeload event handler!
234 // See <http://webkit.org/b/71264>.
235 // ASSERT(!m_inBeforeLoadEventHandler);
236 m_inBeforeLoadEventHandler = true;
237 bool beforeLoadAllowedLoad = HTMLFrameOwnerElement::dispatchBeforeLoadEvent(sourceURL);
238 m_inBeforeLoadEventHandler = false;
239 return beforeLoadAllowedLoad;
242 Widget* HTMLPlugInElement::pluginWidget() const
244 if (m_inBeforeLoadEventHandler) {
245 // The plug-in hasn't loaded yet, and it makes no sense to try to load
246 // if beforeload handler happened to touch the plug-in element. That
247 // would recursively call beforeload for the same element.
251 if (RenderWidget* renderWidget = renderWidgetForJSBindings())
252 return renderWidget->widget();
256 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const
258 if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr)
260 return HTMLFrameOwnerElement::isPresentationAttribute(name);
263 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
265 if (name == widthAttr) {
266 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
267 } else if (name == heightAttr) {
268 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
269 } else if (name == vspaceAttr) {
270 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
271 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
272 } else if (name == hspaceAttr) {
273 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
274 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
275 } else if (name == alignAttr) {
276 applyAlignmentAttributeToStyle(value, style);
278 HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name, value, style);
282 void HTMLPlugInElement::defaultEventHandler(Event* event)
284 // Firefox seems to use a fake event listener to dispatch events to plug-in
285 // (tested with mouse events only). This is observable via different order
286 // of events - in Firefox, event listeners specified in HTML attributes
287 // fires first, then an event gets dispatched to plug-in, and only then
288 // other event listeners fire. Hopefully, this difference does not matter in
291 // FIXME: Mouse down and scroll events are passed down to plug-in via custom
292 // code in EventHandler; these code paths should be united.
294 RenderObject* r = renderer();
295 if (!r || !r->isWidget())
297 if (r->isEmbeddedObject()) {
298 if (toRenderEmbeddedObject(r)->showsUnavailablePluginIndicator())
300 if (displayState() < Playing)
303 RefPtr<Widget> widget = toRenderWidget(r)->widget();
306 widget->handleEvent(event);
307 if (event->defaultHandled())
309 HTMLFrameOwnerElement::defaultEventHandler(event);
312 RenderWidget* HTMLPlugInElement::renderWidgetForJSBindings() const
314 // Needs to load the plugin immediatedly because this function is called
315 // when JavaScript code accesses the plugin.
316 // FIXME: Check if dispatching events here is safe.
317 document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously);
318 return existingRenderWidget();
321 bool HTMLPlugInElement::isKeyboardFocusable() const
323 if (!document().isActive())
325 return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
328 bool HTMLPlugInElement::hasCustomFocusLogic() const
330 return !hasAuthorShadowRoot();
333 bool HTMLPlugInElement::isPluginElement() const
338 bool HTMLPlugInElement::rendererIsFocusable() const
340 if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::rendererIsFocusable())
343 if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject())
345 return !toRenderEmbeddedObject(renderer())->showsUnavailablePluginIndicator();
348 NPObject* HTMLPlugInElement::getNPObject()
350 ASSERT(document().frame());
352 m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this);
356 bool HTMLPlugInElement::isImageType()
358 if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
359 m_serviceType = mimeTypeFromDataURL(m_url);
361 if (Frame* frame = document().frame()) {
362 KURL completedURL = document().completeURL(m_url);
363 return frame->loader().client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
366 return Image::supportsType(m_serviceType);
369 const String HTMLPlugInElement::loadedMimeType() const
371 String mimeType = m_serviceType;
372 if (mimeType.isEmpty())
373 mimeType = mimeTypeFromURL(m_loadedUrl);
377 RenderEmbeddedObject* HTMLPlugInElement::renderEmbeddedObject() const
379 // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
380 // when using fallback content.
381 if (!renderer() || !renderer()->isEmbeddedObject())
383 return toRenderEmbeddedObject(renderer());
386 // We don't use m_url, as it may not be the final URL that the object loads,
387 // depending on <param> values.
388 bool HTMLPlugInElement::allowedToLoadFrameURL(const String& url)
390 KURL completeURL = document().completeURL(url);
391 if (contentFrame() && protocolIsJavaScript(completeURL)
392 && !document().securityOrigin()->canAccess(contentDocument()->securityOrigin()))
394 return document().frame()->isURLAllowed(completeURL);
397 // We don't use m_url, or m_serviceType as they may not be the final values
398 // that <object> uses depending on <param> values.
399 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
401 ASSERT(document().frame());
404 completedURL = document().completeURL(url);
405 return document().frame()->loader().client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin;
408 bool HTMLPlugInElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
410 if (url.isEmpty() && mimeType.isEmpty())
413 // FIXME: None of this code should use renderers!
414 RenderEmbeddedObject* renderer = renderEmbeddedObject();
419 KURL completedURL = document().completeURL(url);
420 if (!pluginIsLoadable(completedURL, mimeType))
424 if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback))
425 return loadPlugin(completedURL, mimeType, paramNames, paramValues, useFallback);
427 // If the plug-in element already contains a subframe,
428 // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
429 // frame and set it as the RenderPart's widget, causing what was previously
430 // in the widget to be torn down.
431 return loadOrRedirectSubframe(completedURL, getNameAttribute(), true);
434 bool HTMLPlugInElement::loadPlugin(const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
436 Frame* frame = document().frame();
438 if (!frame->loader().allowPlugins(AboutToInstantiatePlugin))
441 RenderEmbeddedObject* renderer = renderEmbeddedObject();
442 // FIXME: This code should not depend on renderer!
443 if (!renderer || useFallback)
446 WTF_LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
447 WTF_LOG(Plugins, " Loaded URL: %s", url.string().utf8().data());
450 IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
451 bool loadManually = document().isPluginDocument() && !document().containsPlugins() && toPluginDocument(document()).shouldLoadPluginManually();
452 RefPtr<Widget> widget = frame->loader().client()->createPlugin(contentSize, this, url, paramNames, paramValues, mimeType, loadManually);
455 if (!renderer->showsUnavailablePluginIndicator())
456 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
460 renderer->setWidget(widget);
461 document().setContainsPlugins();
462 setNeedsStyleRecalc(LocalStyleChange, StyleChangeFromRenderer);
466 bool HTMLPlugInElement::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
468 // Allow other plug-ins to win over QuickTime because if the user has
469 // installed a plug-in that can handle TIFF (which QuickTime can also
470 // handle) they probably intended to override QT.
471 if (document().frame()->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
472 const PluginData* pluginData = document().frame()->page()->pluginData();
473 String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
474 if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
478 ObjectContentType objectType = document().frame()->loader().client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages());
479 // If an object's content can't be handled and it has no fallback, let
480 // it be handled as a plugin to show the broken plugin icon.
481 useFallback = objectType == ObjectContentNone && hasFallback;
482 return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
486 void HTMLPlugInElement::dispatchErrorEvent()
488 if (document().isPluginDocument() && document().ownerElement())
489 document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error));
491 dispatchEvent(Event::create(EventTypeNames::error));
494 bool HTMLPlugInElement::pluginIsLoadable(const KURL& url, const String& mimeType)
496 Frame* frame = document().frame();
497 Settings* settings = frame->settings();
501 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType) && !settings->javaEnabled())
504 if (document().isSandboxed(SandboxPlugins))
507 if (!document().securityOrigin()->canDisplay(url)) {
508 FrameLoader::reportLocalLoadFailed(frame, url.string());
512 AtomicString declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
513 document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
514 fastGetAttribute(HTMLNames::typeAttr);
515 if (!document().contentSecurityPolicy()->allowObjectFromSource(url)
516 || !document().contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
517 renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
521 return frame->loader().mixedContentChecker()->canRunInsecureContent(document().securityOrigin(), url);
524 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot&)
526 userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
529 void HTMLPlugInElement::didAddShadowRoot(ShadowRoot& root)
531 if (root.isOldestAuthorShadowRoot())
532 lazyReattachIfAttached();
535 bool HTMLPlugInElement::useFallbackContent() const
537 return hasAuthorShadowRoot();