Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / html / HTMLPlugInImageElement.cpp
index f4dfd1f..9e5c277 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2011, 2012, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2017 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 
 #include "Chrome.h"
 #include "ChromeClient.h"
-#include "Event.h"
-#include "EventHandler.h"
+#include "CommonVM.h"
+#include "ContentSecurityPolicy.h"
+#include "EventNames.h"
 #include "Frame.h"
-#include "FrameLoader.h"
 #include "FrameLoaderClient.h"
-#include "FrameView.h"
 #include "HTMLImageLoader.h"
-#include "JSDocumentFragment.h"
+#include "JSDOMConvertBoolean.h"
+#include "JSDOMConvertInterface.h"
+#include "JSDOMConvertStrings.h"
+#include "JSShadowRoot.h"
 #include "LocalizedStrings.h"
 #include "Logging.h"
-#include "MainFrame.h"
 #include "MouseEvent.h"
-#include "NodeList.h"
-#include "NodeRenderStyle.h"
 #include "Page.h"
+#include "PlatformMouseEvent.h"
 #include "PlugInClient.h"
 #include "PluginViewBase.h"
-#include "RenderEmbeddedObject.h"
 #include "RenderImage.h"
 #include "RenderSnapshottedPlugIn.h"
+#include "RenderTreeUpdater.h"
 #include "SchemeRegistry.h"
 #include "ScriptController.h"
 #include "SecurityOrigin.h"
 #include "Settings.h"
 #include "ShadowRoot.h"
-#include "StyleResolver.h"
+#include "StyleTreeResolver.h"
 #include "SubframeLoader.h"
-#include <JavaScriptCore/APICast.h>
-#include <JavaScriptCore/JSBase.h>
-#include <wtf/HashMap.h>
-#include <wtf/text/StringHash.h>
-
-#if PLATFORM(IOS)
-#include "HTMLIFrameElement.h"
-#include "RenderBlockFlow.h"
-#include "YouTubeEmbedShadowElement.h"
-#endif
+#include "TypedElementDescendantIterator.h"
+#include "UserGestureIndicator.h"
+#include <JavaScriptCore/CatchScope.h>
+#include <wtf/IsoMallocInlines.h>
 
 namespace WebCore {
 
-using namespace HTMLNames;
-
-typedef Vector<RefPtr<HTMLPlugInImageElement>> HTMLPlugInImageElementList;
-typedef HashMap<String, String> MimeTypeToLocalizedStringMap;
+WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLPlugInImageElement);
 
 static const int sizingTinyDimensionThreshold = 40;
 static const float sizingFullPageAreaRatioThreshold = 0.96;
-static const float autostartSoonAfterUserGestureThreshold = 5.0;
+static const Seconds autostartSoonAfterUserGestureThreshold = 5_s;
 
 // This delay should not exceed the snapshot delay in PluginView.cpp
-static const double simulatedMouseClickTimerDelay = .75;
-static const double removeSnapshotTimerDelay = 1.5;
+static const Seconds simulatedMouseClickTimerDelay { 750_ms };
+
+#if PLATFORM(COCOA)
+static const Seconds removeSnapshotTimerDelay { 1500_ms };
+#endif
 
-static const String titleText(Page* page, String mimeType)
+static const String titleText(Page& page, const String& mimeType)
 {
-    DEPRECATED_DEFINE_STATIC_LOCAL(MimeTypeToLocalizedStringMap, mimeTypeToLabelTitleMap, ());
-    String titleText = mimeTypeToLabelTitleMap.get(mimeType);
-    if (!titleText.isEmpty())
-        return titleText;
+    if (mimeType.isEmpty())
+        return snapshottedPlugInLabelTitle();
 
-    titleText = page->chrome().client().plugInStartLabelTitle(mimeType);
-    if (titleText.isEmpty())
-        titleText = snapshottedPlugInLabelTitle();
-    mimeTypeToLabelTitleMap.set(mimeType, titleText);
-    return titleText;
+    // FIXME: It's not consistent to get a string from the page's chrome client, but then cache it globally.
+    // If it's global, it should come from elsewhere. If it's per-page then it should be cached per page.
+    static NeverDestroyed<HashMap<String, String>> mimeTypeToLabelTitleMap;
+    return mimeTypeToLabelTitleMap.get().ensure(mimeType, [&] {
+        auto title = page.chrome().client().plugInStartLabelTitle(mimeType);
+        if (!title.isEmpty())
+            return title;
+        return snapshottedPlugInLabelTitle();
+    }).iterator->value;
 };
 
-static const String subtitleText(Page* page, String mimeType)
+static const String subtitleText(Page& page, const String& mimeType)
 {
-    DEPRECATED_DEFINE_STATIC_LOCAL(MimeTypeToLocalizedStringMap, mimeTypeToLabelSubtitleMap, ());
-    String subtitleText = mimeTypeToLabelSubtitleMap.get(mimeType);
-    if (!subtitleText.isEmpty())
-        return subtitleText;
+    if (mimeType.isEmpty())
+        return snapshottedPlugInLabelSubtitle();
 
-    subtitleText = page->chrome().client().plugInStartLabelSubtitle(mimeType);
-    if (subtitleText.isEmpty())
-        subtitleText = snapshottedPlugInLabelSubtitle();
-    mimeTypeToLabelSubtitleMap.set(mimeType, subtitleText);
-    return subtitleText;
+    // FIXME: It's not consistent to get a string from the page's chrome client, but then cache it globally.
+    // If it's global, it should come from elsewhere. If it's per-page then it should be cached per page.
+    static NeverDestroyed<HashMap<String, String>> mimeTypeToLabelSubtitleMap;
+    return mimeTypeToLabelSubtitleMap.get().ensure(mimeType, [&] {
+        auto subtitle = page.chrome().client().plugInStartLabelSubtitle(mimeType);
+        if (!subtitle.isEmpty())
+            return subtitle;
+        return snapshottedPlugInLabelSubtitle();
+    }).iterator->value;
 };
 
-HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document& document, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption)
+HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document& document)
     : HTMLPlugInElement(tagName, document)
