The maximum subframe count check should not be skipped for empty URLs.
[WebKit-https.git] / Source / WebCore / loader / SubframeLoader.cpp
index 655dcf5..daa144e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
  * Copyright (C) 2008 Alp Toker <alp@atoker.com>
 #include "config.h"
 #include "SubframeLoader.h"
 
-#include "Chrome.h"
-#include "ChromeClient.h"
 #include "ContentSecurityPolicy.h"
+#include "DiagnosticLoggingClient.h"
 #include "DiagnosticLoggingKeys.h"
+#include "DocumentLoader.h"
 #include "Frame.h"
 #include "FrameLoader.h"
 #include "FrameLoaderClient.h"
 #include "HTMLAppletElement.h"
-#include "HTMLAudioElement.h"
-#include "HTMLFrameElementBase.h"
+#include "HTMLFrameElement.h"
+#include "HTMLIFrameElement.h"
 #include "HTMLNames.h"
 #include "HTMLObjectElement.h"
 #include "MIMETypeRegistry.h"
+#include "NavigationScheduler.h"
 #include "Page.h"
 #include "PluginData.h"
 #include "PluginDocument.h"
 #include "SecurityOrigin.h"
 #include "SecurityPolicy.h"
 #include "Settings.h"
-
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-#include "HTMLMediaElement.h"
-#include "RenderVideo.h"
-#endif
+#include <wtf/CompletionHandler.h>
 
 namespace WebCore {
     
@@ -76,66 +73,76 @@ void SubframeLoader::clear()
     m_containsPlugins = false;
 }
 
-bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
+bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
 {
     // Support for <frame src="javascript:string">
     URL scriptURL;
     URL url;
-    if (protocolIsJavaScript(urlString)) {
+    if (WTF::protocolIsJavaScript(urlString)) {
         scriptURL = completeURL(urlString); // completeURL() encodes the URL.
-        url = blankURL();
+        url = WTF::blankURL();
     } else
         url = completeURL(urlString);
 
+    if (shouldConvertInvalidURLsToBlank() && !url.isValid())
+        url = WTF::blankURL();
+
+    // If we will schedule a JavaScript URL load, we need to delay the firing of the load event at least until we've run the JavaScript in the URL.
+    CompletionHandlerCallingScope stopDelayingLoadEvent;
+    if (!scriptURL.isEmpty()) {
+        ownerElement.document().incrementLoadEventDelayCount();
+        stopDelayingLoadEvent = CompletionHandlerCallingScope([ownerDocument = makeRef(ownerElement.document())] {
+            ownerDocument->decrementLoadEventDelayCount();
+        });
+    }
+
     Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
     if (!frame)
         return false;
 
-    if (!scriptURL.isEmpty())
-        frame->script().executeIfJavaScriptURL(scriptURL);
+    if (!scriptURL.isEmpty() && ownerElement.canLoadScriptURL(scriptURL)) {
+        // FIXME: Some sites rely on the javascript:'' loading synchronously, which is why we have this special case.
+        // Blink has the same workaround (https://bugs.chromium.org/p/chromium/issues/detail?id=923585).
+        if (urlString == "javascript:''" || urlString == "javascript:\"\"")
+            frame->script().executeIfJavaScriptURL(scriptURL);
+        else
+            frame->navigationScheduler().scheduleLocationChange(ownerElement.document(), ownerElement.document().securityOrigin(), scriptURL, m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList, stopDelayingLoadEvent.release());
+    }
 
     return true;
 }
     
-bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
+bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType)
 {
     URL completedURL;
     if (!url.isEmpty())
         completedURL = completeURL(url);
 
     bool useFallback;
-    return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
+    return shouldUsePlugin(completedURL, mimeType, false, useFallback);
 }
 
-bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType)
+bool SubframeLoader::pluginIsLoadable(const URL& url, const String& mimeType)
 {
+    auto* document = m_frame.document();
+
     if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
         if (!m_frame.settings().isJavaEnabled())
             return false;
-        if (document() && document()->securityOrigin()->isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles())
+        if (document && document->securityOrigin().isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles())
             return false;
     }
 
