Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLPlugInElement.cpp
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.
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include "config.h"
24 #include "core/html/HTMLPlugInElement.h"
25
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/FrameView.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/csp/ContentSecurityPolicy.h"
37 #include "core/html/HTMLContentElement.h"
38 #include "core/html/HTMLImageLoader.h"
39 #include "core/html/PluginDocument.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"
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document& doc, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
59     : HTMLFrameOwnerElement(tagName, doc)
60     , m_isDelayingLoadEvent(false)
61     , m_NPObject(0)
62     , m_isCapturingMouseEvents(false)
63     // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
64     // widget updates until after all children are parsed. For HTMLEmbedElement
65     // this delay is unnecessary, but it is simpler to make both classes share
66     // the same codepath in this class.
67     , m_needsWidgetUpdate(!createdByParser)
68     , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
69     , m_displayState(Playing)
70 {
71     setHasCustomStyleCallbacks();
72 }
73
74 HTMLPlugInElement::~HTMLPlugInElement()
75 {
76     ASSERT(!m_pluginWrapper); // cleared in detach()
77     ASSERT(!m_isDelayingLoadEvent);
78
79     if (m_NPObject) {
80         _NPN_ReleaseObject(m_NPObject);
81         m_NPObject = 0;
82     }
83 }
84
85 bool HTMLPlugInElement::canProcessDrag() const
86 {
87     return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->canProcessDrag();
88 }
89
90 bool HTMLPlugInElement::willRespondToMouseClickEvents()
91 {
92     if (isDisabledFormControl())
93         return false;
94     RenderObject* r = renderer();
95     return r && (r->isEmbeddedObject() || r->isWidget());
96 }
97
98 void HTMLPlugInElement::removeAllEventListeners()
99 {
100     HTMLFrameOwnerElement::removeAllEventListeners();
101     if (RenderWidget* renderer = existingRenderWidget()) {
102         if (Widget* widget = renderer->widget())
103             widget->eventListenersRemoved();
104     }
105 }
106
107 void HTMLPlugInElement::didMoveToNewDocument(Document& oldDocument)
108 {
109     if (m_imageLoader)
110         m_imageLoader->elementDidMoveToNewDocument();
111     HTMLFrameOwnerElement::didMoveToNewDocument(oldDocument);
112 }
113
114 void HTMLPlugInElement::attach(const AttachContext& context)
115 {
116     HTMLFrameOwnerElement::attach(context);
117
118     if (!renderer() || useFallbackContent())
119         return;
120
121     if (isImageType()) {
122         if (!m_imageLoader)
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();
133     }
134 }
135
136 void HTMLPlugInElement::updateWidget()
137 {
138     RefPtr<HTMLPlugInElement> protector(this);
139     updateWidgetInternal();
140     if (m_isDelayingLoadEvent) {
141         m_isDelayingLoadEvent = false;
142         document().decrementLoadEventDelayCount();
143     }
144 }
145
146 void HTMLPlugInElement::requestPluginCreationWithoutRendererIfPossible()
147 {
148     if (m_serviceType.isEmpty())
149         return;
150
151     if (!document().frame()
152         || !document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType))
153         return;
154
155     if (renderer() && renderer()->isWidget())
156         return;
157
158     createPluginWithoutRenderer();
159 }
160
161 void HTMLPlugInElement::createPluginWithoutRenderer()
162 {
163     ASSERT(document().frame()->loader().client()->canCreatePluginWithoutRenderer(m_serviceType));
164
165     KURL url;
166     Vector<String> paramNames;
167     Vector<String> paramValues;
168
169     paramNames.append("type");
170     paramValues.append(m_serviceType);
171
172     bool useFallback = false;
173     loadPlugin(url, m_serviceType, paramNames, paramValues, useFallback, false);
174 }
175
176 void HTMLPlugInElement::detach(const AttachContext& context)
177 {
178     // Update the widget the next time we attach (detaching destroys the plugin).
179     // FIXME: None of this "needsWidgetUpdate" related code looks right.
180     if (renderer() && !useFallbackContent())
181         setNeedsWidgetUpdate(true);
182     if (m_isDelayingLoadEvent) {
183         m_isDelayingLoadEvent = false;
184         document().decrementLoadEventDelayCount();
185     }
186
187     // Only try to persist a plugin widget we actually own.
188     Widget* plugin = ownedWidget();
189     if (plugin && plugin->pluginShouldPersist())
190         m_persistedPluginWidget = plugin;
191     resetInstance();
192     // FIXME - is this next line necessary?
193     setWidget(nullptr);
194
195     if (m_isCapturingMouseEvents) {
196         if (LocalFrame* frame = document().frame())
197             frame->eventHandler().setCapturingMouseEventsNode(nullptr);
198         m_isCapturingMouseEvents = false;
199     }
200
201     if (m_NPObject) {
202         _NPN_ReleaseObject(m_NPObject);
203         m_NPObject = 0;
204     }
205
206     HTMLFrameOwnerElement::detach(context);
207 }
208
209 RenderObject* HTMLPlugInElement::createRenderer(RenderStyle* style)
210 {
211     // Fallback content breaks the DOM->Renderer class relationship of this
212     // class and all superclasses because createObject won't necessarily
213     // return a RenderEmbeddedObject, RenderPart or even RenderWidget.
214     if (useFallbackContent())
215         return RenderObject::createObject(this, style);
216
217     if (isImageType()) {
218         RenderImage* image = new RenderImage(this);
219         image->setImageResource(RenderImageResource::create());
220         return image;
221     }
222
223     return new RenderEmbeddedObject(this);
224 }
225
226 void HTMLPlugInElement::willRecalcStyle(StyleRecalcChange)
227 {
228     // FIXME: Why is this necessary? Manual re-attach is almost always wrong.
229     if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType())
230         reattach();
231 }
232
233 void HTMLPlugInElement::finishParsingChildren()
234 {
235     HTMLFrameOwnerElement::finishParsingChildren();
236     if (useFallbackContent())
237         return;
238
239     setNeedsWidgetUpdate(true);
240     if (inDocument())
241         setNeedsStyleRecalc(SubtreeStyleChange);
242 }
243
244 void HTMLPlugInElement::resetInstance()
245 {
246     m_pluginWrapper.clear();
247 }
248
249 SharedPersistent<v8::Object>* HTMLPlugInElement::pluginWrapper()
250 {
251     LocalFrame* frame = document().frame();
252     if (!frame)
253         return 0;
254
255     // If the host dynamically turns off JavaScript (or Java) we will still
256     // return the cached allocated Bindings::Instance. Not supporting this
257     // edge-case is OK.
258     if (!m_pluginWrapper) {
259         Widget* plugin;
260
261         if (m_persistedPluginWidget)
262             plugin = m_persistedPluginWidget.get();
263         else
264             plugin = pluginWidget();
265
266         if (plugin)
267             m_pluginWrapper = frame->script().createPluginWrapper(plugin);
268     }
269     return m_pluginWrapper.get();
270 }
271
272 Widget* HTMLPlugInElement::pluginWidget() const
273 {
274     if (RenderWidget* renderWidget = renderWidgetForJSBindings())
275         return renderWidget->widget();
276     return 0;
277 }
278
279 bool HTMLPlugInElement::isPresentationAttribute(const QualifiedName& name) const
280 {
281     if (name == widthAttr || name == heightAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr)
282         return true;
283     return HTMLFrameOwnerElement::isPresentationAttribute(name);
284 }
285
286 void HTMLPlugInElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
287 {
288     if (name == widthAttr) {
289         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
290     } else if (name == heightAttr) {
291         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
292     } else if (name == vspaceAttr) {
293         addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
294         addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
295     } else if (name == hspaceAttr) {
296         addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
297         addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
298     } else if (name == alignAttr) {
299         applyAlignmentAttributeToStyle(value, style);
300     } else {
301         HTMLFrameOwnerElement::collectStyleForPresentationAttribute(name, value, style);
302     }
303 }
304
305 void HTMLPlugInElement::defaultEventHandler(Event* event)
306 {
307     // Firefox seems to use a fake event listener to dispatch events to plug-in
308     // (tested with mouse events only). This is observable via different order
309     // of events - in Firefox, event listeners specified in HTML attributes
310     // fires first, then an event gets dispatched to plug-in, and only then
311     // other event listeners fire. Hopefully, this difference does not matter in
312     // practice.
313
314     // FIXME: Mouse down and scroll events are passed down to plug-in via custom
315     // code in EventHandler; these code paths should be united.
316
317     RenderObject* r = renderer();
318     if (!r || !r->isWidget())
319         return;
320     if (r->isEmbeddedObject()) {
321         if (toRenderEmbeddedObject(r)->showsUnavailablePluginIndicator())
322             return;
323         if (displayState() < Playing)
324             return;
325     }
326     RefPtr<Widget> widget = toRenderWidget(r)->widget();
327     if (!widget)
328         return;
329     widget->handleEvent(event);
330     if (event->defaultHandled())
331         return;
332     HTMLFrameOwnerElement::defaultEventHandler(event);
333 }
334
335 RenderWidget* HTMLPlugInElement::renderWidgetForJSBindings() const
336 {
337     // Needs to load the plugin immediatedly because this function is called
338     // when JavaScript code accesses the plugin.
339     // FIXME: Check if dispatching events here is safe.
340     document().updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasksSynchronously);
341     return existingRenderWidget();
342 }
343
344 bool HTMLPlugInElement::isKeyboardFocusable() const
345 {
346     if (!document().isActive())
347         return false;
348     return pluginWidget() && pluginWidget()->isPluginView() && toPluginView(pluginWidget())->supportsKeyboardFocus();
349 }
350
351 bool HTMLPlugInElement::hasCustomFocusLogic() const
352 {
353     return !hasAuthorShadowRoot();
354 }
355
356 bool HTMLPlugInElement::isPluginElement() const
357 {
358     return true;
359 }
360
361 bool HTMLPlugInElement::rendererIsFocusable() const
362 {
363     if (HTMLFrameOwnerElement::supportsFocus() && HTMLFrameOwnerElement::rendererIsFocusable())
364         return true;
365
366     if (useFallbackContent() || !renderer() || !renderer()->isEmbeddedObject())
367         return false;
368     return !toRenderEmbeddedObject(renderer())->showsUnavailablePluginIndicator();
369 }
370
371 NPObject* HTMLPlugInElement::getNPObject()
372 {
373     ASSERT(document().frame());
374     if (!m_NPObject)
375         m_NPObject = document().frame()->script().createScriptObjectForPluginElement(this);
376     return m_NPObject;
377 }
378
379 bool HTMLPlugInElement::isImageType()
380 {
381     if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
382         m_serviceType = mimeTypeFromDataURL(m_url);
383
384     if (LocalFrame* frame = document().frame()) {
385         KURL completedURL = document().completeURL(m_url);
386         return frame->loader().client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
387     }
388
389     return Image::supportsType(m_serviceType);
390 }
391
392 RenderEmbeddedObject* HTMLPlugInElement::renderEmbeddedObject() const
393 {
394     // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
395     // when using fallback content.
396     if (!renderer() || !renderer()->isEmbeddedObject())
397         return 0;
398     return toRenderEmbeddedObject(renderer());
399 }
400
401 // We don't use m_url, as it may not be the final URL that the object loads,
402 // depending on <param> values.
403 bool HTMLPlugInElement::allowedToLoadFrameURL(const String& url)
404 {
405     KURL completeURL = document().completeURL(url);
406     if (contentFrame() && protocolIsJavaScript(completeURL)
407         && !document().securityOrigin()->canAccess(contentDocument()->securityOrigin()))
408         return false;
409     return document().frame()->isURLAllowed(completeURL);
410 }
411
412 // We don't use m_url, or m_serviceType as they may not be the final values
413 // that <object> uses depending on <param> values.
414 bool HTMLPlugInElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
415 {
416     ASSERT(document().frame());
417     KURL completedURL;
418     if (!url.isEmpty())
419         completedURL = document().completeURL(url);
420     return document().frame()->loader().client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin;
421 }
422
423 bool HTMLPlugInElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
424 {
425     if (url.isEmpty() && mimeType.isEmpty())
426         return false;
427
428     // FIXME: None of this code should use renderers!
429     RenderEmbeddedObject* renderer = renderEmbeddedObject();
430     ASSERT(renderer);
431     if (!renderer)
432         return false;
433
434     KURL completedURL = document().completeURL(url);
435     if (!pluginIsLoadable(completedURL, mimeType))
436         return false;
437
438     bool useFallback;
439     bool requireRenderer = true;
440     if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback))
441         return loadPlugin(completedURL, mimeType, paramNames, paramValues, useFallback, requireRenderer);
442
443     // If the plug-in element already contains a subframe,
444     // loadOrRedirectSubframe will re-use it. Otherwise, it will create a new
445     // frame and set it as the RenderPart's widget, causing what was previously
446     // in the widget to be torn down.
447     return loadOrRedirectSubframe(completedURL, getNameAttribute(), true);
448 }
449
450 bool HTMLPlugInElement::loadPlugin(const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback, bool requireRenderer)
451 {
452     LocalFrame* frame = document().frame();
453
454     if (!frame->loader().allowPlugins(AboutToInstantiatePlugin))
455         return false;
456
457     RenderEmbeddedObject* renderer = renderEmbeddedObject();
458     // FIXME: This code should not depend on renderer!
459     if ((!renderer && requireRenderer) || useFallback)
460         return false;
461
462     WTF_LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
463     WTF_LOG(Plugins, "   Loaded URL: %s", url.string().utf8().data());
464     m_loadedUrl = url;
465
466     RefPtr<Widget> widget = m_persistedPluginWidget;
467     if (!widget) {
468         bool loadManually = document().isPluginDocument() && !document().containsPlugins() && toPluginDocument(document()).shouldLoadPluginManually();
469         FrameLoaderClient::DetachedPluginPolicy policy = requireRenderer ? FrameLoaderClient::FailOnDetachedPlugin : FrameLoaderClient::AllowDetachedPlugin;
470         widget = frame->loader().client()->createPlugin(this, url, paramNames, paramValues, mimeType, loadManually, policy);
471     }
472
473     if (!widget) {
474         if (renderer && !renderer->showsUnavailablePluginIndicator())
475             renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
476         return false;
477     }
478
479     if (renderer) {
480         setWidget(widget);
481         m_persistedPluginWidget = nullptr;
482     } else if (widget != m_persistedPluginWidget) {
483         m_persistedPluginWidget = widget;
484     }
485     document().setContainsPlugins();
486     scheduleLayerUpdate();
487     return true;
488 }
489
490 bool HTMLPlugInElement::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback)
491 {
492     // Allow other plug-ins to win over QuickTime because if the user has
493     // installed a plug-in that can handle TIFF (which QuickTime can also
494     // handle) they probably intended to override QT.
495     if (document().frame()->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
496         const PluginData* pluginData = document().frame()->page()->pluginData();
497         String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String();
498         if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
499             return true;
500     }
501
502     ObjectContentType objectType = document().frame()->loader().client()->objectContentType(url, mimeType, shouldPreferPlugInsForImages());
503     // If an object's content can't be handled and it has no fallback, let
504     // it be handled as a plugin to show the broken plugin icon.
505     useFallback = objectType == ObjectContentNone && hasFallback;
506     return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
507
508 }
509
510 void HTMLPlugInElement::dispatchErrorEvent()
511 {
512     if (document().isPluginDocument() && document().ownerElement())
513         document().ownerElement()->dispatchEvent(Event::create(EventTypeNames::error));
514     else
515         dispatchEvent(Event::create(EventTypeNames::error));
516 }
517
518 bool HTMLPlugInElement::pluginIsLoadable(const KURL& url, const String& mimeType)
519 {
520     LocalFrame* frame = document().frame();
521     Settings* settings = frame->settings();
522     if (!settings)
523         return false;
524
525     if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType) && !settings->javaEnabled())
526         return false;
527
528     if (document().isSandboxed(SandboxPlugins))
529         return false;
530
531     if (!document().securityOrigin()->canDisplay(url)) {
532         FrameLoader::reportLocalLoadFailed(frame, url.string());
533         return false;
534     }
535
536     AtomicString declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
537         document().ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
538         fastGetAttribute(HTMLNames::typeAttr);
539     if (!document().contentSecurityPolicy()->allowObjectFromSource(url)
540         || !document().contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
541         renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
542         return false;
543     }
544
545     return frame->loader().mixedContentChecker()->canRunInsecureContent(document().securityOrigin(), url);
546 }
547
548 void HTMLPlugInElement::didAddUserAgentShadowRoot(ShadowRoot&)
549 {
550     userAgentShadowRoot()->appendChild(HTMLContentElement::create(document()));
551 }
552
553 void HTMLPlugInElement::willAddFirstAuthorShadowRoot()
554 {
555     lazyReattachIfAttached();
556 }
557
558 bool HTMLPlugInElement::useFallbackContent() const
559 {
560     return hasAuthorShadowRoot();
561 }
562
563 }