-    // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay
-    // widget updates until after all children are parsed.  For HTMLEmbedElement
-    // this delay is unnecessary, but it is simpler to make both classes share
-    // the same codepath in this class.
-    , m_needsWidgetUpdate(!createdByParser)
-    , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages)
-    , m_needsDocumentActivationCallbacks(false)
-    , m_simulatedMouseClickTimer(this, &HTMLPlugInImageElement::simulatedMouseClickTimerFired, simulatedMouseClickTimerDelay)
-    , m_removeSnapshotTimer(this, &HTMLPlugInImageElement::removeSnapshotTimerFired)
-    , m_createdDuringUserGesture(ScriptController::processingUserGesture())
-    , m_isRestartedPlugin(false)
-    , m_needsCheckForSizeChange(false)
-    , m_plugInWasCreated(false)
-    , m_deferredPromotionToPrimaryPlugIn(false)
-    , m_snapshotDecision(SnapshotNotYetDecided)
+    , m_simulatedMouseClickTimer(*this, &HTMLPlugInImageElement::simulatedMouseClickTimerFired, simulatedMouseClickTimerDelay)
+    , m_removeSnapshotTimer(*this, &HTMLPlugInImageElement::removeSnapshotTimerFired)
+    , m_createdDuringUserGesture(UserGestureIndicator::processingUserGesture())
 {
     setHasCustomStyleResolveCallbacks();
 }
 
+void HTMLPlugInImageElement::finishCreating()
+{
+    scheduleUpdateForAfterStyleResolution();
+}
+
 HTMLPlugInImageElement::~HTMLPlugInImageElement()
 {
     if (m_needsDocumentActivationCallbacks)
-        document().unregisterForPageCacheSuspensionCallbacks(this);
+        document().unregisterForDocumentSuspensionCallbacks(this);
 }
 
 void HTMLPlugInImageElement::setDisplayState(DisplayState state)
@@ -137,7 +128,7 @@ void HTMLPlugInImageElement::setDisplayState(DisplayState state)
     if (state == RestartingWithPendingMouseClick || state == Restarting) {
         m_isRestartedPlugin = true;
         m_snapshotDecision = NeverSnapshot;
-        setNeedsStyleRecalc(SyntheticStyleChange);
+        invalidateStyleAndLayerComposition();
         if (displayState() == DisplayingSnapshot)
             m_removeSnapshotTimer.startOneShot(removeSnapshotTimerDelay);
     }
@@ -148,11 +139,8 @@ void HTMLPlugInImageElement::setDisplayState(DisplayState state)
 
 RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const
 {
-    // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers
-    // when using fallback content.
-    if (!renderer() || !renderer()->isEmbeddedObject())
-        return 0;
-    return toRenderEmbeddedObject(renderer());
+    // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers when using fallback content.
+    return is<RenderEmbeddedObject>(renderer()) ? downcast<RenderEmbeddedObject>(renderer()) : nullptr;
 }
 
 bool HTMLPlugInImageElement::isImageType()
@@ -160,369 +148,336 @@ bool HTMLPlugInImageElement::isImageType()
     if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
         m_serviceType = mimeTypeFromDataURL(m_url);
 
-    if (Frame* frame = document().frame()) {
-        URL completedURL = document().completeURL(m_url);
-        return frame->loader().client().objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage;
-    }
+    if (auto frame = makeRefPtr(document().frame()))
+        return frame->loader().client().objectContentType(document().completeURL(m_url), m_serviceType) == ObjectContentType::Image;
 
     return Image::supportsType(m_serviceType);
 }
 
-// We don't use m_url, as it may not be the final URL that the object loads,
-// depending on <param> values.
+// We don't use m_url, as it may not be the final URL that the object loads, depending on <param> values.
 bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url)
 {
     URL completeURL = document().completeURL(url);
-
-    if (contentFrame() && protocolIsJavaScript(completeURL)
-        && !document().securityOrigin()->canAccess(contentDocument()->securityOrigin()))
+    if (contentFrame() && WTF::protocolIsJavaScript(completeURL) && !document().securityOrigin().canAccess(contentDocument()->securityOrigin()))
         return false;
-
     return document().frame()->isURLAllowed(completeURL);
 }
 
 // We don't use m_url, or m_serviceType as they may not be the final values
 // that <object> uses depending on <param> values.
-bool HTMLPlugInImageElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType)
+bool HTMLPlugInImageElement::wouldLoadAsPlugIn(const String& url, const String& serviceType)
 {
     ASSERT(document().frame());
     URL completedURL;
     if (!url.isEmpty())
         completedURL = document().completeURL(url);
-
-    FrameLoader& frameLoader = document().frame()->loader();
-    if (frameLoader.client().objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin)
-        return true;
-    return false;
+    return document().frame()->loader().client().objectContentType(completedURL, serviceType) == ObjectContentType::PlugIn;
 }
 