-    if (document()) {
-        if (document()->isSandboxed(SandboxPlugins))
+    if (document) {
+        if (document->isSandboxed(SandboxPlugins))
             return false;
 
-        if (!document()->securityOrigin()->canDisplay(url)) {
+        if (!document->securityOrigin().canDisplay(url)) {
             FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
             return false;
         }
 
-        String declaredMimeType = document()->isPluginDocument() && document()->ownerElement() ?
-            document()->ownerElement()->fastGetAttribute(HTMLNames::typeAttr) :
-            pluginElement.fastGetAttribute(HTMLNames::typeAttr);
-        if (!document()->contentSecurityPolicy()->allowObjectFromSource(url)
-            || !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url)) {
-            RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject();
-            renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
-            return false;
-        }
-
-        if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document()->securityOrigin(), url))
+        if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document->securityOrigin(), url))
             return false;
     }
 
@@ -147,10 +154,10 @@ bool SubframeLoader::requestPlugin(HTMLPlugInImageElement& ownerElement, const U
     // Application plug-ins are plug-ins implemented by the user agent, for example Qt plug-ins,
     // as opposed to third-party code such as Flash. The user agent decides whether or not they are
     // permitted, rather than WebKit.
-    if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
+    if ((!allowPlugins() && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
         return false;
 
-    if (!pluginIsLoadable(ownerElement, url, mimeType))
+    if (!pluginIsLoadable(url, mimeType))
         return false;
 
     ASSERT(ownerElement.hasTagName(objectTag) || ownerElement.hasTagName(embedTag));
@@ -170,11 +177,13 @@ static String findPluginMIMETypeFromURL(Page* page, const String& url)
 
     const PluginData& pluginData = page->pluginData();
 
-    for (size_t i = 0; i < pluginData.mimes().size(); ++i) {
-        const MimeClassInfo& mimeClassInfo = pluginData.mimes()[i];
-        for (size_t j = 0; j < mimeClassInfo.extensions.size(); ++j) {
-            if (equalIgnoringCase(extension, mimeClassInfo.extensions[j]))
-                return mimeClassInfo.type;
+    Vector<MimeClassInfo> mimes;
+    Vector<size_t> mimePluginIndices;
+    pluginData.getWebVisibleMimesAndPluginIndices(mimes, mimePluginIndices);
+    for (auto& mime : mimes) {
+        for (auto& mimeExtension : mime.extensions) {
+            if (equalIgnoringASCIICase(extension, mimeExtension))
+                return mime.type;
         }
     }
 
@@ -183,7 +192,7 @@ static String findPluginMIMETypeFromURL(Page* page, const String& url)
 
 static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
 {
-    if (!page || !page->settings().diagnosticLoggingEnabled())
+    if (!page)
         return;
 
     String newMIMEType = mimeType;
@@ -194,36 +203,40 @@ static void logPluginRequest(Page* page, const String& mimeType, const String& u
             return;
     }
 
-    String pluginFile = page->pluginData().pluginFileForMimeType(newMIMEType);
+    String pluginFile = page->pluginData().pluginFileForWebVisibleMimeType(newMIMEType);
     String description = !pluginFile ? newMIMEType : pluginFile;
 
-    ChromeClient& chromeClient = page->chrome().client();
-    chromeClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey());
+    DiagnosticLoggingClient& diagnosticLoggingClient = page->diagnosticLoggingClient();
+    diagnosticLoggingClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, ShouldSample::No);
 
     if (!page->hasSeenAnyPlugin())
-        chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
-    
+        diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), ShouldSample::No);
+
     if (!page->hasSeenPlugin(description))
-        chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey());
+        diagnosticLoggingClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, ShouldSample::No);
 
     page->sawPlugin(description);
 }
 
-bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const String& url, const AtomicString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
+bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const String& url, const AtomString& frameName, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
 {
     if (url.isEmpty() && mimeType.isEmpty())
         return false;
 
+    auto& document = ownerElement.document();
+
     URL completedURL;
     if (!url.isEmpty())
         completedURL = completeURL(url);
 
-    bool hasFallbackContent = isHTMLObjectElement(ownerElement) && toHTMLObjectElement(ownerElement).hasFallbackContent();
+    document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(completedURL, ContentSecurityPolicy::InsecureRequestType::Load);
+
+    bool hasFallbackContent = is<HTMLObjectElement>(ownerElement) && downcast<HTMLObjectElement>(ownerElement).hasFallbackContent();
 
     bool useFallback;
-    if (shouldUsePlugin(completedURL, mimeType, ownerElement.shouldPreferPlugInsForImages(), hasFallbackContent, useFallback)) {
+    if (shouldUsePlugin(completedURL, mimeType, hasFallbackContent, useFallback)) {
         bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
-        logPluginRequest(document()->page(), mimeType, completedURL, success);
+        logPluginRequest(document.page(), mimeType, completedURL, success);
         return success;
     }
 
@@ -233,80 +246,43 @@ bool SubframeLoader::requestObject(HTMLPlugInImageElement& ownerElement, const S
     return loadOrRedirectSubframe(ownerElement, completedURL, frameName, LockHistory::Yes, LockBackForwardList::Yes);
 }
 
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(HTMLMediaElement& mediaElement, const URL& url, const Vector<String>& paramNames, const Vector<String>& paramValues)
-{
-    ASSERT(mediaElement.hasTagName(videoTag) || isHTMLAudioElement(mediaElement));
-
-    URL completedURL;
-    if (!url.isEmpty())
-        completedURL = completeURL(url);
-
-    if (!m_frame.document()->securityOrigin()->canDisplay(completedURL)) {
-        FrameLoader::reportLocalLoadFailed(&m_frame, completedURL.string());
-        return nullptr;
-    }
-
-    if (!m_frame.document()->contentSecurityPolicy()->allowMediaFromSource(completedURL))
-        return nullptr;
-
-    RenderWidget* renderer = toRenderWidget(mediaElement.renderer());
-    IntSize size;
-
-    if (renderer)
-        size = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
-    else if (mediaElement.isVideo())
-        size = RenderVideo::defaultSize();
-
-    if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(m_frame.document()->securityOrigin(), completedURL))
-        return nullptr;
-
-    RefPtr<Widget> widget = m_frame.loader().client().createMediaPlayerProxyPlugin(size, &mediaElement, completedURL, paramNames, paramValues, "application/x-media-element-proxy-plugin");
-
-    if (widget && renderer) {
-        renderer->setWidget(widget);
-        renderer->frameOwnerElement().setNeedsStyleRecalc(SyntheticStyleChange);
-    }
-    m_containsPlugins = true;
-
-    return widget ? widget.release() : nullptr;
-}
-#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-
-PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues)
+RefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement& element, const Vector<String>& paramNames, const Vector<String>& paramValues)
 {
     String baseURLString;
     String codeBaseURLString;
 
     for (size_t i = 0; i < paramNames.size(); ++i) {
-        if (equalIgnoringCase(paramNames[i], "baseurl"))
+        if (equalLettersIgnoringASCIICase(paramNames[i], "baseurl"))
             baseURLString = paramValues[i];
-        else if (equalIgnoringCase(paramNames[i], "codebase"))
+        else if (equalLettersIgnoringASCIICase(paramNames[i], "codebase"))
             codeBaseURLString = paramValues[i];
     }
 
     if (!codeBaseURLString.isEmpty()) {
         URL codeBaseURL = completeURL(codeBaseURLString);
-        if (!element.document().securityOrigin()->canDisplay(codeBaseURL)) {
+        if (!element.document().securityOrigin().canDisplay(codeBaseURL)) {
             FrameLoader::reportLocalLoadFailed(&m_frame, codeBaseURL.string());
             return nullptr;
         }
 
         const char javaAppletMimeType[] = "application/x-java-applet";
-        if (!element.document().contentSecurityPolicy()->allowObjectFromSource(codeBaseURL)
-            || !element.document().contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL))
+        ASSERT(element.document().contentSecurityPolicy());
+        auto& contentSecurityPolicy = *element.document().contentSecurityPolicy();
+        // Elements in user agent show tree should load whatever the embedding document policy is.
+        if (!element.isInUserAgentShadowTree()
+            && (!contentSecurityPolicy.allowObjectFromSource(codeBaseURL) || !contentSecurityPolicy.allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL)))
             return nullptr;
     }
 
     if (baseURLString.isEmpty())
