| Index: third_party/WebKit/Source/core/html/HTMLImageElement.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
|
| index b140a0b375475872a13fcf3f450d5b7be669162d..ffb879084741a6190fab5663a478e20a7f741082 100644
|
| --- a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
|
| +++ b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
|
| @@ -31,6 +31,7 @@
|
| #include "core/css/MediaValuesDynamic.h"
|
| #include "core/css/parser/SizesAttributeParser.h"
|
| #include "core/dom/Attribute.h"
|
| +#include "core/dom/DOMException.h"
|
| #include "core/dom/NodeTraversal.h"
|
| #include "core/dom/shadow/ShadowRoot.h"
|
| #include "core/frame/Deprecation.h"
|
| @@ -51,6 +52,7 @@
|
| #include "core/layout/LayoutImage.h"
|
| #include "core/layout/api/LayoutImageItem.h"
|
| #include "core/loader/resource/ImageResourceContent.h"
|
| +#include "core/page/ChromeClient.h"
|
| #include "core/page/Page.h"
|
| #include "core/style/ContentData.h"
|
| #include "core/svg/graphics/SVGImageForContainer.h"
|
| @@ -95,7 +97,8 @@ HTMLImageElement::HTMLImageElement(Document& document, bool createdByParser)
|
| m_formWasSetByParser(false),
|
| m_elementCreatedByParser(createdByParser),
|
| m_isFallbackImage(false),
|
| - m_referrerPolicy(ReferrerPolicyDefault) {
|
| + m_referrerPolicy(ReferrerPolicyDefault),
|
| + m_weakPtrFactory(this) {
|
| setHasCustomStyleCallbacks();
|
| }
|
|
|
| @@ -108,13 +111,16 @@ HTMLImageElement* HTMLImageElement::create(Document& document,
|
| return new HTMLImageElement(document, createdByParser);
|
| }
|
|
|
| -HTMLImageElement::~HTMLImageElement() {}
|
| +HTMLImageElement::~HTMLImageElement() {
|
| + m_weakPtrFactory.revokeAll();
|
| +}
|
|
|
| DEFINE_TRACE(HTMLImageElement) {
|
| visitor->trace(m_imageLoader);
|
| visitor->trace(m_listener);
|
| visitor->trace(m_form);
|
| visitor->trace(m_source);
|
| + visitor->trace(m_decodePromiseResolvers);
|
| HTMLElement::trace(visitor);
|
| }
|
|
|
| @@ -125,6 +131,38 @@ void HTMLImageElement::notifyViewportChanged() {
|
| selectSourceURL(ImageLoader::UpdateSizeChanged);
|
| }
|
|
|
| +void HTMLImageElement::requestDecode() {
|
| + DCHECK(!m_decodePromiseResolvers.isEmpty());
|
| + if (!imageLoader().image() || !imageLoader().image()->getImage()) {
|
| + didDecode(false);
|
| + return;
|
| + }
|
| + Image* image = imageLoader().image()->getImage();
|
| + auto* page = document().page();
|
| + if (page) {
|
| + page->chromeClient().requestDecode(
|
| + image->imageForCurrentFrame(),
|
| + WTF::bind(&HTMLImageElement::didDecode,
|
| + m_weakPtrFactory.createWeakPtr()));
|
| + }
|
| +}
|
| +
|
| +void HTMLImageElement::didDecode(bool success) {
|
| + if (m_decodePromiseResolvers.isEmpty())
|
| + return;
|
| + static auto resolve = [](ScriptPromiseResolver* resolver) {
|
| + resolver->resolve();
|
| + };
|
| + static auto reject = [](ScriptPromiseResolver* resolver) {
|
| + resolver->reject(DOMException::create(
|
| + EncodingError, "The source image cannot be decoded"));
|
| + };
|
| + auto process = success ? resolve : reject;
|
| + for (auto& resolver : m_decodePromiseResolvers)
|
| + process(resolver);
|
| + m_decodePromiseResolvers.clear();
|
| +}
|
| +
|
| HTMLImageElement* HTMLImageElement::createForJSConstructor(Document& document) {
|
| HTMLImageElement* image = new HTMLImageElement(document);
|
| image->m_elementCreatedByParser = false;
|
| @@ -260,6 +298,12 @@ void HTMLImageElement::parseAttribute(
|
| }
|
| } else if (name == srcAttr || name == srcsetAttr || name == sizesAttr) {
|
| selectSourceURL(ImageLoader::UpdateIgnorePreviousError);
|
| + // Ensure to fail any pending decodes on possible source changes.
|
| + if (!m_decodePromiseResolvers.isEmpty() &&
|
| + params.oldValue != params.newValue) {
|
| + didDecode(false);
|
| + m_weakPtrFactory.revokeAll();
|
| + }
|
| } else if (name == usemapAttr) {
|
| setIsLink(!params.newValue.isNull());
|
| } else if (name == referrerpolicyAttr) {
|
| @@ -415,9 +459,8 @@ Node::InsertionNotificationRequest HTMLImageElement::insertedInto(
|
| }
|
|
|
| void HTMLImageElement::removedFrom(ContainerNode* insertionPoint) {
|
| - if (!m_form ||
|
| - NodeTraversal::highestAncestorOrSelf(*m_form.get()) !=
|
| - NodeTraversal::highestAncestorOrSelf(*this))
|
| + if (!m_form || NodeTraversal::highestAncestorOrSelf(*m_form.get()) !=
|
| + NodeTraversal::highestAncestorOrSelf(*this))
|
| resetFormOwner();
|
| if (m_listener) {
|
| document().mediaQueryMatcher().removeViewportListener(m_listener);
|
| @@ -524,9 +567,9 @@ const String& HTMLImageElement::currentSrc() const {
|
| // 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.
|
| + // 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;
|
| @@ -592,6 +635,17 @@ int HTMLImageElement::y() const {
|
| return absPos.y();
|
| }
|
|
|
| +ScriptPromise HTMLImageElement::decode(ScriptState* scriptState,
|
| + ExceptionState& exceptionState) {
|
| + exceptionState.clearException();
|
| + m_decodePromiseResolvers.push_back(
|
| + ScriptPromiseResolver::create(scriptState));
|
| + ScriptPromise promise = m_decodePromiseResolvers.back()->promise();
|
| + if (complete())
|
| + requestDecode();
|
| + return promise;
|
| +}
|
| +
|
| bool HTMLImageElement::complete() const {
|
| return imageLoader().imageComplete();
|
| }
|
| @@ -608,8 +662,8 @@ bool HTMLImageElement::isServerMap() const {
|
|
|
| const AtomicString& usemap = fastGetAttribute(usemapAttr);
|
|
|
| - // If the usemap attribute starts with '#', it refers to a map element in the
|
| - // document.
|
| + // If the usemap attribute starts with '#', it refers to a map element in
|
| + // the document.
|
| if (usemap[0] == '#')
|
| return false;
|
|
|
| @@ -860,8 +914,8 @@ void HTMLImageElement::setLayoutDisposition(LayoutDisposition layoutDisposition,
|
|
|
| m_layoutDisposition = layoutDisposition;
|
|
|
| - // This can happen inside of attachLayoutTree() in the middle of a recalcStyle
|
| - // so we need to reattach synchronously here.
|
| + // This can happen inside of attachLayoutTree() in the middle of a
|
| + // recalcStyle so we need to reattach synchronously here.
|
| if (document().inStyleRecalc()) {
|
| reattachLayoutTree();
|
| } else {
|
| @@ -887,6 +941,18 @@ PassRefPtr<ComputedStyle> HTMLImageElement::customStyleForLayoutObject() {
|
| }
|
| }
|
|
|
| +void HTMLImageElement::defaultEventHandler(Event* event) {
|
| + HTMLElement::defaultEventHandler(event);
|
| + if (m_decodePromiseResolvers.isEmpty())
|
| + return;
|
| +
|
| + if (event->type() == EventTypeNames::load) {
|
| + requestDecode();
|
| + } else if (event->type() == EventTypeNames::error) {
|
| + didDecode(false);
|
| + }
|
| +}
|
| +
|
| bool HTMLImageElement::isOpaque() const {
|
| Image* image = const_cast<HTMLImageElement*>(this)->imageContents();
|
| return image && image->currentFrameKnownToBeOpaque();
|
|
|