-RenderPtr<RenderElement> HTMLPlugInImageElement::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> HTMLPlugInImageElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
 {
-    ASSERT(!document().inPageCache());
+    ASSERT(document().pageCacheState() == Document::NotInPageCache);
 
     if (displayState() >= PreparingPluginReplacement)
-        return HTMLPlugInElement::createElementRenderer(std::move(style));
+        return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
 
-    // Once a PlugIn Element creates its renderer, it needs to be told when the Document goes
+    // Once a plug-in element creates its renderer, it needs to be told when the document goes
     // inactive or reactivates so it can clear the renderer before going into the page cache.
     if (!m_needsDocumentActivationCallbacks) {
         m_needsDocumentActivationCallbacks = true;
-        document().registerForPageCacheSuspensionCallbacks(this);
+        document().registerForDocumentSuspensionCallbacks(this);
     }
 
     if (displayState() == DisplayingSnapshot) {
-        auto renderSnapshottedPlugIn = createRenderer<RenderSnapshottedPlugIn>(*this, std::move(style));
-        renderSnapshottedPlugIn->updateSnapshot(m_snapshotImage);
-        return std::move(renderSnapshottedPlugIn);
+        auto renderSnapshottedPlugIn = createRenderer<RenderSnapshottedPlugIn>(*this, WTFMove(style));
+        renderSnapshottedPlugIn->updateSnapshot(m_snapshotImage.get());
+        return WTFMove(renderSnapshottedPlugIn);
     }
 
-    // Fallback content breaks the DOM->Renderer class relationship of this
-    // class and all superclasses because createObject won't necessarily
-    // return a RenderEmbeddedObject or RenderWidget.
     if (useFallbackContent())
-        return RenderElement::createFor(*this, std::move(style));
+        return RenderElement::createFor(*this, WTFMove(style));
 
     if (isImageType())
-        return createRenderer<RenderImage>(*this, std::move(style));
+        return createRenderer<RenderImage>(*this, WTFMove(style));
 
-#if PLATFORM(IOS)
-    if (ShadowRoot* shadowRoot = this->shadowRoot()) {
-        Node* shadowNode = shadowRoot->firstChild();
-        if (shadowNode && shadowNode->isElementNode() && toElement(*shadowNode).shadowPseudoId() == "-apple-youtube-shadow-iframe")
-            return createRenderer<RenderBlockFlow>(*this, std::move(style));
-    }
-#endif
+    return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
+}
+
+bool HTMLPlugInImageElement::childShouldCreateRenderer(const Node& child) const
+{
+    if (is<RenderSnapshottedPlugIn>(renderer()) && !hasShadowRootParent(child))
+        return false;
 
-    return HTMLPlugInElement::createElementRenderer(std::move(style));
+    return HTMLPlugInElement::childShouldCreateRenderer(child);
 }
 
