Skip to content

Commit 9c94060

Browse files
trflynn89awesomekling
authored andcommitted
LibWeb: Implement the HTMLMediaElement error attribute
1 parent 73a80b7 commit 9c94060

File tree

3 files changed

+39
-19
lines changed

3 files changed

+39
-19
lines changed

Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <LibWeb/HTML/HTMLAudioElement.h>
2121
#include <LibWeb/HTML/HTMLMediaElement.h>
2222
#include <LibWeb/HTML/HTMLVideoElement.h>
23+
#include <LibWeb/HTML/MediaError.h>
2324
#include <LibWeb/HTML/PotentialCORSRequest.h>
2425
#include <LibWeb/HTML/Scripting/Environments.h>
2526
#include <LibWeb/HTML/TimeRanges.h>
@@ -81,6 +82,7 @@ void HTMLMediaElement::queue_a_media_element_task(JS::SafeFunction<void()> steps
8182
void HTMLMediaElement::visit_edges(Cell::Visitor& visitor)
8283
{
8384
Base::visit_edges(visitor);
85+
visitor.visit(m_error);
8486
visitor.visit(m_fetch_controller);
8587
visitor.visit(m_video_tracks);
8688
}
@@ -251,8 +253,14 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> HTMLMediaElement::play()
251253

252254
// FIXME: 1. If the media element is not allowed to play, then return a promise rejected with a "NotAllowedError" DOMException.
253255

254-
// 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
255-
// rejected with a "NotSupportedError" DOMException.
256+
// 2. If the media element's error attribute is not null and its code is MEDIA_ERR_SRC_NOT_SUPPORTED, then return a promise
257+
// rejected with a "NotSupportedError" DOMException.
258+
if (m_error && m_error->code() == MediaError::Code::SrcNotSupported) {
259+
auto error = WebIDL::NotSupportedError::create(realm, m_error->message().to_deprecated_string());
260+
auto promise = WebIDL::create_rejected_promise(realm, error);
261+
262+
return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise->promise()) };
263+
}
256264

257265
// 3. Let promise be a new promise and append promise to the list of pending play promises.
258266
auto promise = WebIDL::create_promise(realm);
@@ -361,7 +369,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::load_element()
361369
// FIXME: 7. Set the playbackRate attribute to the value of the defaultPlaybackRate attribute.
362370

363371
// 8. Set the error attribute to null and the can autoplay flag to true.
364-
// FIXME: Handle the error attribute.
372+
m_error = nullptr;
365373
m_can_autoplay = true;
366374

367375
// 9. Invoke the media element's resource selection algorithm.
@@ -444,14 +452,14 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
444452

