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