-        baseURLString = m_frame.document()->baseURL().string();
+        baseURLString = element.document().baseURL().string();
     URL baseURL = completeURL(baseURLString);
 
     RefPtr<Widget> widget;
-    if (allowPlugins(AboutToInstantiatePlugin))
-        widget = m_frame.loader().client().createJavaAppletWidget(size, &element, baseURL, paramNames, paramValues);
+    if (allowPlugins())
+        widget = m_frame.loader().client().createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
 
-    logPluginRequest(document()->page(), element.serviceType(), String(), widget);
+    logPluginRequest(m_frame.page(), element.serviceType(), String(), widget);
 
     if (!widget) {
         RenderEmbeddedObject* renderer = element.renderEmbeddedObject();
@@ -320,13 +296,18 @@ PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, H
     return widget;
 }
 
-Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
+Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& requestURL, const AtomString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
 {
-    Frame* frame = ownerElement.contentFrame();
+    auto& initiatingDocument = ownerElement.document();
+
+    URL upgradedRequestURL = requestURL;
+    initiatingDocument.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(upgradedRequestURL, ContentSecurityPolicy::InsecureRequestType::Load);
+
+    auto* frame = ownerElement.contentFrame();
     if (frame)
-        frame->navigationScheduler().scheduleLocationChange(m_frame.document()->securityOrigin(), url.string(), m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList);
+        frame->navigationScheduler().scheduleLocationChange(initiatingDocument, initiatingDocument.securityOrigin(), upgradedRequestURL, m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList);
     else
-        frame = loadSubframe(ownerElement, url, frameName, m_frame.loader().outgoingReferrer());
+        frame = loadSubframe(ownerElement, upgradedRequestURL, frameName, m_frame.loader().outgoingReferrer());
 
     if (!frame)
         return nullptr;
@@ -338,18 +319,9 @@ Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElemen
 Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const String& name, const String& referrer)
 {
     Ref<Frame> protect(m_frame);
+    auto document = makeRef(ownerElement.document());
 
-    bool allowsScrolling = true;
-    int marginWidth = -1;
-    int marginHeight = -1;
-    if (ownerElement.hasTagName(frameTag) || ownerElement.hasTagName(iframeTag)) {
-        HTMLFrameElementBase& frameElementBase = toHTMLFrameElementBase(ownerElement);
-        allowsScrolling = frameElementBase.scrollingMode() != ScrollbarAlwaysOff;
-        marginWidth = frameElementBase.marginWidth();
-        marginHeight = frameElementBase.marginHeight();
-    }
-
-    if (!ownerElement.document().securityOrigin()->canDisplay(url)) {
+    if (!document->securityOrigin().canDisplay(url)) {
         FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
         return nullptr;
     }
@@ -357,14 +329,26 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U
     if (!SubframeLoadingDisabler::canLoadFrame(ownerElement))
         return nullptr;
 
-    String referrerToUse = SecurityPolicy::generateReferrerHeader(ownerElement.document().referrerPolicy(), url, referrer);
-    RefPtr<Frame> frame = m_frame.loader().client().createFrame(url, name, &ownerElement, referrerToUse, allowsScrolling, marginWidth, marginHeight);
+    if (!m_frame.page() || m_frame.page()->subframeCount() >= Page::maxNumberOfFrames)
+        return nullptr;
+
+    ReferrerPolicy policy = ownerElement.referrerPolicy();
+    if (policy == ReferrerPolicy::EmptyString)
+        policy = document->referrerPolicy();
+    String referrerToUse = SecurityPolicy::generateReferrerHeader(policy, url, referrer);
+
+    // Prevent initial empty document load from triggering load events.
+    document->incrementLoadEventDelayCount();
+
+    auto frame = m_frame.loader().client().createFrame(url, name, ownerElement, referrerToUse);
+
+    document->decrementLoadEventDelayCount();
 
     if (!frame)  {
         m_frame.loader().checkCallImplicitClose();
         return nullptr;
     }
-    
+
     // All new frames will have m_isComplete set to true at this point due to synchronously loading
     // an empty document in FrameLoader::init(). But many frames will now be starting an
     // asynchronous load of url, so we set m_isComplete to false and then check if the load is
@@ -373,13 +357,13 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U
     // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed.
     frame->loader().started();
    
-    RenderObject* renderer = ownerElement.renderer();
-    FrameView* view = frame->view();
-    if (renderer && renderer->isWidget() && view)
-        toRenderWidget(renderer)->setWidget(view);
-    
+    auto* renderer = ownerElement.renderer();
+    auto* view = frame->view();
+    if (is<RenderWidget>(renderer) && view)
+        downcast<RenderWidget>(*renderer).setWidget(view);
+
     m_frame.loader().checkCallImplicitClose();
-    
+
     // Some loads are performed synchronously (e.g., about:blank and loads
     // cancelled by returning a null ResourceRequest from requestFromDelegate).
     // In these cases, the synchronous load would have finished
@@ -395,58 +379,56 @@ Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const U
     return frame.get();
 }
 
-bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins)
+bool SubframeLoader::allowPlugins()
 {
     return m_frame.settings().arePluginsEnabled();
 }
 
-bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
+bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool hasFallback, bool& useFallback)
 {
     if (m_frame.loader().client().shouldAlwaysUsePluginDocument(mimeType)) {
         useFallback = false;
         return true;
     }
 
-    // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
-    // can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
-    if (m_frame.page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
-        String pluginName = m_frame.page()->pluginData().pluginNameForMimeType(mimeType);
-        if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) 
-            return true;
-    }
-        
-    ObjectContentType objectType = m_frame.loader().client().objectContentType(url, mimeType, shouldPreferPlugInsForImages);
+    ObjectContentType objectType = m_frame.loader().client().objectContentType(url, mimeType);
     // If an object's content can't be handled and it has no fallback, let
     // it be handled as a plugin to show the broken plugin icon.
-    useFallback = objectType == ObjectContentNone && hasFallback;
-    return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
-}
+    useFallback = objectType == ObjectContentType::None && hasFallback;
 
-Document* SubframeLoader::document() const
-{
-    return m_frame.document();
+    return objectType == ObjectContentType::None || objectType == ObjectContentType::PlugIn;
 }
 
 bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
 {
-    RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject();
+    if (useFallback)
+        return false;
+
+    auto& document = pluginElement.document();
+    auto* renderer = pluginElement.renderEmbeddedObject();
 
     // FIXME: This code should not depend on renderer!
-    if (!renderer || useFallback)
+    if (!renderer)
         return false;
 
     pluginElement.subframeLoaderWillCreatePlugIn(url);
 
     IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
-    bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
+    bool loadManually = is<PluginDocument>(document) && !m_containsPlugins && downcast<PluginDocument>(document).shouldLoadPluginManually();
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     // On iOS, we only tell the plugin to be in full page mode if the containing plugin document is the top level document.
-    if (document()->ownerElement())
+    if (document.ownerElement())
         loadManually = false;
 #endif
 
-    RefPtr<Widget> widget = m_frame.loader().client().createPlugin(contentSize, &pluginElement, url, paramNames, paramValues, mimeType, loadManually);
+    auto weakRenderer = makeWeakPtr(*renderer);
+
+    auto widget = m_frame.loader().client().createPlugin(contentSize, pluginElement, url, paramNames, paramValues, mimeType, loadManually);
+
+    // The call to createPlugin *may* cause this renderer to disappear from underneath.
+    if (!weakRenderer)
+        return false;
 
     if (!widget) {
         if (!renderer->isPluginUnavailable())
@@ -454,13 +436,9 @@ bool SubframeLoader::loadPlugin(HTMLPlugInImageElement& pluginElement, const URL
         return false;
     }
 
-    pluginElement.subframeLoaderDidCreatePlugIn(widget.get());
-    renderer->setWidget(widget);
+    pluginElement.subframeLoaderDidCreatePlugIn(*widget);
+    renderer->setWidget(WTFMove(widget));
     m_containsPlugins = true;
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
-    pluginElement.setNeedsStyleRecalc(SyntheticStyleChange);
-#endif
     return true;
 }
 
@@ -470,4 +448,9 @@ URL SubframeLoader::completeURL(const String& url) const
     return m_frame.document()->completeURL(url);
 }
 
+bool SubframeLoader::shouldConvertInvalidURLsToBlank() const
+{
+    return m_frame.settings().shouldConvertInvalidURLsToBlank();
+}
+
 } // namespace WebCore