-bool HTMLPlugInImageElement::willRecalcStyle(Style::Change)
+void HTMLPlugInImageElement::willRecalcStyle(Style::Change change)
 {
+    // Make sure style recalcs scheduled by a child shadow tree don't trigger reconstruction and cause flicker.
+    if (change == Style::NoChange && styleValidity() == Style::Validity::Valid)
+        return;
+
     // FIXME: There shoudn't be need to force render tree reconstruction here.
     // It is only done because loading and load event dispatching is tied to render tree construction.
-    if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType() && (displayState() != DisplayingSnapshot))
-        setNeedsStyleRecalc(ReconstructRenderTree);
-    return true;
+    if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType() && displayState() != DisplayingSnapshot)
+        invalidateStyleAndRenderersForSubtree();
+}
+
+void HTMLPlugInImageElement::didRecalcStyle(Style::Change styleChange)
+{
+    scheduleUpdateForAfterStyleResolution();
+
+    HTMLPlugInElement::didRecalcStyle(styleChange);
 }
 
 void HTMLPlugInImageElement::didAttachRenderers()
 {
-    if (!isImageType()) {
-        RefPtr<HTMLPlugInImageElement> element = this;
-        Style::queuePostResolutionCallback([element]{
-            element->updateWidgetIfNecessary();
-        });
-        return;
+    m_needsWidgetUpdate = true;
+    scheduleUpdateForAfterStyleResolution();
+
+    // Update the RenderImageResource of the associated RenderImage.
+    if (m_imageLoader && is<RenderImage>(renderer())) {
+        auto& renderImageResource = downcast<RenderImage>(*renderer()).imageResource();
+        if (!renderImageResource.cachedImage())
+            renderImageResource.setCachedImage(m_imageLoader->image());
     }
-    if (!renderer() || useFallbackContent())
-        return;
 
-    // Image load might complete synchronously and cause us to re-enter.
-    RefPtr<HTMLPlugInImageElement> element = this;
-    Style::queuePostResolutionCallback([element]{
-        element->startLoadingImage();
-    });
+    HTMLPlugInElement::didAttachRenderers();
 }
 
 void HTMLPlugInImageElement::willDetachRenderers()
 {
-    // FIXME: Because of the insanity that is HTMLPlugInImageElement::willRecalcStyle,
-    // we can end up detaching during an attach() call, before we even have a
-    // renderer. In that case, don't mark the widget for update.
-    if (renderer() && !useFallbackContent()) {
-        // Update the widget the next time we attach (detaching destroys the plugin).
-        setNeedsWidgetUpdate(true);
-    }
+    auto widget = makeRefPtr(pluginWidget(PluginLoadingPolicy::DoNotLoad));
+    if (is<PluginViewBase>(widget))
+        downcast<PluginViewBase>(*widget).willDetachRenderer();
 
     HTMLPlugInElement::willDetachRenderers();
 }
 
-void HTMLPlugInImageElement::updateWidgetIfNecessary()
+void HTMLPlugInImageElement::scheduleUpdateForAfterStyleResolution()
 {
-    document().updateStyleIfNeeded();
-
-    if (!needsWidgetUpdate() || useFallbackContent() || isImageType())
+    if (m_hasUpdateScheduledForAfterStyleResolution)
         return;
 
-    if (!renderEmbeddedObject() || renderEmbeddedObject()->isPluginUnavailable())
-        return;
+    document().incrementLoadEventDelayCount();
+
+    m_hasUpdateScheduledForAfterStyleResolution = true;
 
-    updateWidget(CreateOnlyNonNetscapePlugins);
+    Style::queuePostResolutionCallback([protectedThis = makeRef(*this)] {
+        protectedThis->updateAfterStyleResolution();
+    });
 }
 
-void HTMLPlugInImageElement::finishParsingChildren()
+void HTMLPlugInImageElement::updateAfterStyleResolution()
 {
-    HTMLPlugInElement::finishParsingChildren();
-    if (useFallbackContent())
-        return;
+    m_hasUpdateScheduledForAfterStyleResolution = false;
+
+    // Do this after style resolution, since the image or widget load might complete synchronously
+    // and cause us to re-enter otherwise. Also, we can't really answer the question "do I have a renderer"
+    // accurately until after style resolution.
 
-    setNeedsWidgetUpdate(true);
-    if (inDocument())
-        setNeedsStyleRecalc();
+    if (renderer() && !useFallbackContent()) {
+        if (isImageType()) {
+            if (!m_imageLoader)
+                m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
+            if (m_needsImageReload)
+                m_imageLoader->updateFromElementIgnoringPreviousError();
+            else
+                m_imageLoader->updateFromElement();
+        } else {
+            if (needsWidgetUpdate() && renderEmbeddedObject() && !renderEmbeddedObject()->isPluginUnavailable())
+                updateWidget(CreatePlugins::No);
+        }
+    }
+
+    // Either we reloaded the image just now, or we had some reason not to.
+    // Either way, clear the flag now, since we don't need to remember to try again.
+    m_needsImageReload = false;
+
+    document().decrementLoadEventDelayCount();
 }
 
-void HTMLPlugInImageElement::didMoveToNewDocument(Document* oldDocument)
+void HTMLPlugInImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
 {
+    ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
     if (m_needsDocumentActivationCallbacks) {
-        oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
-        document().registerForPageCacheSuspensionCallbacks(this);
+        oldDocument.unregisterForDocumentSuspensionCallbacks(this);
+        newDocument.registerForDocumentSuspensionCallbacks(this);
     }
 
     if (m_imageLoader)
         m_imageLoader->elementDidMoveToNewDocument();
 
-    HTMLPlugInElement::didMoveToNewDocument(oldDocument);
+    if (m_hasUpdateScheduledForAfterStyleResolution) {
+        oldDocument.decrementLoadEventDelayCount();
+        newDocument.incrementLoadEventDelayCount();
+    }
+
+    HTMLPlugInElement::didMoveToNewDocument(oldDocument, newDocument);
 }
 
-void HTMLPlugInImageElement::documentWillSuspendForPageCache()
+void HTMLPlugInImageElement::prepareForDocumentSuspension()
 {
     if (renderer())
-        Style::detachRenderTree(*this);
+        RenderTreeUpdater::tearDownRenderers(*this);
 
-    HTMLPlugInElement::documentWillSuspendForPageCache();
+    HTMLPlugInElement::prepareForDocumentSuspension();
 }
 
-void HTMLPlugInImageElement::documentDidResumeFromPageCache()
+void HTMLPlugInImageElement::resumeFromDocumentSuspension()
 {
-    setNeedsStyleRecalc(ReconstructRenderTree);
+    scheduleUpdateForAfterStyleResolution();
+    invalidateStyleAndRenderersForSubtree();
 
-    HTMLPlugInElement::documentDidResumeFromPageCache();
+    HTMLPlugInElement::resumeFromDocumentSuspension();
 }
 
-void HTMLPlugInImageElement::startLoadingImage()
-{
-    if (!m_imageLoader)
-        m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
-    m_imageLoader->updateFromElement();
-}
-
-void HTMLPlugInImageElement::updateSnapshot(PassRefPtr<Image> image)
+void HTMLPlugInImageElement::updateSnapshot(Image* image)
 {
     if (displayState() > DisplayingSnapshot)
         return;
 
     m_snapshotImage = image;
 
-    if (renderer()->isSnapshottedPlugIn()) {
-        toRenderSnapshottedPlugIn(renderer())->updateSnapshot(image);
+    auto* renderer = this->renderer();
+    if (!renderer)
         return;
-    }
-
-    if (renderer()->isEmbeddedObject())
-        renderer()->repaint();
-}
 
-void HTMLPlugInImageElement::checkSnapshotStatus()
-{
-    if (!renderer()->isSnapshottedPlugIn()) {
-        if (displayState() == Playing)
-            checkSizeChangeForSnapshotting();
+    if (is<RenderSnapshottedPlugIn>(*renderer)) {
+        downcast<RenderSnapshottedPlugIn>(*renderer).updateSnapshot(image);
         return;
     }
 
-    // Notify the shadow root that the size changed so that we may update the overlay layout.
-    ensureUserAgentShadowRoot().dispatchEvent(Event::create(eventNames().resizeEvent, true, false));
+    if (is<RenderEmbeddedObject>(*renderer))
+        renderer->repaint();
 }
 
 static DOMWrapperWorld& plugInImageElementIsolatedWorld()
 {
-    static DOMWrapperWorld& isolatedWorld = *DOMWrapperWorld::create(JSDOMWindow::commonVM()).leakRef();
+    static auto& isolatedWorld = DOMWrapperWorld::create(commonVM()).leakRef();
     return isolatedWorld;
 }
 
-void HTMLPlugInImageElement::didAddUserAgentShadowRoot(ShadowRoot* root)
+void HTMLPlugInImageElement::didAddUserAgentShadowRoot(ShadowRoot& root)
 {
     HTMLPlugInElement::didAddUserAgentShadowRoot(root);
     if (displayState() >= PreparingPluginReplacement)
         return;
 
-    Page* page = document().page();
+    auto* page = document().page();
     if (!page)
         return;
 
     // Reset any author styles that may apply as we only want explicit
     // styles defined in the injected user agents stylesheets to specify
     // the look-and-feel of the snapshotted plug-in overlay. 
-    root->setResetStyleInheritance(true);
+    root.setResetStyleInheritance(true);
     
-    String mimeType = loadedMimeType();
+    String mimeType = serviceType();
 
-    DOMWrapperWorld& isolatedWorld = plugInImageElementIsolatedWorld();
+    auto& isolatedWorld = plugInImageElementIsolatedWorld();
     document().ensurePlugInsInjectedScript(isolatedWorld);
 
-    ScriptController& scriptController = page->mainFrame().script();
-    JSDOMGlobalObject* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(isolatedWorld));
-    JSC::ExecState* exec = globalObject->globalExec();
+    auto& scriptController = document().frame()->script();
+    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(scriptController.globalObject(isolatedWorld));
 
-    JSC::JSLockHolder lock(exec);
+    auto& vm = globalObject.vm();
+    JSC::JSLockHolder lock(vm);
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    auto& state = *globalObject.globalExec();
 
     JSC::MarkedArgumentBuffer argList;
-    argList.append(toJS(exec, globalObject, root));
-    argList.append(jsString(exec, titleText(page, mimeType)));
-    argList.append(jsString(exec, subtitleText(page, mimeType)));
+    argList.append(toJS<IDLInterface<ShadowRoot>>(state, globalObject, root));
+    argList.append(toJS<IDLDOMString>(state, titleText(*page, mimeType)));
+    argList.append(toJS<IDLDOMString>(state, subtitleText(*page, mimeType)));
     
     // This parameter determines whether or not the snapshot overlay should always be visible over the plugin snapshot.
     // If no snapshot was found then we want the overlay to be visible.
-    argList.append(JSC::jsBoolean(!m_snapshotImage));
+    argList.append(toJS<IDLBoolean>(!m_snapshotImage));
+    ASSERT(!argList.hasOverflowed());
 
     // It is expected the JS file provides a createOverlay(shadowRoot, title, subtitle) function.
-    JSC::JSObject* overlay = globalObject->get(exec, JSC::Identifier(exec, "createOverlay")).toObject(exec);
+    auto* overlay = globalObject.get(&state, JSC::Identifier::fromString(&state, "createOverlay")).toObject(&state);
+    ASSERT(!overlay == !!scope.exception());
+    if (!overlay) {
+        scope.clearException();
+        return;
+    }
     JSC::CallData callData;
-    JSC::CallType callType = overlay->methodTable()->getCallData(overlay, callData);
-    if (callType == JSC::CallTypeNone)
+    auto callType = overlay->methodTable(vm)->getCallData(overlay, callData);
+    if (callType == JSC::CallType::None)
         return;
 
-    JSC::call(exec, overlay, callType, callData, globalObject, argList);
+    call(&state, overlay, callType, callData, &globalObject, argList);
+    scope.clearException();
 }
 
-bool HTMLPlugInImageElement::partOfSnapshotOverlay(Node* node)
+bool HTMLPlugInImageElement::partOfSnapshotOverlay(const EventTarget* target) const
 {
-    DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, selector, (".snapshot-overlay", AtomicString::ConstructFromLiteral));
-    RefPtr<Element> snapshotLabel = ensureUserAgentShadowRoot().querySelector(selector, ASSERT_NO_EXCEPTION);
-    return node && snapshotLabel && (node == snapshotLabel.get() || node->isDescendantOf(snapshotLabel.get()));
-}
-
-#if PLATFORM(IOS)
-void HTMLPlugInImageElement::createShadowIFrameSubtree(const String& src)
-{
-    if (this->shadowRoot())
-        return;
-
-    if (src.isEmpty())
-        return;
-
-    RefPtr<YouTubeEmbedShadowElement> shadowElement = YouTubeEmbedShadowElement::create(document());
-    ShadowRoot& root = this->ensureUserAgentShadowRoot();
-    root.appendChild(shadowElement, ASSERT_NO_EXCEPTION);
-
-    RefPtr<HTMLIFrameElement> iframeElement = HTMLIFrameElement::create(iframeTag, document());
-    if (hasAttribute(widthAttr))
-        iframeElement->setAttribute(widthAttr, AtomicString("100%", AtomicString::ConstructFromLiteral));
-    if (hasAttribute(heightAttr)) {
-        iframeElement->setAttribute(styleAttr, AtomicString("max-height: 100%", AtomicString::ConstructFromLiteral));
-        iframeElement->setAttribute(heightAttr, getAttribute(heightAttr));
-    }
-    iframeElement->setAttribute(srcAttr, src);
-    iframeElement->setAttribute(frameborderAttr, AtomicString("0", AtomicString::ConstructFromLiteral));
-
-    // Disable frame flattening for this iframe.
-    iframeElement->setAttribute(scrollingAttr, AtomicString("no", AtomicString::ConstructFromLiteral));
-    shadowElement->appendChild(iframeElement, ASSERT_NO_EXCEPTION);
+    static NeverDestroyed<AtomicString> selector(".snapshot-overlay", AtomicString::ConstructFromLiteral);
+    auto shadow = userAgentShadowRoot();
+    if (!shadow)
+        return false;
+    if (!is<Node>(target))
+        return false;
+    auto queryResult = shadow->querySelector(selector.get());
+    if (queryResult.hasException())
+        return false;
+    auto snapshotLabel = makeRefPtr(queryResult.releaseReturnValue());
+    return snapshotLabel && snapshotLabel->contains(downcast<Node>(target));
 }
