Implement HTMLImageElement decode (#31269)

* Implement HTMLImageElement decode

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Fix Decode doc link

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Temp

* Decode HTML Image

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Fix doc link

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Move image decode to process_image_response_for_environment_change

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Update some wpt test result

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Handle multiple image decode promises

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Remove unnecessary promise calls

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Update more wpt test result

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Undo body-size-cross-origin.https.html.ini changes

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Reject decode when src and srcset are missing

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Revert "Reject decode when src and srcset are missing"

This reverts commit 1b57ab978f9fc24facafc8af97ee8851d5142533.

* Drain promises vec and run update_the_image_data when element is created

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* resolve decode promise in abort_request when request is CompletelyAvailable

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Update wpt test

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Move storing promise in decode task

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Remove the resolve logic from decode task

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Revert "Remove the resolve logic from decode task"

This reverts commit eee6096d50.

* reject or reject current promise before storing it

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

* Add comment to explain why resolve promise when state is CompletelyAvailable

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>

---------

Signed-off-by: Bentaimia Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
Taym Haddadi 2024-06-13 12:15:49 +02:00 committed by GitHub
parent 43df0a48ee
commit 1d048f4f6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 97 additions and 173 deletions

View file

@ -41,6 +41,7 @@ pub enum DOMErrorName {
TimeoutError = DOMExceptionConstants::TIMEOUT_ERR,
InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR,
DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR,
EncodingError,
NotReadableError,
OperationError,
}
@ -70,6 +71,7 @@ impl DOMErrorName {
"TimeoutError" => Some(DOMErrorName::TimeoutError),
"InvalidNodeTypeError" => Some(DOMErrorName::InvalidNodeTypeError),
"DataCloneError" => Some(DOMErrorName::DataCloneError),
"EncodingError" => Some(DOMErrorName::EncodingError),
"NotReadableError" => Some(DOMErrorName::NotReadableError),
"OperationError" => Some(DOMErrorName::OperationError),
_ => None,
@ -115,6 +117,9 @@ impl DOMException {
"The supplied node is incorrect or has an incorrect ancestor for this operation."
},
DOMErrorName::DataCloneError => "The object can not be cloned.",
DOMErrorName::EncodingError => {
"The encoding operation (either encoded or decoding) failed."
},
DOMErrorName::NotReadableError => "The I/O read operation failed.",
DOMErrorName::OperationError => {
"The operation failed for an operation-specific reason."

View file

@ -5,6 +5,7 @@
use std::cell::Cell;
use std::collections::HashSet;
use std::default::Default;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{char, i32, mem};
@ -47,6 +48,8 @@ use style::values::specified::AbsoluteLength;
use style_traits::ParsingMode;
use url::Url;
use super::domexception::DOMErrorName;
use super::types::DOMException;
use crate::document_loader::{LoadBlocker, LoadType};
use crate::dom::activation::Activatable;
use crate::dom::attr::Attr;
@ -84,6 +87,7 @@ use crate::dom::node::{
UnbindContext,
};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::promise::Promise;
use crate::dom::values::UNSIGNED_LONG_MAX;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
@ -169,6 +173,8 @@ pub struct HTMLImageElement {
#[ignore_malloc_size_of = "SourceSet"]
source_set: DomRefCell<SourceSet>,
last_selected_source: DomRefCell<Option<USVString>>,
#[ignore_malloc_size_of = "promises are hard"]
image_decode_promises: DomRefCell<Vec<Rc<Promise>>>,
}
impl HTMLImageElement {
@ -424,6 +430,7 @@ impl HTMLImageElement {
LoadBlocker::terminate(&mut self.current_request.borrow_mut().blocker);
// Mark the node dirty
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
self.resolve_image_decode_promises();
}
/// Step 24 of <https://html.spec.whatwg.org/multipage/#update-the-image-data>
@ -525,6 +532,12 @@ impl HTMLImageElement {
request.state = state;
request.image = None;
request.metadata = None;
if matches!(state, State::Broken) {
self.reject_image_decode_promises();
} else if matches!(state, State::CompletelyAvailable) {
self.resolve_image_decode_promises();
}
}
/// <https://html.spec.whatwg.org/multipage/#update-the-source-set>
@ -1161,6 +1174,48 @@ impl HTMLImageElement {
}
}
// Step 2 for <https://html.spec.whatwg.org/multipage/#dom-img-decode>
fn react_to_decode_image_sync_steps(&self, promise: Rc<Promise>) {
let document = document_from_node(self);
// Step 2.1 of <https://html.spec.whatwg.org/multipage/#dom-img-decode>
if !document.is_fully_active() ||
matches!(self.current_request.borrow().state, State::Broken)
{
promise.reject_native(&DOMException::new(
&document.global(),
DOMErrorName::EncodingError,
));
} else if matches!(
self.current_request.borrow().state,
State::CompletelyAvailable
) {
// this doesn't follow the spec, but it's been discussed in <https://github.com/whatwg/html/issues/4217>
promise.resolve_native(&());
} else {
self.image_decode_promises
.borrow_mut()
.push(promise.clone());
}
}
fn resolve_image_decode_promises(&self) {
for promise in self.image_decode_promises.borrow().iter() {
promise.resolve_native(&());
}
self.image_decode_promises.borrow_mut().clear();
}
fn reject_image_decode_promises(&self) {
let document = document_from_node(self);
for promise in self.image_decode_promises.borrow().iter() {
promise.reject_native(&DOMException::new(
&document.global(),
DOMErrorName::EncodingError,
));
}
self.image_decode_promises.borrow_mut().clear();
}
/// Step 15 for <https://html.spec.whatwg.org/multipage/#img-environment-changes>
fn finish_reacting_to_environment_change(
&self,
@ -1249,6 +1304,7 @@ impl HTMLImageElement {
generation: Default::default(),
source_set: DomRefCell::new(SourceSet::new()),
last_selected_source: DomRefCell::new(None),
image_decode_promises: DomRefCell::new(vec![]),
}
}
@ -1291,6 +1347,10 @@ impl HTMLImageElement {
image.SetHeight(h);
}
// run update_the_image_data when the element is created.
// https://html.spec.whatwg.org/multipage/#when-to-obtain-images
image.update_the_image_data();
Ok(image)
}
pub fn areas(&self) -> Option<Vec<DomRoot<HTMLAreaElement>>> {
@ -1347,6 +1407,11 @@ pub enum ImageElementMicrotask {
elem: DomRoot<HTMLImageElement>,
generation: u32,
},
DecodeTask {
elem: DomRoot<HTMLImageElement>,
#[ignore_malloc_size_of = "promises are hard"]
promise: Rc<Promise>,
},
}
impl MicrotaskRunnable for ImageElementMicrotask {
@ -1368,13 +1433,20 @@ impl MicrotaskRunnable for ImageElementMicrotask {
} => {
elem.react_to_environment_changes_sync_steps(*generation);
},
ImageElementMicrotask::DecodeTask {
ref elem,
ref promise,
} => {
elem.react_to_decode_image_sync_steps(promise.clone());
},
}
}
fn enter_realm(&self) -> JSAutoRealm {
match self {
&ImageElementMicrotask::StableStateUpdateImageDataTask { ref elem, .. } |
&ImageElementMicrotask::EnvironmentChangesTask { ref elem, .. } => enter_realm(&**elem),
&ImageElementMicrotask::EnvironmentChangesTask { ref elem, .. } |
&ImageElementMicrotask::DecodeTask { ref elem, .. } => enter_realm(&**elem),
}
}
}
@ -1607,6 +1679,22 @@ impl HTMLImageElementMethods for HTMLImageElement {
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-img-decode>
fn Decode(&self) -> Rc<Promise> {
// Step 1
let promise = Promise::new(&self.global());
// Step 2
let task = ImageElementMicrotask::DecodeTask {
elem: DomRoot::from_ref(self),
promise: promise.clone(),
};
ScriptThread::await_stable_state(Microtask::ImageElement(task));
// Step 3
promise
}
// https://html.spec.whatwg.org/multipage/#dom-img-name
make_getter!(Name, "name");

View file

@ -29,6 +29,9 @@ interface HTMLImageElement : HTMLElement {
readonly attribute USVString currentSrc;
[CEReactions]
attribute DOMString referrerPolicy;
Promise<undefined> decode();
// also has obsolete members
};

View file

@ -3558,9 +3558,6 @@
[HTMLFrameElement interface: attribute frameBorder]
expected: FAIL
[HTMLImageElement interface: new Image() must inherit property "decode()" with the proper type]
expected: FAIL
[HTMLTableColElement interface: document.createElement("col") must inherit property "width" with the proper type]
expected: FAIL
@ -4248,9 +4245,6 @@
[HTMLInputElement interface: createInput("hidden") must inherit property "autocomplete" with the proper type]
expected: FAIL
[HTMLImageElement interface: document.createElement("img") must inherit property "decode()" with the proper type]
expected: FAIL
[HTMLAreaElement interface: document.createElement("area") must inherit property "username" with the proper type]
expected: FAIL

View file

@ -1,5 +1,2 @@
[image-decode-image-document.html]
expected: ERROR
[HTMLImageElement.prototype.decode(), image document tests. Decode from iframe with image document, succeeds (img not loaded)]
expected: TIMEOUT

View file

@ -1,26 +1,11 @@
[image-decode-path-changes.html]
type: testharness
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following good png decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following good svg decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following bad decode fails.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes to the same path succeed.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode; following good decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode; following bad decode fails.]
expected: FAIL

