1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 11:18:11 +00:00

LibWeb: Implement the HTMLMediaElement error attribute

This commit is contained in:
Timothy Flynn 2023-04-22 14:44:33 -04:00 committed by Andreas Kling
parent 73a80b7047
commit 9c940608fd
3 changed files with 39 additions and 19 deletions

View file

@ -20,6 +20,7 @@
#include <LibWeb/HTML/HTMLAudioElement.h> #include <LibWeb/HTML/HTMLAudioElement.h>
#include <LibWeb/HTML/HTMLMediaElement.h> #include <LibWeb/HTML/HTMLMediaElement.h>
#include <LibWeb/HTML/HTMLVideoElement.h> #include <LibWeb/HTML/HTMLVideoElement.h>
#include <LibWeb/HTML/MediaError.h>
#include <LibWeb/HTML/PotentialCORSRequest.h> #include <LibWeb/HTML/PotentialCORSRequest.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/TimeRanges.h> #include <LibWeb/HTML/TimeRanges.h>
@ -81,6 +82,7 @@ void HTMLMediaElement::queue_a_media_element_task(JS::SafeFunction<void()> steps
void HTMLMediaElement::visit_edges(Cell::Visitor& visitor) void HTMLMediaElement::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);
visitor.visit(m_error);
visitor.visit(m_fetch_controller); visitor.visit(m_fetch_controller);
visitor.visit(m_video_tracks); visitor.visit(m_video_tracks);
} }
@ -251,8 +253,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> HTMLMediaElement::play()
// FIXME: 1. If the media element is not allowed to play, then return a promise rejected with a "NotAllowedError" DOMException. // FIXME: 1. If the media element is not allowed to play, then return a promise rejected with a "NotAllowedError" DOMException.
// FIXME: 2. If the media element's error attribute is not null and its code is MEDIA_ERR_SRC_NOT_SUPPORTED, then return a promise // 2. If the media element's error attribute is not null and its code is MEDIA_ERR_SRC_NOT_SUPPORTED, then return a promise
// rejected with a "NotSupportedError" DOMException. // rejected with a "NotSupportedError" DOMException.
if (m_error && m_error->code() == MediaError::Code::SrcNotSupported) {
auto error = WebIDL::NotSupportedError::create(realm, m_error->message().to_deprecated_string());
auto promise = WebIDL::create_rejected_promise(realm, error);
return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise->promise()) };
}
// 3. Let promise be a new promise and append promise to the list of pending play promises. // 3. Let promise be a new promise and append promise to the list of pending play promises.
auto promise = WebIDL::create_promise(realm); auto promise = WebIDL::create_promise(realm);
@ -361,7 +369,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::load_element()
// FIXME: 7. Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. // FIXME: 7. Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
// 8. Set the error attribute to null and the can autoplay flag to true. // 8. Set the error attribute to null and the can autoplay flag to true.
// FIXME: Handle the error attribute. m_error = nullptr;
m_can_autoplay = true; m_can_autoplay = true;
// 9. Invoke the media element's resource selection algorithm. // 9. Invoke the media element's resource selection algorithm.
@ -444,14 +452,14 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
// -> If mode is attribute // -> If mode is attribute
case SelectMode::Attribute: { case SelectMode::Attribute: {
auto failed_with_attribute = [this]() { auto failed_with_attribute = [this](auto error_message) {
bool ran_media_element_task = false; bool ran_media_element_task = false;
// 6. Failed with attribute: Reaching this step indicates that the media resource failed to load or that the given URL could not be parsed. Take // 6. Failed with attribute: Reaching this step indicates that the media resource failed to load or that the given URL could not be parsed. Take
// pending play promises and queue a media element task given the media element to run the dedicated media source failure steps with the result. // pending play promises and queue a media element task given the media element to run the dedicated media source failure steps with the result.
queue_a_media_element_task([this, &ran_media_element_task]() { queue_a_media_element_task([this, &ran_media_element_task, error_message = move(error_message)]() mutable {
auto promises = take_pending_play_promises(); auto promises = take_pending_play_promises();
handle_media_source_failure(promises).release_value_but_fixme_should_propagate_errors(); handle_media_source_failure(promises, move(error_message)).release_value_but_fixme_should_propagate_errors();
ran_media_element_task = true; ran_media_element_task = true;
}); });
@ -463,7 +471,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
// 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below. // 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below.
auto source = attribute(HTML::AttributeNames::src); auto source = attribute(HTML::AttributeNames::src);
if (source.is_empty()) { if (source.is_empty()) {
failed_with_attribute(); failed_with_attribute(TRY_OR_THROW_OOM(vm, "The 'src' attribute is empty"_string));
return {}; return {};
} }
@ -484,7 +492,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
return {}; return {};
} }
failed_with_attribute(); failed_with_attribute(TRY_OR_THROW_OOM(vm, "Failed to parse 'src' attribute as a URL"_string));
// 8. Return. The element won't attempt to load another resource until this algorithm is triggered again. // 8. Return. The element won't attempt to load another resource until this algorithm is triggered again.
return {}; return {};
@ -553,7 +561,7 @@ enum class FetchMode {
}; };
// https://html.spec.whatwg.org/multipage/media.html#concept-media-load-resource // https://html.spec.whatwg.org/multipage/media.html#concept-media-load-resource
WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_record, Function<void()> failure_callback) WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_record, Function<void(String)> failure_callback)
{ {
auto& realm = this->realm(); auto& realm = this->realm();
auto& vm = realm.vm(); auto& vm = realm.vm();
@ -623,7 +631,8 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_re
// 4. If the result of verifying response given the current media resource and byteRange is false, then abort these steps. // 4. If the result of verifying response given the current media resource and byteRange is false, then abort these steps.
// NOTE: We do this step before creating the updateMedia task so that we can invoke the failure callback. // NOTE: We do this step before creating the updateMedia task so that we can invoke the failure callback.
if (!verify_response(response, byte_range)) { if (!verify_response(response, byte_range)) {
failure_callback(); auto error_message = response->network_error_message().value_or("Failed to fetch media resource"sv);
failure_callback(String::from_utf8(error_message).release_value_but_fixme_should_propagate_errors());
return; return;
} }
@ -718,7 +727,7 @@ bool HTMLMediaElement::verify_response(JS::NonnullGCPtr<Fetch::Infrastructure::R
} }
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list // https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list
WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()> failure_callback) WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void(String)> failure_callback)
{ {
auto& realm = this->realm(); auto& realm = this->realm();
auto& vm = realm.vm(); auto& vm = realm.vm();
@ -734,7 +743,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()>
m_fetch_controller->stop_fetch(); m_fetch_controller->stop_fetch();
// 2. Abort this subalgorithm, returning to the resource selection algorithm. // 2. Abort this subalgorithm, returning to the resource selection algorithm.
failure_callback(); failure_callback(TRY_OR_THROW_OOM(vm, String::from_utf8(playback_manager.error().description())));
return {}; return {};
} }
@ -866,11 +875,13 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()>
} }
// https://html.spec.whatwg.org/multipage/media.html#dedicated-media-source-failure-steps // https://html.spec.whatwg.org/multipage/media.html#dedicated-media-source-failure-steps
WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises) WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises, String error_message)
{ {
auto& vm = this->vm(); auto& realm = this->realm();
auto& vm = realm.vm();
// FIXME: 1. Set the error attribute to the result of creating a MediaError with MEDIA_ERR_SRC_NOT_SUPPORTED. // 1. Set the error attribute to the result of creating a MediaError with MEDIA_ERR_SRC_NOT_SUPPORTED.
m_error = TRY(vm.heap().allocate<MediaError>(realm, realm, MediaError::Code::SrcNotSupported, move(error_message)));
// 2. Forget the media element's media-resource-specific tracks. // 2. Forget the media element's media-resource-specific tracks.
forget_media_resource_specific_tracks(); forget_media_resource_specific_tracks();
@ -882,7 +893,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS:
set_show_poster(true); set_show_poster(true);
// 5. Fire an event named error at the media element. // 5. Fire an event named error at the media element.
dispatch_event(TRY(DOM::Event::create(realm(), HTML::EventNames::error))); dispatch_event(TRY(DOM::Event::create(realm, HTML::EventNames::error)));
// 6. Reject pending play promises with promises and a "NotSupportedError" DOMException. // 6. Reject pending play promises with promises and a "NotSupportedError" DOMException.
reject_pending_play_promises<WebIDL::NotSupportedError>(promises, TRY_OR_THROW_OOM(vm, "Media is not supported"_fly_string)); reject_pending_play_promises<WebIDL::NotSupportedError>(promises, TRY_OR_THROW_OOM(vm, "Media is not supported"_fly_string));

View file

@ -34,6 +34,8 @@ public:
void queue_a_media_element_task(JS::SafeFunction<void()> steps); void queue_a_media_element_task(JS::SafeFunction<void()> steps);
JS::GCPtr<MediaError> error() const { return m_error; }
String const& current_src() const { return m_current_src; } String const& current_src() const { return m_current_src; }
enum class NetworkState : u16 { enum class NetworkState : u16 {
@ -103,10 +105,10 @@ private:
WebIDL::ExceptionOr<void> load_element(); WebIDL::ExceptionOr<void> load_element();
WebIDL::ExceptionOr<void> select_resource(); WebIDL::ExceptionOr<void> select_resource();
WebIDL::ExceptionOr<void> fetch_resource(AK::URL const&, Function<void()> failure_callback); WebIDL::ExceptionOr<void> fetch_resource(AK::URL const&, Function<void(String)> failure_callback);
static bool verify_response(JS::NonnullGCPtr<Fetch::Infrastructure::Response>, ByteRange const&); static bool verify_response(JS::NonnullGCPtr<Fetch::Infrastructure::Response>, ByteRange const&);
WebIDL::ExceptionOr<void> process_media_data(Function<void()> failure_callback); WebIDL::ExceptionOr<void> process_media_data(Function<void(String)> failure_callback);
WebIDL::ExceptionOr<void> handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises); WebIDL::ExceptionOr<void> handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises, String error_message);
void forget_media_resource_specific_tracks(); void forget_media_resource_specific_tracks();
void set_ready_state(ReadyState); void set_ready_state(ReadyState);
@ -148,6 +150,9 @@ private:
// https://html.spec.whatwg.org/multipage/media.html#media-element-event-task-source // https://html.spec.whatwg.org/multipage/media.html#media-element-event-task-source
UniqueTaskSource m_media_element_event_task_source {}; UniqueTaskSource m_media_element_event_task_source {};
// https://html.spec.whatwg.org/multipage/media.html#dom-media-error
JS::GCPtr<MediaError> m_error;
// https://html.spec.whatwg.org/multipage/media.html#dom-media-crossorigin // https://html.spec.whatwg.org/multipage/media.html#dom-media-crossorigin
CORSSettingAttribute m_crossorigin { CORSSettingAttribute::NoCORS }; CORSSettingAttribute m_crossorigin { CORSSettingAttribute::NoCORS };

View file

@ -1,4 +1,5 @@
#import <HTML/HTMLElement.idl> #import <HTML/HTMLElement.idl>
#import <HTML/MediaError.idl>
#import <HTML/TimeRanges.idl> #import <HTML/TimeRanges.idl>
#import <HTML/VideoTrackList.idl> #import <HTML/VideoTrackList.idl>
@ -12,6 +13,9 @@ enum CanPlayTypeResult {
[Exposed=Window] [Exposed=Window]
interface HTMLMediaElement : HTMLElement { interface HTMLMediaElement : HTMLElement {
// error state
readonly attribute MediaError? error;
// network state // network state
[Reflect, CEReactions] attribute DOMString src; [Reflect, CEReactions] attribute DOMString src;
readonly attribute USVString currentSrc; readonly attribute USVString currentSrc;