-#endif
 
-void HTMLPlugInImageElement::removeSnapshotTimerFired(Timer<HTMLPlugInImageElement>&)
+void HTMLPlugInImageElement::removeSnapshotTimerFired()
 {
     m_snapshotImage = nullptr;
     m_isRestartedPlugin = false;
-    setNeedsStyleRecalc(SyntheticStyleChange);
+    invalidateStyleAndLayerComposition();
     if (renderer())
         renderer()->repaint();
 }
 
-static void addPlugInsFromNodeListMatchingPlugInOrigin(HTMLPlugInImageElementList& plugInList, PassRefPtr<NodeList> collection, const String& plugInOrigin, const String& mimeType)
-{
-    for (unsigned i = 0, length = collection->length(); i < length; i++) {
-        Node* node = collection->item(i);
-        if (node->isPluginElement()) {
-            HTMLPlugInElement* plugInElement = toHTMLPlugInElement(node);
-            if (plugInElement->isPlugInImageElement()) {
-                HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(node);
-                const URL& loadedURL = plugInImageElement->loadedUrl();
-                String otherMimeType = plugInImageElement->loadedMimeType();
-                if (plugInOrigin == loadedURL.host() && mimeType == otherMimeType)
-                    plugInList.append(plugInImageElement);
-            }
-        }
-    }
-}
-
 void HTMLPlugInImageElement::restartSimilarPlugIns()
 {
     // Restart any other snapshotted plugins in the page with the same origin. Note that they
     // may be in different frames, so traverse from the top of the document.
 
-    String plugInOrigin = m_loadedUrl.host();
-    String mimeType = loadedMimeType();
-    HTMLPlugInImageElementList similarPlugins;
+    auto plugInOrigin = m_loadedUrl.host();
+    String mimeType = serviceType();
+    Vector<Ref<HTMLPlugInImageElement>> similarPlugins;
 
     if (!document().page())
         return;
 
-    for (Frame* frame = &document().page()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
+    for (RefPtr<Frame> frame = &document().page()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
         if (!frame->loader().subframeLoader().containsPlugins())
             continue;
         
         if (!frame->document())
             continue;
 
-        RefPtr<NodeList> plugIns = frame->document()->getElementsByTagName(embedTag.localName());
-        if (plugIns)
-            addPlugInsFromNodeListMatchingPlugInOrigin(similarPlugins, plugIns, plugInOrigin, mimeType);
-
-        plugIns = frame->document()->getElementsByTagName(objectTag.localName());
-        if (plugIns)
-            addPlugInsFromNodeListMatchingPlugInOrigin(similarPlugins, plugIns, plugInOrigin, mimeType);
+        for (auto& element : descendantsOfType<HTMLPlugInImageElement>(*frame->document())) {
+            if (plugInOrigin == element.loadedUrl().host() && mimeType == element.serviceType())
+                similarPlugins.append(element);
+        }
     }
 
-    for (size_t i = 0, length = similarPlugins.size(); i < length; ++i) {
-        HTMLPlugInImageElement* plugInToRestart = similarPlugins[i].get();
+    for (auto& plugInToRestart : similarPlugins) {
         if (plugInToRestart->displayState() <= HTMLPlugInElement::DisplayingSnapshot) {
-            LOG(Plugins, "%p Plug-in looks similar to a restarted plug-in. Restart.", plugInToRestart);
+            LOG(Plugins, "%p Plug-in looks similar to a restarted plug-in. Restart.", plugInToRestart.ptr());
             plugInToRestart->restartSnapshottedPlugIn();
         }
         plugInToRestart->m_snapshotDecision = NeverSnapshot;
     }
 }
 
-void HTMLPlugInImageElement::userDidClickSnapshot(PassRefPtr<MouseEvent> event, bool forwardEvent)
+void HTMLPlugInImageElement::userDidClickSnapshot(MouseEvent& event, bool forwardEvent)
 {
     if (forwardEvent)
-        m_pendingClickEventFromSnapshot = event;
+        m_pendingClickEventFromSnapshot = &event;
 
-    String plugInOrigin = m_loadedUrl.host();
-    if (document().page() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(document().page()->mainFrame().document()->baseURL().protocol()) && document().page()->settings().autostartOriginPlugInSnapshottingEnabled())
-        document().page()->plugInClient()->didStartFromOrigin(document().page()->mainFrame().document()->baseURL().host(), plugInOrigin, loadedMimeType());
+    auto plugInOrigin = m_loadedUrl.host();
+    if (document().page() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(document().page()->mainFrame().document()->baseURL().protocol().toStringWithoutCopying()) && document().page()->settings().autostartOriginPlugInSnapshottingEnabled())
+        document().page()->plugInClient()->didStartFromOrigin(document().page()->mainFrame().document()->baseURL().host().toString(), plugInOrigin.toString(), serviceType(), document().page()->sessionID());
 
     LOG(Plugins, "%p User clicked on snapshotted plug-in. Restart.", this);
     restartSnapshottedPlugIn();
@@ -554,7 +509,7 @@ void HTMLPlugInImageElement::restartSnapshottedPlugIn()
         return;
 
     setDisplayState(Restarting);
-    setNeedsStyleRecalc(ReconstructRenderTree);
+    invalidateStyleAndRenderersForSubtree();
 }
 
 void HTMLPlugInImageElement::dispatchPendingMouseClick()
@@ -563,7 +518,7 @@ void HTMLPlugInImageElement::dispatchPendingMouseClick()
     m_simulatedMouseClickTimer.restart();
 }
 
-void HTMLPlugInImageElement::simulatedMouseClickTimerFired(DeferrableOneShotTimer<HTMLPlugInImageElement>&)
+void HTMLPlugInImageElement::simulatedMouseClickTimerFired()
 {
     ASSERT(displayState() == RestartingWithPendingMouseClick);
     ASSERT(m_pendingClickEventFromSnapshot);
@@ -576,15 +531,11 @@ void HTMLPlugInImageElement::simulatedMouseClickTimerFired(DeferrableOneShotTime
 
 static bool documentHadRecentUserGesture(Document& document)
 {
-    double lastKnownUserGestureTimestamp = document.lastHandledUserGestureTimestamp();
-
+    MonotonicTime lastKnownUserGestureTimestamp = document.lastHandledUserGestureTimestamp();
     if (document.frame() != &document.page()->mainFrame() && document.page()->mainFrame().document())
         lastKnownUserGestureTimestamp = std::max(lastKnownUserGestureTimestamp, document.page()->mainFrame().document()->lastHandledUserGestureTimestamp());
 
-    if (monotonicallyIncreasingTime() - lastKnownUserGestureTimestamp < autostartSoonAfterUserGestureThreshold)
-        return true;
-
-    return false;
+    return MonotonicTime::now() - lastKnownUserGestureTimestamp < autostartSoonAfterUserGestureThreshold;
 }
 
 void HTMLPlugInImageElement::checkSizeChangeForSnapshotting()
@@ -593,7 +544,8 @@ void HTMLPlugInImageElement::checkSizeChangeForSnapshotting()
         return;
 
     m_needsCheckForSizeChange = false;
-    LayoutRect contentBoxRect = toRenderBox(renderer())->contentBoxRect();
+
+    auto contentBoxRect = downcast<RenderBox>(*renderer()).contentBoxRect();
     int contentWidth = contentBoxRect.width();
     int contentHeight = contentBoxRect.height();
 
@@ -603,21 +555,71 @@ void HTMLPlugInImageElement::checkSizeChangeForSnapshotting()
     LOG(Plugins, "%p Plug-in originally avoided snapshotting because it was sized %dx%d. Now it is %dx%d. Tell it to snapshot.\n", this, m_sizeWhenSnapshotted.width(), m_sizeWhenSnapshotted.height(), contentWidth, contentHeight);
     setDisplayState(WaitingForSnapshot);
     m_snapshotDecision = Snapshotted;
-    Widget* widget = pluginWidget();
-    if (widget && widget->isPluginViewBase())
-        toPluginViewBase(widget)->beginSnapshottingRunningPlugin();
+    auto widget = makeRefPtr(pluginWidget());
+    if (is<PluginViewBase>(widget))
+        downcast<PluginViewBase>(*widget).beginSnapshottingRunningPlugin();
 }
 
 static inline bool is100Percent(Length length)
 {
     return length.isPercent() && length.percent() == 100;
 }
+    
+static inline bool isSmallerThanTinySizingThreshold(const RenderEmbeddedObject& renderer)
+{
+    auto contentRect = renderer.contentBoxRect();
+    return contentRect.width() <= sizingTinyDimensionThreshold || contentRect.height() <= sizingTinyDimensionThreshold;
+}
+    
+bool HTMLPlugInImageElement::isTopLevelFullPagePlugin(const RenderEmbeddedObject& renderer) const
+{
+    ASSERT(document().frame());
+    auto& frame = *document().frame();
+    if (!frame.isMainFrame())
+        return false;
+    
+    auto& style = renderer.style();
+    auto visibleSize = frame.view()->visibleSize();
+    auto contentRect = renderer.contentBoxRect();
+    float contentWidth = contentRect.width();
+    float contentHeight = contentRect.height();
+    return is100Percent(style.width()) && is100Percent(style.height()) && contentWidth * contentHeight > visibleSize.area().unsafeGet() * sizingFullPageAreaRatioThreshold;
+}
 
+void HTMLPlugInImageElement::checkSnapshotStatus()
+{
+    if (!is<RenderSnapshottedPlugIn>(*renderer())) {
+        if (displayState() == Playing)
+            checkSizeChangeForSnapshotting();
+        return;
+    }
+    
+    // If width and height styles were previously not set and we've snapshotted the plugin we may need to restart the plugin so that its state can be updated appropriately.
+    if (!document().page()->settings().snapshotAllPlugIns() && displayState() <= DisplayingSnapshot && !m_plugInDimensionsSpecified) {
+        auto& renderer = downcast<RenderSnapshottedPlugIn>(*this->renderer());
+        if (!renderer.style().logicalWidth().isSpecified() && !renderer.style().logicalHeight().isSpecified())
+            return;
+        
+        m_plugInDimensionsSpecified = true;
+        if (isTopLevelFullPagePlugin(renderer)) {
+            m_snapshotDecision = NeverSnapshot;
+            restartSnapshottedPlugIn();
+        } else if (isSmallerThanTinySizingThreshold(renderer)) {
+            m_snapshotDecision = MaySnapshotWhenResized;
+            restartSnapshottedPlugIn();
+        }
+        return;
+    }
+    
+    // Notify the shadow root that the size changed so that we may update the overlay layout.
+    ensureUserAgentShadowRoot().dispatchEvent(Event::create(eventNames().resizeEvent, Event::CanBubble::Yes, Event::IsCancelable::No));
+}
+    
 void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const URL& url)
 {
     LOG(Plugins, "%p Plug-in URL: %s", this, m_url.utf8().data());
     LOG(Plugins, "   Actual URL: %s", url.string().utf8().data());
-    LOG(Plugins, "   MIME type: %s", loadedMimeType().utf8().data());
+    LOG(Plugins, "   MIME type: %s", serviceType().utf8().data());
 
     m_loadedUrl = url;
     m_plugInWasCreated = false;
@@ -654,7 +656,7 @@ void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const URL& url)
         return;
     }
 
