-
-
Notifications
You must be signed in to change notification settings - Fork 971
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
LibWeb/HTML: Resolve image decoding promises inside tasks #2229
base: master
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -291,6 +291,9 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> HTMLImageElement::decode( | |
|
||
// 2. Queue a microtask to perform the following steps: | ||
queue_a_microtask(&document(), JS::create_heap_function(realm.heap(), [this, promise, &realm]() mutable { | ||
// 1. Let global be this's relevant global object. | ||
auto& global = relevant_global_object(*this); | ||
|
||
auto reject_if_document_not_fully_active = [this, promise, &realm]() -> bool { | ||
if (this->document().is_fully_active()) | ||
return false; | ||
|
@@ -311,55 +314,97 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> HTMLImageElement::decode( | |
return true; | ||
}; | ||
|
||
// 2.1 If any of the following are true: | ||
// 2.1.1 this's node document is not fully active; | ||
// 2.1.1 then reject promise with an "EncodingError" DOMException. | ||
if (reject_if_document_not_fully_active()) | ||
return; | ||
|
||
// 2.1.2 or this's current request's state is broken, | ||
// 2.1.2 then reject promise with an "EncodingError" DOMException. | ||
if (reject_if_current_request_state_broken()) | ||
// 2. If any of the following are true: | ||
// - this's node document is not fully active; | ||
// - or this's current request's state is broken, | ||
// then reject promise with an "EncodingError" DOMException. | ||
if (reject_if_document_not_fully_active() || reject_if_current_request_state_broken()) { | ||
return; | ||
} | ||
|
||
// 2.2 Otherwise, in parallel wait for one of the following cases to occur, and perform the corresponding actions: | ||
Platform::EventLoopPlugin::the().deferred_invoke(JS::create_heap_function(heap(), [this, promise, &realm, reject_if_document_not_fully_active, reject_if_current_request_state_broken] { | ||
// 3. Otherwise, in parallel wait for one of the following cases to occur, and perform the corresponding actions: | ||
Platform::EventLoopPlugin::the().deferred_invoke(JS::create_heap_function(heap(), [this, promise, &realm, &global] { | ||
enum class ImageState { | ||
DocumentNoLongerFullyActive, | ||
CurrentRequestChanged, | ||
CurrentRequestBroken, | ||
CurrentRequestCompletelyAvailable, | ||
}; | ||
ImageState image_state; | ||
auto previous_state = current_request().state(); | ||
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. This double event loop spin has always been truly puzzling. ... why do we need it? |
||
Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(heap(), [&] { | ||
auto state = this->current_request().state(); | ||
|
||
return !this->document().is_fully_active() || state == ImageRequest::State::Broken || state == ImageRequest::State::CompletelyAvailable; | ||
})); | ||
if (state != previous_state) { | ||
image_state = ImageState::CurrentRequestChanged; | ||
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. does the state of the current request changing really mean that the current request changed? What about ABA? Should we be storing the "Current request" and making sure the image element still has the same request instead? 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. "or is mutated" 🤔 How do we detect that? |
||
return true; | ||
} | ||
previous_state = state; | ||
|
||
// 2.2.1 This img element's node document stops being fully active | ||
// 2.2.1 Reject promise with an "EncodingError" DOMException. | ||
if (reject_if_document_not_fully_active()) | ||
return; | ||
if (!document().is_fully_active()) { | ||
image_state = ImageState::DocumentNoLongerFullyActive; | ||
return true; | ||
} | ||
|
||
// FIXME: 2.2.2 This img element's current request changes or is mutated | ||
// FIXME: 2.2.2 Reject promise with an "EncodingError" DOMException. | ||
if (state == ImageRequest::State::Broken) { | ||
image_state = ImageState::CurrentRequestBroken; | ||
return true; | ||
} | ||
|
||
// 2.2.3 This img element's current request's state becomes broken | ||
// 2.2.3 Reject promise with an "EncodingError" DOMException. | ||
if (reject_if_current_request_state_broken()) | ||
if (state == ImageRequest::State::CompletelyAvailable) { | ||
image_state = ImageState::CurrentRequestCompletelyAvailable; | ||
return true; | ||
} | ||
|
||
return false; | ||
})); | ||
|
||
switch (image_state) { | ||
// -> This img element's node document stops being fully active | ||
// -> This img element's current request changes or is mutated | ||
// -> This img element's current request's state becomes broken | ||
case ImageState::DocumentNoLongerFullyActive: | ||
case ImageState::CurrentRequestChanged: | ||
case ImageState::CurrentRequestBroken: { | ||
// Queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException. | ||
queue_global_task(Task::Source::DOMManipulation, global, JS::create_heap_function(realm.heap(), [&realm, &promise, image_state] { | ||
auto message = [image_state]() -> String { | ||
switch (image_state) { | ||
case ImageState::DocumentNoLongerFullyActive: | ||
return "Node document not fully active"_string; | ||
case ImageState::CurrentRequestChanged: | ||
return "Current request state changed"_string; | ||
case ImageState::CurrentRequestBroken: | ||
return "Current request state is broken"_string; | ||
default: | ||
VERIFY_NOT_REACHED(); | ||
} | ||
}(); | ||
auto exception = WebIDL::EncodingError::create(realm, message); | ||
HTML::TemporaryExecutionContext context(realm); | ||
WebIDL::reject_promise(realm, promise, exception); | ||
})); | ||
return; | ||
} | ||
|
||
// -> This img element's current request's state becomes completely available | ||
case ImageState::CurrentRequestCompletelyAvailable: { | ||
// FIXME: Decode the image. | ||
// FIXME: If decoding does not need to be performed for this image (for example because it is a vector graphic) or the decoding process completes successfully, then queue a global task on the DOM manipulation task source with global to resolve promise with undefined. | ||
// FIXME: If decoding fails (for example due to invalid image data), then queue a global task on the DOM manipulation task source with global to reject promise with an "EncodingError" DOMException. | ||
|
||
// 2.2.4 This img element's current request's state becomes completely available | ||
if (this->current_request().state() == ImageRequest::State::CompletelyAvailable) { | ||
// 2.2.4.1 FIXME: Decode the image. | ||
// 2.2.4.2 FIXME: If decoding does not need to be performed for this image (for example because it is a vector graphic), resolve promise with undefined. | ||
// 2.2.4.3 FIXME: If decoding fails (for example due to invalid image data), reject promise with an "EncodingError" DOMException. | ||
// 2.2.4.4 FIXME: If the decoding process completes successfully, resolve promise with undefined. | ||
// 2.2.4.5 FIXME: User agents should ensure that the decoded media data stays readily available until at least the end of the next successful update | ||
// the rendering step in the event loop. This is an important part of the API contract, and should not be broken if at all possible. | ||
// (Typically, this would only be violated in low-memory situations that require evicting decoded image data, or when the image is too large | ||
// to keep in decoded form for this period of time.) | ||
|
||
HTML::TemporaryExecutionContext context(realm); | ||
WebIDL::resolve_promise(realm, promise, JS::js_undefined()); | ||
// NOTE: For now we just resolve it. | ||
queue_global_task(Task::Source::DOMManipulation, global, JS::create_heap_function(realm.heap(), [&realm, &promise] { | ||
HTML::TemporaryExecutionContext context(realm); | ||
WebIDL::resolve_promise(realm, promise, JS::js_undefined()); | ||
})); | ||
break; | ||
} | ||
} | ||
})); | ||
})); | ||
|
||
// 3. Return promise. | ||
return promise; | ||
} | ||
|
||
|
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.
:/ not initialized at declaration.