-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement HTMLImageElement decode #31269
base: main
Are you sure you want to change the base?
Changes from all commits
ba35908
dba5dad
781fda5
5daf5c5
ea11e15
445f368
1782dea
46b054a
d15fbb5
aee6e84
aae04c6
c117c01
3bf6a5b
d6b7b70
1526af1
ac82081
fb5b00a
35aacbe
eee6096
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
|
@@ -1159,6 +1172,39 @@ 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>) { | ||
self.image_decode_promises | ||
.borrow_mut() | ||
.push(promise.clone()); | ||
|
||
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) | ||
{ | ||
self.reject_image_decode_promises(); | ||
} | ||
} | ||
|
||
fn resolve_image_decode_promises(&self) { | ||
for promise in self.image_decode_promises.borrow().iter() { | ||
Taym95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
promise.resolve_native(&()); | ||
} | ||
self.image_decode_promises.borrow_mut().clear(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it not be better to just do a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use
|
||
} | ||
|
||
fn reject_image_decode_promises(&self) { | ||
let document = document_from_node(self); | ||
for promise in self.image_decode_promises.borrow().iter() { | ||
Taym95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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, | ||
|
@@ -1247,6 +1293,7 @@ impl HTMLImageElement { | |
generation: Default::default(), | ||
source_set: DomRefCell::new(SourceSet::new()), | ||
last_selected_source: DomRefCell::new(None), | ||
image_decode_promises: DomRefCell::new(vec![]), | ||
} | ||
} | ||
|
||
|
@@ -1289,6 +1336,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>>> { | ||
|
@@ -1345,6 +1396,11 @@ pub enum ImageElementMicrotask { | |
elem: DomRoot<HTMLImageElement>, | ||
generation: u32, | ||
}, | ||
DecodeTask { | ||
elem: DomRoot<HTMLImageElement>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One last thing I thought about: there is currently a race condition between when the microtask runs, and the "in parallel" stuff described at 2.2. For example if the request becomes broken, we will reject all promises, including the one for which the micro task hasn't run yet. I'm not sure this is actually a problem, but we may as well be consitent with the spec and:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved storing promise in task and tests are okey |
||
#[ignore_malloc_size_of = "promises are hard"] | ||
promise: Rc<Promise>, | ||
}, | ||
} | ||
|
||
impl MicrotaskRunnable for ImageElementMicrotask { | ||
|
@@ -1366,13 +1422,20 @@ impl MicrotaskRunnable for ImageElementMicrotask { | |
} => { | ||
elem.react_to_environment_changes_sync_steps(*generation); | ||
}, | ||
ImageElementMicrotask::DecodeTask { | ||
Taym95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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), | ||
} | ||
} | ||
} | ||
|
@@ -1605,6 +1668,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"); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,2 @@ | ||
[image-decode-with-quick-attach.html] | ||
[HTMLImageElement.prototype.decode(), attach to DOM before promise resolves.] | ||
expected: FAIL | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here, as per the comment below, let's only reject the promise associated with the task, and remove the resolve logic below. Instead, if the promise is not rejected, we should store it on
pending_
, and resolve/reject it later as the request state changes.I'm going to run the wpt tests before this change, and after, just to see if this makes a difference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the resolve logic but it broke lot of tests.