SubframeLoader.cpp [plain text]
#include "config.h"
#include "SubframeLoader.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "ContentSecurityPolicy.h"
#include "DiagnosticLoggingKeys.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "HTMLAppletElement.h"
#include "HTMLAudioElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLNames.h"
#include "HTMLObjectElement.h"
#include "MIMETypeRegistry.h"
#include "Page.h"
#include "PluginData.h"
#include "PluginDocument.h"
#include "RenderEmbeddedObject.h"
#include "RenderView.h"
#include "ScriptController.h"
#include "SecurityOrigin.h"
#include "SecurityPolicy.h"
#include "Settings.h"
namespace WebCore {
using namespace HTMLNames;
SubframeLoader::SubframeLoader(Frame& frame)
: m_containsPlugins(false)
, m_frame(frame)
{
}
void SubframeLoader::clear()
{
m_containsPlugins = false;
}
bool SubframeLoader::requestFrame(HTMLFrameOwnerElement& ownerElement, const String& urlString, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
{
URL scriptURL;
URL url;
if (protocolIsJavaScript(urlString)) {
scriptURL = completeURL(urlString); url = blankURL();
} else
url = completeURL(urlString);
Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList);
if (!frame)
return false;
if (!scriptURL.isEmpty())
frame->script().executeIfJavaScriptURL(scriptURL);
return true;
}
bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType, bool shouldPreferPlugInsForImages)
{
URL completedURL;
if (!url.isEmpty())
completedURL = completeURL(url);
bool useFallback;
return shouldUsePlugin(completedURL, mimeType, shouldPreferPlugInsForImages, false, useFallback);
}
bool SubframeLoader::pluginIsLoadable(HTMLPlugInImageElement& pluginElement, const URL& url, const String& mimeType)
{
if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
if (!m_frame.settings().isJavaEnabled())
return false;
if (document() && document()->securityOrigin()->isLocal() && !m_frame.settings().isJavaEnabledForLocalFiles())
return false;
}
if (document()) {
if (document()->isSandboxed(SandboxPlugins))
return false;
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);
bool isInUserAgentShadowTree = pluginElement.isInUserAgentShadowTree();
if (!document()->contentSecurityPolicy()->allowObjectFromSource(url, isInUserAgentShadowTree)
|| !document()->contentSecurityPolicy()->allowPluginType(mimeType, declaredMimeType, url, isInUserAgentShadowTree)) {
RenderEmbeddedObject* renderer = pluginElement.renderEmbeddedObject();
renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
return false;
}
if (!m_frame.loader().mixedContentChecker().canRunInsecureContent(document()->securityOrigin(), url))
return false;
}
return true;
}
bool SubframeLoader::requestPlugin(HTMLPlugInImageElement& ownerElement, const URL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback)
{
if ((!allowPlugins(AboutToInstantiatePlugin) && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)))
return false;
if (!pluginIsLoadable(ownerElement, url, mimeType))
return false;
ASSERT(ownerElement.hasTagName(objectTag) || ownerElement.hasTagName(embedTag));
return loadPlugin(ownerElement, url, mimeType, paramNames, paramValues, useFallback);
}
static String findPluginMIMETypeFromURL(Page* page, const String& url)
{
if (!url)
return String();
size_t dotIndex = url.reverseFind('.');
if (dotIndex == notFound)
return String();
String extension = url.substring(dotIndex + 1);
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;
}
}
return String();
}
static void logPluginRequest(Page* page, const String& mimeType, const String& url, bool success)
{
if (!page || !page->settings().diagnosticLoggingEnabled())
return;
String newMIMEType = mimeType;
if (!newMIMEType) {
newMIMEType = findPluginMIMETypeFromURL(page, url);
if (!newMIMEType)
return;
}
String pluginFile = page->pluginData().pluginFileForMimeType(newMIMEType);
String description = !pluginFile ? newMIMEType : pluginFile;
ChromeClient& chromeClient = page->chrome().client();
chromeClient.logDiagnosticMessage(success ? DiagnosticLoggingKeys::pluginLoadedKey() : DiagnosticLoggingKeys::pluginLoadingFailedKey(), description, DiagnosticLoggingKeys::noopKey());
if (!page->hasSeenAnyPlugin())
chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsAtLeastOnePluginKey(), emptyString(), DiagnosticLoggingKeys::noopKey());
if (!page->hasSeenPlugin(description))
chromeClient.logDiagnosticMessage(DiagnosticLoggingKeys::pageContainsPluginKey(), description, DiagnosticLoggingKeys::noopKey());
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)
{
if (url.isEmpty() && mimeType.isEmpty())
return false;
URL completedURL;
if (!url.isEmpty())
completedURL = completeURL(url);
bool hasFallbackContent = isHTMLObjectElement(ownerElement) && toHTMLObjectElement(ownerElement).hasFallbackContent();
bool useFallback;
if (shouldUsePlugin(completedURL, mimeType, ownerElement.shouldPreferPlugInsForImages(), hasFallbackContent, useFallback)) {
bool success = requestPlugin(ownerElement, completedURL, mimeType, paramNames, paramValues, useFallback);
logPluginRequest(document()->page(), mimeType, completedURL, success);
return success;
}
return loadOrRedirectSubframe(ownerElement, completedURL, frameName, LockHistory::Yes, LockBackForwardList::Yes);
}
PassRefPtr<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"))
baseURLString = paramValues[i];
else if (equalIgnoringCase(paramNames[i], "codebase"))
codeBaseURLString = paramValues[i];
}
if (!codeBaseURLString.isEmpty()) {
URL codeBaseURL = completeURL(codeBaseURLString);
if (!element.document().securityOrigin()->canDisplay(codeBaseURL)) {
FrameLoader::reportLocalLoadFailed(&m_frame, codeBaseURL.string());
return nullptr;
}
const char javaAppletMimeType[] = "application/x-java-applet";
bool isInUserAgentShadowTree = element.isInUserAgentShadowTree();
if (!element.document().contentSecurityPolicy()->allowObjectFromSource(codeBaseURL, isInUserAgentShadowTree)
|| !element.document().contentSecurityPolicy()->allowPluginType(javaAppletMimeType, javaAppletMimeType, codeBaseURL, isInUserAgentShadowTree))
return nullptr;
}
if (baseURLString.isEmpty())
baseURLString = m_frame.document()->baseURL().string();
URL baseURL = completeURL(baseURLString);
RefPtr<Widget> widget;
if (allowPlugins(AboutToInstantiatePlugin))
widget = m_frame.loader().client().createJavaAppletWidget(size, &element, baseURL, paramNames, paramValues);
logPluginRequest(document()->page(), element.serviceType(), String(), widget);
if (!widget) {
RenderEmbeddedObject* renderer = element.renderEmbeddedObject();
if (!renderer->isPluginUnavailable())
renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
return nullptr;
}
m_containsPlugins = true;
return widget;
}
Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const AtomicString& frameName, LockHistory lockHistory, LockBackForwardList lockBackForwardList)
{
Frame* frame = ownerElement.contentFrame();
if (frame)
frame->navigationScheduler().scheduleLocationChange(m_frame.document()->securityOrigin(), url, m_frame.loader().outgoingReferrer(), lockHistory, lockBackForwardList);
else
frame = loadSubframe(ownerElement, url, frameName, m_frame.loader().outgoingReferrer());
if (!frame)
return nullptr;
ASSERT(ownerElement.contentFrame() == frame || !ownerElement.contentFrame());
return ownerElement.contentFrame();
}
Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement& ownerElement, const URL& url, const String& name, const String& referrer)
{
Ref<Frame> protect(m_frame);
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)) {
FrameLoader::reportLocalLoadFailed(&m_frame, url.string());
return nullptr;
}
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 (!frame) {
m_frame.loader().checkCallImplicitClose();
return nullptr;
}
frame->loader().started();
RenderObject* renderer = ownerElement.renderer();
FrameView* view = frame->view();
if (renderer && renderer->isWidget() && view)
toRenderWidget(renderer)->setWidget(view);
m_frame.loader().checkCallImplicitClose();
if (frame->loader().state() == FrameStateComplete && !frame->loader().policyDocumentLoader())
frame->loader().checkCompleted();
return frame.get();
}
bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins)
{
return m_frame.settings().arePluginsEnabled();
}
bool SubframeLoader::shouldUsePlugin(const URL& url, const String& mimeType, bool shouldPreferPlugInsForImages, bool hasFallback, bool& useFallback)
{
if (m_frame.loader().client().shouldAlwaysUsePluginDocument(mimeType)) {
useFallback = false;
return true;
}
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);
useFallback = objectType == ObjectContentNone && hasFallback;
return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin;
}
Document* SubframeLoader::document() const
{
return m_frame.document();
}
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 (!renderer || useFallback)
return false;
pluginElement.subframeLoaderWillCreatePlugIn(url);
IntSize contentSize = roundedIntSize(LayoutSize(renderer->contentWidth(), renderer->contentHeight()));
bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually();
#if PLATFORM(IOS)
if (document()->ownerElement())
loadManually = false;
#endif
RefPtr<Widget> widget = m_frame.loader().client().createPlugin(contentSize, &pluginElement, url, paramNames, paramValues, mimeType, loadManually);
if (!widget) {
if (!renderer->isPluginUnavailable())
renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginMissing);
return false;
}
pluginElement.subframeLoaderDidCreatePlugIn(widget.get());
renderer->setWidget(widget);
m_containsPlugins = true;
return true;
}
URL SubframeLoader::completeURL(const String& url) const
{
ASSERT(m_frame.document());
return m_frame.document()->completeURL(url);
}
}