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