445453
// -> If mode is attribute
446454
case SelectMode::Attribute: {
447-
auto failed_with_attribute = [this]() {
455+
auto failed_with_attribute = [this](auto error_message) {
448456
bool ran_media_element_task = false;
449457

450458
// 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
451459
// pending play promises and queue a media element task given the media element to run the dedicated media source failure steps with the result.
452-
queue_a_media_element_task([this, &ran_media_element_task]() {
460+
queue_a_media_element_task([this, &ran_media_element_task, error_message = move(error_message)]() mutable {
453461
auto promises = take_pending_play_promises();
454-
handle_media_source_failure(promises).release_value_but_fixme_should_propagate_errors();
462+
handle_media_source_failure(promises, move(error_message)).release_value_but_fixme_should_propagate_errors();
455463

456464
ran_media_element_task = true;
457465
});
@@ -463,7 +471,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
463471
// 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.
464472
auto source = attribute(HTML::AttributeNames::src);
465473
if (source.is_empty()) {
466-
failed_with_attribute();
474+
failed_with_attribute(TRY_OR_THROW_OOM(vm, "The 'src' attribute is empty"_string));
467475
return {};
468476
}
469477

@@ -484,7 +492,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
484492
return {};
485493
}
486494

487-
failed_with_attribute();
495+
failed_with_attribute(TRY_OR_THROW_OOM(vm, "Failed to parse 'src' attribute as a URL"_string));
488496

489497
// 8. Return. The element won't attempt to load another resource until this algorithm is triggered again.
490498
return {};
@@ -553,7 +561,7 @@ enum class FetchMode {
553561
};
554562

555563
// https://html.spec.whatwg.org/multipage/media.html#concept-media-load-resource
556-
WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_record, Function<void()> failure_callback)
564+
WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_record, Function<void(String)> failure_callback)
557565
{
558566
auto& realm = this->realm();
559567
auto& vm = realm.vm();
@@ -623,7 +631,8 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(AK::URL const& url_re
623631
// 4. If the result of verifying response given the current media resource and byteRange is false, then abort these steps.
624632
// NOTE: We do this step before creating the updateMedia task so that we can invoke the failure callback.
625633
if (!verify_response(response, byte_range)) {
626-
failure_callback();
634+
auto error_message = response->network_error_message().value_or("Failed to fetch media resource"sv);
635+
failure_callback(String::from_utf8(error_message).release_value_but_fixme_should_propagate_errors());
627636
return;
628637
}
629638

@@ -718,7 +727,7 @@ bool HTMLMediaElement::verify_response(JS::NonnullGCPtr<Fetch::Infrastructure::R
718727
}
719728

720729
// https://html.spec.whatwg.org/multipage/media.html#media-data-processing-steps-list
721-
WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()> failure_callback)
730+
WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void(String)> failure_callback)
722731
{
723732
auto& realm = this->realm();
724733
auto& vm = realm.vm();
@@ -734,7 +743,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()>
734743
m_fetch_controller->stop_fetch();
735744

736745
// 2. Abort this subalgorithm, returning to the resource selection algorithm.
737-
failure_callback();
746+
failure_callback(TRY_OR_THROW_OOM(vm, String::from_utf8(playback_manager.error().description())));
738747

739748
return {};
740749
}
@@ -866,11 +875,13 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::process_media_data(Function<void()>
866875
}
867876

868877
// https://html.spec.whatwg.org/multipage/media.html#dedicated-media-source-failure-steps
869-
WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises)
878+
WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises, String error_message)
870879
{
871-
auto& vm = this->vm();
880+
auto& realm = this->realm();
881+
auto& vm = realm.vm();
872882

873-
// FIXME: 1. Set the error attribute to the result of creating a MediaError with MEDIA_ERR_SRC_NOT_SUPPORTED.
883+
// 1. Set the error attribute to the result of creating a MediaError with MEDIA_ERR_SRC_NOT_SUPPORTED.
884+
m_error = TRY(vm.heap().allocate<MediaError>(realm, realm, MediaError::Code::SrcNotSupported, move(error_message)));
874885

875886
// 2. Forget the media element's media-resource-specific tracks.
876887
forget_media_resource_specific_tracks();
@@ -882,7 +893,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::handle_media_source_failure(Span<JS:
882893
set_show_poster(true);
883894

884895
// 5. Fire an event named error at the media element.
885-
dispatch_event(TRY(DOM::Event::create(realm(), HTML::EventNames::error)));
896+
dispatch_event(TRY(DOM::Event::create(realm, HTML::EventNames::error)));
886897

887898
// 6. Reject pending play promises with promises and a "NotSupportedError" DOMException.
888899
reject_pending_play_promises<WebIDL::NotSupportedError>(promises, TRY_OR_THROW_OOM(vm, "Media is not supported"_fly_string));

Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class HTMLMediaElement : public HTMLElement {
3434

3535
void queue_a_media_element_task(JS::SafeFunction<void()> steps);
3636

37+
JS::GCPtr<MediaError> error() const { return m_error; }
38+
3739
String const& current_src() const { return m_current_src; }
3840

3941
enum class NetworkState : u16 {
@@ -103,10 +105,10 @@ class HTMLMediaElement : public HTMLElement {
103105

104106
WebIDL::ExceptionOr<void> load_element();
105107
WebIDL::ExceptionOr<void> select_resource();
106-
WebIDL::ExceptionOr<void> fetch_resource(AK::URL const&, Function<void()> failure_callback);
108+
WebIDL::ExceptionOr<void> fetch_resource(AK::URL const&, Function<void(String)> failure_callback);
107109
static bool verify_response(JS::NonnullGCPtr<Fetch::Infrastructure::Response>, ByteRange const&);
108-
WebIDL::ExceptionOr<void> process_media_data(Function<void()> failure_callback);
109-
WebIDL::ExceptionOr<void> handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises);
110+
WebIDL::ExceptionOr<void> process_media_data(Function<void(String)> failure_callback);
111+
WebIDL::ExceptionOr<void> handle_media_source_failure(Span<JS::NonnullGCPtr<WebIDL::Promise>> promises, String error_message);
110112
void forget_media_resource_specific_tracks();
111113
void set_ready_state(ReadyState);
112114

@@ -148,6 +150,9 @@ class HTMLMediaElement : public HTMLElement {
148150
// https://html.spec.whatwg.org/multipage/media.html#media-element-event-task-source
149151
UniqueTaskSource m_media_element_event_task_source {};
150152

153+
// https://html.spec.whatwg.org/multipage/media.html#dom-media-error
154+
JS::GCPtr<MediaError> m_error;
155+
151156
// https://html.spec.whatwg.org/multipage/media.html#dom-media-crossorigin
152157
CORSSettingAttribute m_crossorigin { CORSSettingAttribute::NoCORS };
153158

Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import <HTML/HTMLElement.idl>
2+
#import <HTML/MediaError.idl>
23
#import <HTML/TimeRanges.idl>
34
#import <HTML/VideoTrackList.idl>
45

@@ -12,6 +13,9 @@ enum CanPlayTypeResult {
1213
[Exposed=Window]
1314
interface HTMLMediaElement : HTMLElement {
1415

16+
// error state
17+
readonly attribute MediaError? error;
18+
1519
// network state
1620
[Reflect, CEReactions] attribute DOMString src;
1721
readonly attribute USVString currentSrc;

0 commit comments

Comments
 (0)