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 6ad492963e8641a36d0b0cc8211d58aa936076a0..c317ebb48308ff454edefee3eace24cd98a81255 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" |
@@ -92,6 +94,7 @@ HTMLImageElement::HTMLImageElement(Document& document, bool created_by_parser) |
image_device_pixel_ratio_(1.0f), |
source_(nullptr), |
layout_disposition_(LayoutDisposition::kPrimaryContent), |
+ decode_sequence_id_(0), |
form_was_set_by_parser_(false), |
element_created_by_parser_(created_by_parser), |
is_fallback_image_(false), |
@@ -115,6 +118,7 @@ DEFINE_TRACE(HTMLImageElement) { |
visitor->Trace(listener_); |
visitor->Trace(form_); |
visitor->Trace(source_); |
+ visitor->Trace(decode_promise_resolvers_); |
HTMLElement::Trace(visitor); |
} |
@@ -125,6 +129,45 @@ void HTMLImageElement::NotifyViewportChanged() { |
SelectSourceURL(ImageLoader::kUpdateSizeChanged); |
} |
+void HTMLImageElement::RequestDecode() { |
+ DCHECK(!decode_promise_resolvers_.IsEmpty()); |
+ LocalFrame* frame = GetDocument().GetFrame(); |
+ // If we don't have the image, or the document doesn't have a frame, then |
+ // reject the decode, since we can't plumb the request to the correct place. |
+ if (!GetImageLoader().GetImage() || |
+ !GetImageLoader().GetImage()->GetImage() || !frame) { |
+ DecodeRequestFinished(decode_sequence_id_, false); |
+ return; |
+ } |
+ Image* image = GetImageLoader().GetImage()->GetImage(); |
+ frame->GetChromeClient().RequestDecode( |
+ frame, image->ImageForCurrentFrame(), |
+ WTF::Bind(&HTMLImageElement::DecodeRequestFinished, |
+ WrapWeakPersistent(this), decode_sequence_id_)); |
+} |
+ |
+void HTMLImageElement::DecodeRequestFinished(uint32_t sequence_id, |
+ bool success) { |
+ // If the sequence id attached with this callback doesn't match our current |
+ // sequence id, then the source of the image has changed. In other words, the |
+ // decode resolved/rejected by this callback was already rejected. Since we |
+ // could have had a new decode request, we have to make sure not to |
+ // resolve/reject those using the stale callback. |
+ if (sequence_id != decode_sequence_id_) |
+ return; |
+ |
+ if (success) { |
+ for (auto& resolver : decode_promise_resolvers_) |
+ resolver->Resolve(); |
+ } else { |
+ for (auto& resolver : decode_promise_resolvers_) { |
+ resolver->Reject(DOMException::Create( |
+ kEncodingError, "The source image cannot be decoded")); |
+ } |
+ } |
+ decode_promise_resolvers_.clear(); |
+} |
+ |
HTMLImageElement* HTMLImageElement::CreateForJSConstructor(Document& document) { |
HTMLImageElement* image = new HTMLImageElement(document); |
image->element_created_by_parser_ = false; |
@@ -260,6 +303,14 @@ void HTMLImageElement::ParseAttribute( |
} |
} else if (name == srcAttr || name == srcsetAttr || name == sizesAttr) { |
SelectSourceURL(ImageLoader::kUpdateIgnorePreviousError); |
+ // Ensure to fail any pending decodes on possible source changes. |
+ if (!decode_promise_resolvers_.IsEmpty() && |
+ params.old_value != params.new_value) { |
+ DecodeRequestFinished(decode_sequence_id_, false); |
+ // Increment the sequence id so that any in flight decode completion tasks |
+ // will not trigger promise resolution for new decode requests. |
+ ++decode_sequence_id_; |
+ } |
} else if (name == usemapAttr) { |
SetIsLink(!params.new_value.IsNull()); |
} else if (name == referrerpolicyAttr) { |
@@ -597,6 +648,22 @@ int HTMLImageElement::y() const { |
return abs_pos.Y(); |
} |
+ScriptPromise HTMLImageElement::decode(ScriptState* script_state, |
+ ExceptionState& exception_state) { |
+ if (!script_state->ContextIsValid()) { |
+ exception_state.ThrowDOMException(kEncodingError, |
+ "The source image cannot be decoded"); |
+ return ScriptPromise(); |
+ } |
+ exception_state.ClearException(); |
+ decode_promise_resolvers_.push_back( |
+ ScriptPromiseResolver::Create(script_state)); |
+ ScriptPromise promise = decode_promise_resolvers_.back()->Promise(); |
+ if (complete()) |
+ RequestDecode(); |
+ return promise; |
+} |
+ |
bool HTMLImageElement::complete() const { |
return GetImageLoader().ImageComplete(); |
} |
@@ -613,8 +680,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; |
@@ -812,6 +879,15 @@ PassRefPtr<ComputedStyle> HTMLImageElement::CustomStyleForLayoutObject() { |
} |
} |
+void HTMLImageElement::ImageNotifyFinished(bool success) { |
+ if (decode_promise_resolvers_.IsEmpty()) |
+ return; |
+ if (success) |
+ RequestDecode(); |
+ else |
+ DecodeRequestFinished(decode_sequence_id_, false); |
+} |
+ |
void HTMLImageElement::AssociateWith(HTMLFormElement* form) { |
if (form && form->isConnected()) { |
form_ = form; |