View file

@ -1,26 +1,5 @@
[image-decode-picture.html]
type: testharness
[HTMLImageElement.prototype.decode(), picture tests. Image with PNG source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with multiple sources decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with PNG data URL source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with SVG source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Non-existent source fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Corrupt image in src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image without srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Multiple decodes for images with src succeed.]
expected: FAIL

View file

@ -1,4 +1,2 @@
[image-decode-with-quick-attach.html]
[HTMLImageElement.prototype.decode(), attach to DOM before promise resolves.]
expected: FAIL

View file

@ -1,44 +1,8 @@
[image-decode.html]
type: testharness
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG data URL src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with SVG src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Non-existent src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Inactive document fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Adopted active image into inactive document fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Adopted inactive image into active document succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Corrupt image in src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image without src/srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Multiple decodes for images with src succeed.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG srcset decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with SVG srcset decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Non-existent srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Multiple decodes for images with srcset succeed.]
expected: FAIL

View file

@ -5,9 +5,6 @@
[Retrieving a same-origin resource with Timing-Allow-Origin should expose body size]
expected: FAIL
[Retrieving a no-cors resource without Timing-Allow-Origin should not expose body size]
expected: FAIL
[Retrieving a no-cors resource with Timing-Allow-Origin should not expose body size]
expected: FAIL

