blob: 40065bb451200e9b18508395ce2d5af62ccf1258 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights
* reserved.
* Copyright (C) 2010 Google 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
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "core/html/HTMLImageElement.h"
#include "bindings/core/v8/ScriptEventListener.h"
#include "core/CSSPropertyNames.h"
#include "core/HTMLNames.h"
#include "core/MediaTypeNames.h"
#include "core/css/MediaQueryMatcher.h"
#include "core/css/MediaValuesDynamic.h"
#include "core/css/parser/SizesAttributeParser.h"
#include "core/dom/Attribute.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/fetch/ImageResource.h"
#include "core/frame/Deprecation.h"
#include "core/frame/ImageBitmap.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/html/HTMLAnchorElement.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLFormElement.h"
#include "core/html/HTMLImageFallbackHelper.h"
#include "core/html/HTMLPictureElement.h"
#include "core/html/HTMLSourceElement.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/html/parser/HTMLSrcsetParser.h"
#include "core/imagebitmap/ImageBitmapOptions.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/layout/LayoutBlockFlow.h"
#include "core/layout/LayoutImage.h"
#include "core/layout/api/LayoutImageItem.h"
#include "core/page/Page.h"
#include "core/style/ContentData.h"
#include "core/svg/graphics/SVGImageForContainer.h"
#include "platform/ContentType.h"
#include "platform/EventDispatchForbiddenScope.h"
#include "platform/MIMETypeRegistry.h"
#include "platform/weborigin/SecurityPolicy.h"
namespace blink {
using namespace HTMLNames;
class HTMLImageElement::ViewportChangeListener final
: public MediaQueryListListener {
public:
static ViewportChangeListener* create(HTMLImageElement* element) {
return new ViewportChangeListener(element);
}
void notifyMediaQueryChanged() override {
if (m_element)
m_element->notifyViewportChanged();
}
DEFINE_INLINE_VIRTUAL_TRACE() {
visitor->trace(m_element);
MediaQueryListListener::trace(visitor);
}
private:
explicit ViewportChangeListener(HTMLImageElement* element)
: m_element(element) {}
Member<HTMLImageElement> m_element;
};
HTMLImageElement::HTMLImageElement(Document& document,
HTMLFormElement* form,
bool createdByParser)
: HTMLElement(imgTag, document),
ActiveScriptWrappable(this),
m_imageLoader(HTMLImageLoader::create(this)),
m_imageDevicePixelRatio(1.0f),
m_source(nullptr),
m_formWasSetByParser(false),
m_elementCreatedByParser(createdByParser),
m_useFallbackContent(false),
m_isFallbackImage(false),
m_referrerPolicy(ReferrerPolicyDefault) {
setHasCustomStyleCallbacks();
if (form && form->isConnected()) {
m_form = form;
m_formWasSetByParser = true;
m_form->associate(*this);
m_form->didAssociateByParser();
}
}
HTMLImageElement* HTMLImageElement::create(Document& document) {
return new HTMLImageElement(document);
}
HTMLImageElement* HTMLImageElement::create(Document& document,
HTMLFormElement* form,
bool createdByParser) {
return new HTMLImageElement(document, form, createdByParser);
}
HTMLImageElement::~HTMLImageElement() {}
DEFINE_TRACE(HTMLImageElement) {
visitor->trace(m_imageLoader);
visitor->trace(m_listener);
visitor->trace(m_form);
visitor->trace(m_source);
HTMLElement::trace(visitor);
}
void HTMLImageElement::notifyViewportChanged() {
// Re-selecting the source URL in order to pick a more fitting resource
// And update the image's intrinsic dimensions when the viewport changes.
// Picking of a better fitting resource is UA dependant, not spec required.
selectSourceURL(ImageLoader::UpdateSizeChanged);
}
HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document) {
HTMLImageElement* image = new HTMLImageElement(document);
image->m_elementCreatedByParser = false;
return image;
}
HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document,
int width) {
HTMLImageElement* image = new HTMLImageElement(document);
image->setWidth(width);
image->m_elementCreatedByParser = false;
return image;
}
HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document,
int width,
int height) {
HTMLImageElement* image = new HTMLImageElement(document);
image->setWidth(width);
image->setHeight(height);
image->m_elementCreatedByParser = false;
return image;
}
bool HTMLImageElement::isPresentationAttribute(
const QualifiedName& name) const {
if (name == widthAttr || name == heightAttr || name == borderAttr ||
name == vspaceAttr || name == hspaceAttr || name == alignAttr ||
name == valignAttr)
return true;
return HTMLElement::isPresentationAttribute(name);
}
void HTMLImageElement::collectStyleForPresentationAttribute(
const QualifiedName& name,
const AtomicString& value,
MutableStylePropertySet* style) {
if (name == widthAttr) {
addHTMLLengthToStyle(style, CSSPropertyWidth, value);
} else if (name == heightAttr) {
addHTMLLengthToStyle(style, CSSPropertyHeight, value);
} else if (name == borderAttr) {
applyBorderAttributeToStyle(value, style);
} else if (name == vspaceAttr) {
addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
} else if (name == hspaceAttr) {
addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
} else if (name == alignAttr) {
applyAlignmentAttributeToStyle(value, style);
} else if (name == valignAttr) {
addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign,
value);
} else {
HTMLElement::collectStyleForPresentationAttribute(name, value, style);
}
}
const AtomicString HTMLImageElement::imageSourceURL() const {
return m_bestFitImageURL.isNull() ? fastGetAttribute(srcAttr)
: m_bestFitImageURL;
}
HTMLFormElement* HTMLImageElement::formOwner() const {
return m_form.get();
}
void HTMLImageElement::formRemovedFromTree(const Node& formRoot) {
DCHECK(m_form);
if (NodeTraversal::highestAncestorOrSelf(*this) != formRoot)
resetFormOwner();
}
void HTMLImageElement::resetFormOwner() {
m_formWasSetByParser = false;
HTMLFormElement* nearestForm = findFormAncestor();
if (m_form) {
if (nearestForm == m_form.get())
return;
m_form->disassociate(*this);
}
if (nearestForm) {
m_form = nearestForm;
m_form->associate(*this);
} else {
m_form = nullptr;
}
}
void HTMLImageElement::setBestFitURLAndDPRFromImageCandidate(
const ImageCandidate& candidate) {
m_bestFitImageURL = candidate.url();
float candidateDensity = candidate.density();
float oldImageDevicePixelRatio = m_imageDevicePixelRatio;
if (candidateDensity >= 0)
m_imageDevicePixelRatio = 1.0 / candidateDensity;
bool intrinsicSizingViewportDependant = false;
if (candidate.getResourceWidth() > 0) {
intrinsicSizingViewportDependant = true;
UseCounter::count(document(), UseCounter::SrcsetWDescriptor);
} else if (!candidate.srcOrigin()) {
UseCounter::count(document(), UseCounter::SrcsetXDescriptor);
}
if (layoutObject() && layoutObject()->isImage()) {
LayoutImageItem(toLayoutImage(layoutObject()))
.setImageDevicePixelRatio(m_imageDevicePixelRatio);
if (oldImageDevicePixelRatio != m_imageDevicePixelRatio)
toLayoutImage(layoutObject())->intrinsicSizeChanged();
}
if (intrinsicSizingViewportDependant) {
if (!m_listener)
m_listener = ViewportChangeListener::create(this);
document().mediaQueryMatcher().addViewportListener(m_listener);
} else if (m_listener) {
document().mediaQueryMatcher().removeViewportListener(m_listener);
}
}
void HTMLImageElement::parseAttribute(const QualifiedName& name,
const AtomicString& oldValue,
const AtomicString& value) {
if (name == altAttr || name == titleAttr) {
if (userAgentShadowRoot()) {
Element* text = userAgentShadowRoot()->getElementById("alttext");
String value = altText();
if (text && text->textContent() != value)
text->setTextContent(altText());
}
} else if (name == srcAttr || name == srcsetAttr || name == sizesAttr) {
selectSourceURL(ImageLoader::UpdateIgnorePreviousError);
} else if (name == usemapAttr) {
setIsLink(!value.isNull());
} else if (name == referrerpolicyAttr) {
m_referrerPolicy = ReferrerPolicyDefault;
if (!value.isNull())
SecurityPolicy::referrerPolicyFromStringWithLegacyKeywords(
value, &m_referrerPolicy);
} else {
HTMLElement::parseAttribute(name, oldValue, value);
}
}
String HTMLImageElement::altText() const {
// lets figure out the alt text.. magic stuff
// http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
// also heavily discussed by Hixie on bugzilla
const AtomicString& alt = fastGetAttribute(altAttr);
if (!alt.isNull())
return alt;
// fall back to title attribute
return fastGetAttribute(titleAttr);
}
static bool supportedImageType(const String& type) {
String trimmedType = ContentType(type).type();
// An empty type attribute is implicitly supported.
if (trimmedType.isEmpty())
return true;
return MIMETypeRegistry::isSupportedImagePrefixedMIMEType(trimmedType);
}
// http://picture.responsiveimages.org/#update-source-set
ImageCandidate HTMLImageElement::findBestFitImageFromPictureParent() {
DCHECK(isMainThread());
Node* parent = parentNode();
m_source = nullptr;
if (!parent || !isHTMLPictureElement(*parent))
return ImageCandidate();
for (Node* child = parent->firstChild(); child;
child = child->nextSibling()) {
if (child == this)
return ImageCandidate();
if (!isHTMLSourceElement(*child))
continue;
HTMLSourceElement* source = toHTMLSourceElement(child);
if (!source->fastGetAttribute(srcAttr).isNull())
Deprecation::countDeprecation(document(), UseCounter::PictureSourceSrc);
String srcset = source->fastGetAttribute(srcsetAttr);
if (srcset.isEmpty())
continue;
String type = source->fastGetAttribute(typeAttr);
if (!type.isEmpty() && !supportedImageType(type))
continue;
if (!source->mediaQueryMatches())
continue;
ImageCandidate candidate = bestFitSourceForSrcsetAttribute(
document().devicePixelRatio(), sourceSize(*source),
source->fastGetAttribute(srcsetAttr), &document());
if (candidate.isEmpty())
continue;
m_source = source;
return candidate;
}
return ImageCandidate();
}
LayoutObject* HTMLImageElement::createLayoutObject(const ComputedStyle& style) {
const ContentData* contentData = style.contentData();
if (contentData && contentData->isImage()) {
const StyleImage* contentImage = toImageContentData(contentData)->image();
bool errorOccurred = contentImage && contentImage->cachedImage() &&
contentImage->cachedImage()->errorOccurred();
if (!errorOccurred)
return LayoutObject::createObject(this, style);
}
if (m_useFallbackContent)
return new LayoutBlockFlow(this);
LayoutImage* image = new LayoutImage(this);
image->setImageResource(LayoutImageResource::create());
image->setImageDevicePixelRatio(m_imageDevicePixelRatio);
return image;
}
void HTMLImageElement::attachLayoutTree(const AttachContext& context) {
HTMLElement::attachLayoutTree(context);
if (layoutObject() && layoutObject()->isImage()) {
LayoutImage* layoutImage = toLayoutImage(layoutObject());
LayoutImageResource* layoutImageResource = layoutImage->imageResource();
if (m_isFallbackImage) {
float deviceScaleFactor = blink::deviceScaleFactor(layoutImage->frame());
std::pair<Image*, float> brokenImageAndImageScaleFactor =
ImageResource::brokenImage(deviceScaleFactor);
ImageResource* newImageResource =
ImageResource::create(brokenImageAndImageScaleFactor.first);
layoutImage->imageResource()->setImageResource(newImageResource);
}
if (layoutImageResource->hasImage())
return;
if (!imageLoader().image() && !layoutImageResource->cachedImage())
return;
layoutImageResource->setImageResource(imageLoader().image());
}
}
Node::InsertionNotificationRequest HTMLImageElement::insertedInto(
ContainerNode* insertionPoint) {
if (!m_formWasSetByParser ||
NodeTraversal::highestAncestorOrSelf(*insertionPoint) !=
NodeTraversal::highestAncestorOrSelf(*m_form.get()))
resetFormOwner();
if (m_listener)
document().mediaQueryMatcher().addViewportListener(m_listener);
Node* parent = parentNode();
if (parent && isHTMLPictureElement(*parent))
toHTMLPictureElement(parent)->addListenerToSourceChildren();
bool imageWasModified = false;
if (document().isActive()) {
ImageCandidate candidate = findBestFitImageFromPictureParent();
if (!candidate.isEmpty()) {
setBestFitURLAndDPRFromImageCandidate(candidate);
imageWasModified = true;
}
}
// If we have been inserted from a layoutObject-less document,
// our loader may have not fetched the image, so do it now.
if ((insertionPoint->isConnected() && !imageLoader().image()) ||
imageWasModified)
imageLoader().updateFromElement(ImageLoader::UpdateNormal,
m_referrerPolicy);
return HTMLElement::insertedInto(insertionPoint);
}
void HTMLImageElement::removedFrom(ContainerNode* insertionPoint) {
if (!m_form ||
NodeTraversal::highestAncestorOrSelf(*m_form.get()) !=
NodeTraversal::highestAncestorOrSelf(*this))
resetFormOwner();
if (m_listener) {
document().mediaQueryMatcher().removeViewportListener(m_listener);
Node* parent = parentNode();
if (parent && isHTMLPictureElement(*parent))
toHTMLPictureElement(parent)->removeListenerFromSourceChildren();
}
HTMLElement::removedFrom(insertionPoint);
}
int HTMLImageElement::width() {
if (inActiveDocument())
document().updateStyleAndLayoutIgnorePendingStylesheets();
if (!layoutObject()) {
// check the attribute first for an explicit pixel value
bool ok;
int width = getAttribute(widthAttr).toInt(&ok);
if (ok)
return width;
// if the image is available, use its width
if (imageLoader().image())
return imageLoader()
.image()
->imageSize(LayoutObject::shouldRespectImageOrientation(nullptr),
1.0f)
.width()
.toInt();
}
LayoutBox* box = layoutBox();
return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedWidth(),
box)
: 0;
}
int HTMLImageElement::height() {
if (inActiveDocument())
document().updateStyleAndLayoutIgnorePendingStylesheets();
if (!layoutObject()) {
// check the attribute first for an explicit pixel value
bool ok;
int height = getAttribute(heightAttr).toInt(&ok);
if (ok)
return height;
// if the image is available, use its height
if (imageLoader().image())
return imageLoader()
.image()
->imageSize(LayoutObject::shouldRespectImageOrientation(nullptr),
1.0f)
.height()
.toInt();
}
LayoutBox* box = layoutBox();
return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedHeight(),
box)
: 0;
}
int HTMLImageElement::naturalWidth() const {
if (!imageLoader().image())
return 0;
return imageLoader()
.image()
->imageSize(LayoutObject::shouldRespectImageOrientation(layoutObject()),
m_imageDevicePixelRatio,
ImageResource::IntrinsicCorrectedToDPR)
.width()
.toInt();
}
int HTMLImageElement::naturalHeight() const {
if (!imageLoader().image())
return 0;
return imageLoader()
.image()
->imageSize(LayoutObject::shouldRespectImageOrientation(layoutObject()),
m_imageDevicePixelRatio,
ImageResource::IntrinsicCorrectedToDPR)
.height()
.toInt();
}
const String& HTMLImageElement::currentSrc() const {
// http://www.whatwg.org/specs/web-apps/current-work/multipage/edits.html#dom-img-currentsrc
// The currentSrc IDL attribute must return the img element's current
// request's current URL.
// Return the picked URL string in case of load error.
if (imageLoader().hadError())
return m_bestFitImageURL;
// Initially, the pending request turns into current request when it is either
// available or broken. We use the image's dimensions as a proxy to it being
// in any of these states.
if (!imageLoader().image() || !imageLoader().image()->getImage() ||
!imageLoader().image()->getImage()->width())
return emptyAtom;
return imageLoader().image()->url().getString();
}
bool HTMLImageElement::isURLAttribute(const Attribute& attribute) const {
return attribute.name() == srcAttr || attribute.name() == lowsrcAttr ||
attribute.name() == longdescAttr ||
(attribute.name() == usemapAttr && attribute.value()[0] != '#') ||
HTMLElement::isURLAttribute(attribute);
}
bool HTMLImageElement::hasLegalLinkAttribute(const QualifiedName& name) const {
return name == srcAttr || HTMLElement::hasLegalLinkAttribute(name);
}
const QualifiedName& HTMLImageElement::subResourceAttributeName() const {
return srcAttr;
}
bool HTMLImageElement::draggable() const {
// Image elements are draggable by default.
return !equalIgnoringCase(getAttribute(draggableAttr), "false");
}
void HTMLImageElement::setHeight(int value) {
setIntegralAttribute(heightAttr, value);
}
KURL HTMLImageElement::src() const {
return document().completeURL(getAttribute(srcAttr));
}
void HTMLImageElement::setSrc(const String& value) {
setAttribute(srcAttr, AtomicString(value));
}
void HTMLImageElement::setWidth(int value) {
setIntegralAttribute(widthAttr, value);
}
int HTMLImageElement::x() const {
document().updateStyleAndLayoutIgnorePendingStylesheets();
LayoutObject* r = layoutObject();
if (!r)
return 0;
// FIXME: This doesn't work correctly with transforms.
FloatPoint absPos = r->localToAbsolute();
return absPos.x();
}
int HTMLImageElement::y() const {
document().updateStyleAndLayoutIgnorePendingStylesheets();
LayoutObject* r = layoutObject();
if (!r)
return 0;
// FIXME: This doesn't work correctly with transforms.
FloatPoint absPos = r->localToAbsolute();
return absPos.y();
}
bool HTMLImageElement::complete() const {
return imageLoader().imageComplete();
}
void HTMLImageElement::didMoveToNewDocument(Document& oldDocument) {
selectSourceURL(ImageLoader::UpdateIgnorePreviousError);
imageLoader().elementDidMoveToNewDocument();
HTMLElement::didMoveToNewDocument(oldDocument);
}
bool HTMLImageElement::isServerMap() const {
if (!fastHasAttribute(ismapAttr))
return false;
const AtomicString& usemap = fastGetAttribute(usemapAttr);
// If the usemap attribute starts with '#', it refers to a map element in the
// document.
if (usemap[0] == '#')
return false;
return document()
.completeURL(stripLeadingAndTrailingHTMLSpaces(usemap))
.isEmpty();
}
Image* HTMLImageElement::imageContents() {
if (!imageLoader().imageComplete())
return nullptr;
return imageLoader().image()->getImage();
}
bool HTMLImageElement::isInteractiveContent() const {
return fastHasAttribute(usemapAttr);
}
PassRefPtr<Image> HTMLImageElement::getSourceImageForCanvas(
SourceImageStatus* status,
AccelerationHint,
SnapshotReason,
const FloatSize& defaultObjectSize) const {
if (!complete() || !cachedImage()) {
*status = IncompleteSourceImageStatus;
return nullptr;
}
if (cachedImage()->errorOccurred()) {
*status = UndecodableSourceImageStatus;
return nullptr;
}
RefPtr<Image> sourceImage;
if (cachedImage()->getImage()->isSVGImage()) {
SVGImage* svgImage = toSVGImage(cachedImage()->getImage());
IntSize imageSize =
roundedIntSize(svgImage->concreteObjectSize(defaultObjectSize));
sourceImage = SVGImageForContainer::create(
svgImage, imageSize, 1, document().completeURL(imageSourceURL()));
} else {
sourceImage = cachedImage()->getImage();
}
*status = NormalSourceImageStatus;
return sourceImage->imageForDefaultFrame();
}
bool HTMLImageElement::isSVGSource() const {
return cachedImage() && cachedImage()->getImage()->isSVGImage();
}
bool HTMLImageElement::wouldTaintOrigin(
SecurityOrigin* destinationSecurityOrigin) const {
ImageResource* image = cachedImage();
if (!image)
return false;
return !image->isAccessAllowed(destinationSecurityOrigin);
}
FloatSize HTMLImageElement::elementSize(
const FloatSize& defaultObjectSize) const {
ImageResource* image = cachedImage();
if (!image)
return FloatSize();
if (image->getImage() && image->getImage()->isSVGImage())
return toSVGImage(cachedImage()->getImage())
->concreteObjectSize(defaultObjectSize);
return FloatSize(image->imageSize(
LayoutObject::shouldRespectImageOrientation(layoutObject()), 1.0f));
}
FloatSize HTMLImageElement::defaultDestinationSize(
const FloatSize& defaultObjectSize) const {
ImageResource* image = cachedImage();
if (!image)
return FloatSize();
if (image->getImage() && image->getImage()->isSVGImage())
return toSVGImage(cachedImage()->getImage())
->concreteObjectSize(defaultObjectSize);
LayoutSize size;
size = image->imageSize(
LayoutObject::shouldRespectImageOrientation(layoutObject()), 1.0f);
if (layoutObject() && layoutObject()->isLayoutImage() && image->getImage() &&
!image->getImage()->hasRelativeSize())
size.scale(toLayoutImage(layoutObject())->imageDevicePixelRatio());
return FloatSize(size);
}
static bool sourceSizeValue(Element& element,
Document& currentDocument,
float& sourceSize) {
String sizes = element.fastGetAttribute(sizesAttr);
bool exists = !sizes.isNull();
if (exists)
UseCounter::count(currentDocument, UseCounter::Sizes);
sourceSize =
SizesAttributeParser(MediaValuesDynamic::create(currentDocument), sizes)
.length();
return exists;
}
FetchRequest::ResourceWidth HTMLImageElement::getResourceWidth() {
FetchRequest::ResourceWidth resourceWidth;
Element* element = m_source.get();
if (!element)
element = this;
resourceWidth.isSet =
sourceSizeValue(*element, document(), resourceWidth.width);
return resourceWidth;
}
float HTMLImageElement::sourceSize(Element& element) {
float value;
// We don't care here if the sizes attribute exists, so we ignore the return
// value. If it doesn't exist, we just return the default.
sourceSizeValue(element, document(), value);
return value;
}
void HTMLImageElement::forceReload() const {
imageLoader().updateFromElement(ImageLoader::UpdateForcedReload,
m_referrerPolicy);
}
ScriptPromise HTMLImageElement::createImageBitmap(
ScriptState* scriptState,
EventTarget& eventTarget,
Optional<IntRect> cropRect,
const ImageBitmapOptions& options,
ExceptionState& exceptionState) {
DCHECK(eventTarget.toLocalDOMWindow());
if ((cropRect &&
!ImageBitmap::isSourceSizeValid(cropRect->width(), cropRect->height(),
exceptionState)) ||
!ImageBitmap::isSourceSizeValid(bitmapSourceSize().width(),
bitmapSourceSize().height(),
exceptionState))
return ScriptPromise();
if (!ImageBitmap::isResizeOptionValid(options, exceptionState))
return ScriptPromise();
return ImageBitmapSource::fulfillImageBitmap(
scriptState,
ImageBitmap::create(this, cropRect,
eventTarget.toLocalDOMWindow()->document(), options));
}
void HTMLImageElement::selectSourceURL(
ImageLoader::UpdateFromElementBehavior behavior) {
if (!document().isActive())
return;
bool foundURL = false;
ImageCandidate candidate = findBestFitImageFromPictureParent();
if (!candidate.isEmpty()) {
setBestFitURLAndDPRFromImageCandidate(candidate);
foundURL = true;
}
if (!foundURL) {
candidate = bestFitSourceForImageAttributes(
document().devicePixelRatio(), sourceSize(*this),
fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr), &document());
setBestFitURLAndDPRFromImageCandidate(candidate);
}
imageLoader().updateFromElement(behavior, m_referrerPolicy);
// Images such as data: uri's can return immediately and may already have
// errored out.
bool imageHasLoaded = imageLoader().image() &&
!imageLoader().image()->isLoading() &&
!imageLoader().image()->errorOccurred();
bool imageStillLoading =
!imageHasLoaded && imageLoader().hasPendingActivity() &&
!imageLoader().hasPendingError() && !imageSourceURL().isEmpty();
bool imageHasImage =
imageLoader().image() && imageLoader().image()->hasImage();
bool imageIsDocument = imageLoader().isLoadingImageDocument() &&
imageLoader().image() &&
!imageLoader().image()->errorOccurred();
// Icky special case for deferred images:
// A deferred image is not loading, does have pending activity, does not
// have an error, but it does have an ImageResource associated
// with it, so imageHasLoaded will be true even though the image hasn't
// actually loaded. Fixing the definition of imageHasLoaded isn't
// sufficient, because a deferred image does have pending activity, does not
// have a pending error, and does have a source URL, so if imageHasLoaded
// was correct, imageStillLoading would become wrong.
//
// Instead of dealing with that, there's a separate check that the
// ImageResource has non-null image data associated with it, which isn't
// folded into imageHasLoaded above.
if ((imageHasLoaded && imageHasImage) || imageStillLoading || imageIsDocument)
ensurePrimaryContent();
else
ensureFallbackContent();
}
const KURL& HTMLImageElement::sourceURL() const {
return cachedImage()->response().url();
}
void HTMLImageElement::didAddUserAgentShadowRoot(ShadowRoot&) {
HTMLImageFallbackHelper::createAltTextShadowTree(*this);
}
void HTMLImageElement::ensureFallbackForGeneratedContent() {
setUseFallbackContent();
reattachFallbackContent();
}
void HTMLImageElement::ensureFallbackContent() {
if (m_useFallbackContent || m_isFallbackImage)
return;
setUseFallbackContent();
reattachFallbackContent();
}
void HTMLImageElement::ensurePrimaryContent() {
if (!m_useFallbackContent)
return;
m_useFallbackContent = false;
reattachFallbackContent();
}
void HTMLImageElement::reattachFallbackContent() {
// This can happen inside of attachLayoutTree() in the middle of a recalcStyle
// so we need to reattach synchronously here.
if (document().inStyleRecalc())
reattachLayoutTree();
else
lazyReattachIfAttached();
}
PassRefPtr<ComputedStyle> HTMLImageElement::customStyleForLayoutObject() {
RefPtr<ComputedStyle> newStyle = originalStyleForLayoutObject();
if (!m_useFallbackContent)
return newStyle;
RefPtr<ComputedStyle> style = ComputedStyle::clone(*newStyle);
return HTMLImageFallbackHelper::customStyleForAltText(*this, style);
}
void HTMLImageElement::setUseFallbackContent() {
m_useFallbackContent = true;
if (document().inStyleRecalc())
return;
EventDispatchForbiddenScope::AllowUserAgentEvents allowEvents;
ensureUserAgentShadowRoot();
}
bool HTMLImageElement::isOpaque() const {
Image* image = const_cast<HTMLImageElement*>(this)->imageContents();
return image && image->currentFrameKnownToBeOpaque();
}
int HTMLImageElement::sourceWidth() {
SourceImageStatus status;
FloatSize defaultObjectSize(width(), height());
RefPtr<Image> image = getSourceImageForCanvas(
&status, PreferNoAcceleration, SnapshotReasonCopyToWebGLTexture,
defaultObjectSize);
return image->width();
}
int HTMLImageElement::sourceHeight() {
SourceImageStatus status;
FloatSize defaultObjectSize(width(), height());
RefPtr<Image> image = getSourceImageForCanvas(
&status, PreferNoAcceleration, SnapshotReasonCopyToWebGLTexture,
defaultObjectSize);
return image->height();
}
IntSize HTMLImageElement::bitmapSourceSize() const {
ImageResource* image = cachedImage();
if (!image)
return IntSize();
LayoutSize lSize = image->imageSize(
LayoutObject::shouldRespectImageOrientation(layoutObject()), 1.0f);
DCHECK(lSize.fraction().isZero());
return IntSize(lSize.width().toInt(), lSize.height().toInt());
}
} // namespace blink