Home | History | Annotate | Download | only in html
      1 /**
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2000 Stefan Schimanski (1Stein (at) 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 }
    602