HTMLPlugInImageElement.cpp [plain text]
#include "config.h"
#include "HTMLPlugInImageElement.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "CommonVM.h"
#include "ContentSecurityPolicy.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameLoaderClient.h"
#include "HTMLImageLoader.h"
#include "JSDOMConvertBoolean.h"
#include "JSDOMConvertInterface.h"
#include "JSDOMConvertStrings.h"
#include "JSShadowRoot.h"
#include "LegacySchemeRegistry.h"
#include "LocalizedStrings.h"
#include "Logging.h"
#include "MouseEvent.h"
#include "Page.h"
#include "PlatformMouseEvent.h"
#include "PluginViewBase.h"
#include "RenderImage.h"
#include "RenderTreeUpdater.h"
#include "ScriptController.h"
#include "SecurityOrigin.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "StyleTreeResolver.h"
#include "SubframeLoader.h"
#include "TypedElementDescendantIterator.h"
#include "UserGestureIndicator.h"
#include <JavaScriptCore/CatchScope.h>
#include <JavaScriptCore/JSGlobalObjectInlines.h>
#include <wtf/IsoMallocInlines.h>
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLPlugInImageElement);
HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document& document)
: HTMLPlugInElement(tagName, document)
{
}
void HTMLPlugInImageElement::finishCreating()
{
scheduleUpdateForAfterStyleResolution();
}
HTMLPlugInImageElement::~HTMLPlugInImageElement()
{
if (m_needsDocumentActivationCallbacks)
document().unregisterForDocumentSuspensionCallbacks(*this);
}
RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const
{
return is<RenderEmbeddedObject>(renderer()) ? downcast<RenderEmbeddedObject>(renderer()) : nullptr;
}
bool HTMLPlugInImageElement::isImageType()
{
if (m_serviceType.isEmpty() && protocolIs(m_url, "data"))
m_serviceType = mimeTypeFromDataURL(m_url);
if (auto frame = makeRefPtr(document().frame()))
return frame->loader().client().objectContentType(document().completeURL(m_url), m_serviceType) == ObjectContentType::Image;
return Image::supportsType(m_serviceType);
}
bool HTMLPlugInImageElement::canLoadURL(const String& relativeURL) const
{
return canLoadURL(document().completeURL(relativeURL));
}
bool HTMLPlugInImageElement::canLoadURL(const URL& completeURL) const
{
if (completeURL.protocolIsJavaScript()) {
RefPtr<Document> contentDocument = this->contentDocument();
if (contentDocument && !document().securityOrigin().canAccess(contentDocument->securityOrigin()))
return false;
}
return !isProhibitedSelfReference(completeURL);
}
bool HTMLPlugInImageElement::wouldLoadAsPlugIn(const String& relativeURL, const String& serviceType)
{
ASSERT(document().frame());
URL completedURL;
if (!relativeURL.isEmpty())
completedURL = document().completeURL(relativeURL);
return document().frame()->loader().client().objectContentType(completedURL, serviceType) == ObjectContentType::PlugIn;
}
RenderPtr<RenderElement> HTMLPlugInImageElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
{
ASSERT(document().backForwardCacheState() == Document::NotInBackForwardCache);
if (displayState() >= PreparingPluginReplacement)
return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
if (!m_needsDocumentActivationCallbacks) {
m_needsDocumentActivationCallbacks = true;
document().registerForDocumentSuspensionCallbacks(*this);
}
if (useFallbackContent())
return RenderElement::createFor(*this, WTFMove(style));
if (isImageType())
return createRenderer<RenderImage>(*this, WTFMove(style));
return HTMLPlugInElement::createElementRenderer(WTFMove(style), insertionPosition);
}
bool HTMLPlugInImageElement::childShouldCreateRenderer(const Node& child) const
{
return HTMLPlugInElement::childShouldCreateRenderer(child);
}
void HTMLPlugInImageElement::willRecalcStyle(Style::Change change)
{
if (change == Style::Change::None && styleValidity() == Style::Validity::Valid)
return;
if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType())
invalidateStyleAndRenderersForSubtree();
}
void HTMLPlugInImageElement::didRecalcStyle(Style::Change styleChange)
{
scheduleUpdateForAfterStyleResolution();
HTMLPlugInElement::didRecalcStyle(styleChange);
}
void HTMLPlugInImageElement::didAttachRenderers()
{
m_needsWidgetUpdate = true;
scheduleUpdateForAfterStyleResolution();
if (m_imageLoader && is<RenderImage>(renderer())) {
auto& renderImageResource = downcast<RenderImage>(*renderer()).imageResource();
if (!renderImageResource.cachedImage())
renderImageResource.setCachedImage(m_imageLoader->image());
}
HTMLPlugInElement::didAttachRenderers();
}
void HTMLPlugInImageElement::willDetachRenderers()
{
auto widget = makeRefPtr(pluginWidget(PluginLoadingPolicy::DoNotLoad));
if (is<PluginViewBase>(widget))
downcast<PluginViewBase>(*widget).willDetachRenderer();
HTMLPlugInElement::willDetachRenderers();
}
void HTMLPlugInImageElement::scheduleUpdateForAfterStyleResolution()
{
if (m_hasUpdateScheduledForAfterStyleResolution)
return;
document().incrementLoadEventDelayCount();
m_hasUpdateScheduledForAfterStyleResolution = true;
Style::queuePostResolutionCallback([protectedThis = makeRef(*this)] {
protectedThis->updateAfterStyleResolution();
});
}
void HTMLPlugInImageElement::updateAfterStyleResolution()
{
m_hasUpdateScheduledForAfterStyleResolution = false;
if (renderer() && !useFallbackContent()) {
if (isImageType()) {
if (!m_imageLoader)
m_imageLoader = makeUnique<HTMLImageLoader>(*this);
if (m_needsImageReload)
m_imageLoader->updateFromElementIgnoringPreviousError();
else
m_imageLoader->updateFromElement();
} else {
if (needsWidgetUpdate() && renderEmbeddedObject() && !renderEmbeddedObject()->isPluginUnavailable())
updateWidget(CreatePlugins::No);
}
}
m_needsImageReload = false;
document().decrementLoadEventDelayCount();
}
void HTMLPlugInImageElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
{
ASSERT_WITH_SECURITY_IMPLICATION(&document() == &newDocument);
if (m_needsDocumentActivationCallbacks) {
oldDocument.unregisterForDocumentSuspensionCallbacks(*this);
newDocument.registerForDocumentSuspensionCallbacks(*this);
}
if (m_imageLoader)
m_imageLoader->elementDidMoveToNewDocument(oldDocument);
if (m_hasUpdateScheduledForAfterStyleResolution) {
oldDocument.decrementLoadEventDelayCount();
newDocument.incrementLoadEventDelayCount();
}
HTMLPlugInElement::didMoveToNewDocument(oldDocument, newDocument);
}
void HTMLPlugInImageElement::prepareForDocumentSuspension()
{
if (renderer())
RenderTreeUpdater::tearDownRenderers(*this);
HTMLPlugInElement::prepareForDocumentSuspension();
}
void HTMLPlugInImageElement::resumeFromDocumentSuspension()
{
scheduleUpdateForAfterStyleResolution();
invalidateStyleAndRenderersForSubtree();
HTMLPlugInElement::resumeFromDocumentSuspension();
}
bool HTMLPlugInImageElement::canLoadPlugInContent(const String& relativeURL, const String& mimeType) const
{
if (isInUserAgentShadowTree())
return true;
URL completedURL;
if (!relativeURL.isEmpty())
completedURL = document().completeURL(relativeURL);
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& relativeURL, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues)
{
ASSERT(document().frame());
if (relativeURL.isEmpty() && mimeType.isEmpty())
return false;
if (!canLoadPlugInContent(relativeURL, mimeType)) {
renderEmbeddedObject()->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginBlockedByContentSecurityPolicy);
return false;
}
if (HTMLPlugInElement::requestObject(relativeURL, mimeType, paramNames, paramValues))
return true;
return document().frame()->loader().subframeLoader().requestObject(*this, relativeURL, getNameAttribute(), mimeType, paramNames, paramValues);
}
void HTMLPlugInImageElement::updateImageLoaderWithNewURLSoon()
{
if (m_needsImageReload)
return;
m_needsImageReload = true;
scheduleUpdateForAfterStyleResolution();
invalidateStyle();
}
}