View file

@ -3381,9 +3381,6 @@
[HTMLFrameElement interface: attribute frameBorder]
expected: FAIL
[HTMLImageElement interface: new Image() must inherit property "decode()" with the proper type]
expected: FAIL
[HTMLTableColElement interface: document.createElement("col") must inherit property "width" with the proper type]
expected: FAIL
@ -4044,9 +4041,6 @@
[HTMLTableColElement interface: document.createElement("colgroup") must inherit property "chOff" with the proper type]
expected: FAIL
[HTMLImageElement interface: document.createElement("img") must inherit property "decode()" with the proper type]
expected: FAIL
[HTMLAreaElement interface: document.createElement("area") must inherit property "username" with the proper type]
expected: FAIL

View file

@ -1,4 +1 @@
[image-decode-image-document.html]
expected: ERROR
[HTMLImageElement.prototype.decode(), image document tests. Decode from iframe with image document, succeeds (img not loaded)]
expected: TIMEOUT

View file

@ -1,24 +1,9 @@
[image-decode-path-changes.html]
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following good png decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following good svg decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes fail decode; following bad decode fails.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. src changes to the same path succeed.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode; following good decode succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), src/srcset mutation tests. srcset changes fail decode; following bad decode fails.]
expected: FAIL

View file

@ -1,24 +1,3 @@
[image-decode-picture.html]
[HTMLImageElement.prototype.decode(), picture tests. Image with PNG source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with multiple sources decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with PNG data URL source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image with SVG source decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Non-existent source fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Corrupt image in src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Image without srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), picture tests. Multiple decodes for images with src succeed.]
expected: FAIL

View file

@ -1,3 +1 @@
[image-decode-with-quick-attach.html]
[HTMLImageElement.prototype.decode(), attach to DOM before promise resolves.]
expected: FAIL

View file

@ -1,42 +1,6 @@
[image-decode.html]
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG data URL src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with SVG src decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Non-existent src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Inactive document fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Adopted active image into inactive document fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Adopted inactive image into active document succeeds.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Corrupt image in src fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image without src/srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Multiple decodes for images with src succeed.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with PNG srcset decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Image with SVG srcset decodes with undefined.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Non-existent srcset fails decode.]
expected: FAIL
[HTMLImageElement.prototype.decode(), basic tests. Multiple decodes for images with srcset succeed.]
expected: FAIL

View file

@ -5,9 +5,6 @@
[Retrieving a same-origin resource with Timing-Allow-Origin should expose body size]
expected: FAIL
[Retrieving a no-cors resource without Timing-Allow-Origin should not expose body size]
expected: FAIL
[Retrieving a no-cors resource with Timing-Allow-Origin should not expose body size]
expected: FAIL