-    if (ScriptController::processingUserGesture()) {
+    if (UserGestureIndicator::processingUserGesture()) {
         LOG(Plugins, "%p Script is currently processing user gesture, set to play", this);
         m_snapshotDecision = NeverSnapshot;
         return;
@@ -679,43 +681,38 @@ void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const URL& url)
         return;
     }
 
-    if (document().page()->settings().autostartOriginPlugInSnapshottingEnabled() && document().page()->plugInClient() && document().page()->plugInClient()->shouldAutoStartFromOrigin(document().page()->mainFrame().document()->baseURL().host(), url.host(), loadedMimeType())) {
+    if (document().page()->settings().autostartOriginPlugInSnapshottingEnabled() && document().page()->plugInClient() && document().page()->plugInClient()->shouldAutoStartFromOrigin(document().page()->mainFrame().document()->baseURL().host().toString(), url.host().toString(), serviceType())) {
         LOG(Plugins, "%p Plug-in from (%s, %s) is marked to auto-start, set to play", this, document().page()->mainFrame().document()->baseURL().host().utf8().data(), url.host().utf8().data());
         m_snapshotDecision = NeverSnapshot;
         return;
     }
 
-    if (m_loadedUrl.isEmpty() && !loadedMimeType().isEmpty()) {
-        LOG(Plugins, "%p Plug-in has no src URL but does have a valid mime type %s, set to play", this, loadedMimeType().utf8().data());
+    if (m_loadedUrl.isEmpty() && !serviceType().isEmpty()) {
+        LOG(Plugins, "%p Plug-in has no src URL but does have a valid mime type %s, set to play", this, serviceType().utf8().data());
         m_snapshotDecision = MaySnapshotWhenContentIsSet;
         return;
     }
 
-    if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(m_loadedUrl.protocol()) && !m_loadedUrl.host().isEmpty() && m_loadedUrl.host() == document().page()->mainFrame().document()->baseURL().host()) {
+    if (!SchemeRegistry::shouldTreatURLSchemeAsLocal(m_loadedUrl.protocol().toStringWithoutCopying()) && !m_loadedUrl.host().isEmpty() && m_loadedUrl.host() == document().page()->mainFrame().document()->baseURL().host()) {
         LOG(Plugins, "%p Plug-in is served from page's domain, set to play", this);
         m_snapshotDecision = NeverSnapshot;
         return;
     }
-
-    auto& renderer = toRenderEmbeddedObject(*this->renderer());
-    LayoutRect contentRect = renderer.contentBoxRect();
+    
+    auto& renderer = downcast<RenderEmbeddedObject>(*this->renderer());
+    auto contentRect = renderer.contentBoxRect();
     int contentWidth = contentRect.width();
     int contentHeight = contentRect.height();
-
-    if (inMainFrame) {
-        auto& style = renderer.style();
-        bool isFullPage = is100Percent(style.width()) && is100Percent(style.height());
-        IntSize visibleViewSize = document().frame()->view()->visibleSize();
-        float contentArea = contentWidth * contentHeight;
-        float visibleArea = visibleViewSize.width() * visibleViewSize.height();
-        if (isFullPage && contentArea > visibleArea * sizingFullPageAreaRatioThreshold) {
-            LOG(Plugins, "%p Plug-in is top level full page, set to play", this);
-            m_snapshotDecision = NeverSnapshot;
-            return;
-        }
+    
+    m_plugInDimensionsSpecified = renderer.style().logicalWidth().isSpecified() || renderer.style().logicalHeight().isSpecified();
+    
+    if (isTopLevelFullPagePlugin(renderer)) {
+        LOG(Plugins, "%p Plug-in is top level full page, set to play", this);
+        m_snapshotDecision = NeverSnapshot;
+        return;
     }
 
-    if (contentWidth <= sizingTinyDimensionThreshold || contentHeight <= sizingTinyDimensionThreshold) {
+    if (isSmallerThanTinySizingThreshold(renderer)) {
         LOG(Plugins, "%p Plug-in is very small %dx%d, set to play", this, contentWidth, contentHeight);
         m_sizeWhenSnapshotted = IntSize(contentWidth, contentHeight);
         m_snapshotDecision = MaySnapshotWhenResized;
@@ -734,11 +731,11 @@ void HTMLPlugInImageElement::subframeLoaderWillCreatePlugIn(const URL& url)
     setDisplayState(WaitingForSnapshot);
 }
 
-void HTMLPlugInImageElement::subframeLoaderDidCreatePlugIn(const Widget* widget)
+void HTMLPlugInImageElement::subframeLoaderDidCreatePlugIn(const Widget& widget)
 {
     m_plugInWasCreated = true;
 
-    if (widget->isPluginViewBase() && toPluginViewBase(widget)->shouldAlwaysAutoStart()) {
+    if (is<PluginViewBase>(widget) && downcast<PluginViewBase>(widget).shouldAlwaysAutoStart()) {
         LOG(Plugins, "%p Plug-in should auto-start, set to play", this);
         m_snapshotDecision = NeverSnapshot;
         setDisplayState(Playing);
@@ -752,29 +749,68 @@ void HTMLPlugInImageElement::subframeLoaderDidCreatePlugIn(const Widget* widget)
     }
 }
 
-void HTMLPlugInImageElement::defaultEventHandler(Event* event)
+void HTMLPlugInImageElement::defaultEventHandler(Event& event)
 {
-    RenderElement* r = renderer();
-    if (r && r->isEmbeddedObject()) {
-        if (isPlugInImageElement() && displayState() == WaitingForSnapshot && event->isMouseEvent() && event->type() == eventNames().clickEvent) {
-            MouseEvent* mouseEvent = toMouseEvent(event);
-            if (mouseEvent->button() == LeftButton) {
-                userDidClickSnapshot(mouseEvent, true);
-                event->setDefaultHandled();
-                return;
-            }
+    if (is<RenderEmbeddedObject>(renderer()) && displayState() == WaitingForSnapshot && is<MouseEvent>(event) && event.type() == eventNames().clickEvent) {
+        auto& mouseEvent = downcast<MouseEvent>(event);
+        if (mouseEvent.button() == LeftButton) {
+            userDidClickSnapshot(mouseEvent, true);
+            mouseEvent.setDefaultHandled();
+            return;
         }
     }
     HTMLPlugInElement::defaultEventHandler(event);
 }
 
+bool HTMLPlugInImageElement::allowedToLoadPluginContent(const String& url, const String& mimeType) const
+{
+    // Elements in user agent show tree should load whatever the embedding document policy is.
+    if (isInUserAgentShadowTree())
+        return true;
+
+    URL completedURL;
+    if (!url.isEmpty())
+        completedURL = document().completeURL(url);
+
+    ASSERT(document().contentSecurityPolicy());
+    const ContentSecurityPolicy& contentSecurityPolicy = *document().contentSecurityPolicy();
+
+    contentSecurityPolicy.upgradeInsecureRequestIfNeeded(completedURL, ContentSecurityPolicy::InsecureRequestType::Load);
+
+    if (!contentSecurityPolicy.allowObjectFromSource(completedURL))
+        return false;
+
+    auto& declaredMimeType = document().isPluginDocument() && document().ownerElement() ?
+        document().ownerElement()->attributeWithoutSynchronization(HTMLNames::typeAttr) : attributeWithoutSynchronization(HTMLNames::typeAttr);
+    return contentSecurityPolicy.allowPluginType(mimeType, declaredMimeType, completedURL);
+}
+
 bool HTMLPlugInImageElement::requestObject(const String& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
 {
+    ASSERT(document().frame());
+
+    if (url.isEmpty() && mimeType.isEmpty())
+        return false;
+
+    if (!allowedToLoadPluginContent(url, mimeType)) {
+        renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
+        return false;
+    }
+
     if (HTMLPlugInElement::requestObject(url, mimeType, paramNames, paramValues))
         return true;
     
-    SubframeLoader& loader = document().frame()->loader().subframeLoader();
-    return loader.requestObject(*this, url, getNameAttribute(), mimeType, paramNames, paramValues);
+    return document().frame()->loader().subframeLoader().requestObject(*this, url, getNameAttribute(), mimeType, paramNames, paramValues);
+}
+
+void HTMLPlugInImageElement::updateImageLoaderWithNewURLSoon()
+{
+    if (m_needsImageReload)
+        return;
+
+    m_needsImageReload = true;
+    scheduleUpdateForAfterStyleResolution();
+    invalidateStyle();
 }
 
 } // namespace WebCore