diff options
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp | 98 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h | 12 |
2 files changed, 110 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp index adfec5c96e..d2f71cc9ef 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp @@ -8,9 +8,15 @@ #include <LibGfx/Bitmap.h> #include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/DOM/Document.h> +#include <LibWeb/Fetch/Fetching/Fetching.h> +#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h> +#include <LibWeb/Fetch/Infrastructure/FetchController.h> +#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h> +#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h> #include <LibWeb/HTML/HTMLVideoElement.h> #include <LibWeb/HTML/VideoTrack.h> #include <LibWeb/Layout/VideoBox.h> +#include <LibWeb/Platform/ImageCodecPlugin.h> namespace Web::HTML { @@ -33,6 +39,23 @@ void HTMLVideoElement::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_video_track); + visitor.visit(m_fetch_controller); +} + +void HTMLVideoElement::parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) +{ + Base::parse_attribute(name, value); + + if (name == HTML::AttributeNames::poster) + determine_element_poster_frame(value).release_value_but_fixme_should_propagate_errors(); +} + +void HTMLVideoElement::did_remove_attribute(DeprecatedFlyString const& name) +{ + Base::did_remove_attribute(name); + + if (name == HTML::AttributeNames::poster) + determine_element_poster_frame({}).release_value_but_fixme_should_propagate_errors(); } JS::GCPtr<Layout::Node> HTMLVideoElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style) @@ -107,4 +130,79 @@ void HTMLVideoElement::on_seek(double position, MediaSeekMode seek_mode) m_video_track->seek(Time::from_milliseconds(position * 1000.0), seek_mode); } +// https://html.spec.whatwg.org/multipage/media.html#attr-video-poster +WebIDL::ExceptionOr<void> HTMLVideoElement::determine_element_poster_frame(Optional<StringView> const& poster) +{ + auto& realm = this->realm(); + auto& vm = realm.vm(); + + m_poster_frame = nullptr; + + // 1. If there is an existing instance of this algorithm running for this video element, abort that instance of + // this algorithm without changing the poster frame. + if (m_fetch_controller) + m_fetch_controller->stop_fetch(); + + // 2. If the poster attribute's value is the empty string or if the attribute is absent, then there is no poster + // frame; return. + if (!poster.has_value() || poster->is_empty()) + return {}; + + // 3. Parse the poster attribute's value relative to the element's node document. If this fails, then there is no + // poster frame; return. + auto url_record = document().parse_url(*poster); + if (!url_record.is_valid()) + return {}; + + // 4. Let request be a new request whose URL is the resulting URL record, client is the element's node document's + // relevant settings object, destination is "image", initiator type is "video", credentials mode is "include", + // and whose use-URL-credentials flag is set. + auto request = Fetch::Infrastructure::Request::create(vm); + request->set_url(move(url_record)); + request->set_client(&document().relevant_settings_object()); + request->set_destination(Fetch::Infrastructure::Request::Destination::Image); + request->set_initiator_type(Fetch::Infrastructure::Request::InitiatorType::Video); + request->set_credentials_mode(Fetch::Infrastructure::Request::CredentialsMode::Include); + request->set_use_url_credentials(true); + + // 5. Fetch request. This must delay the load event of the element's node document. + Fetch::Infrastructure::FetchAlgorithms::Input fetch_algorithms_input {}; + m_load_event_delayer.emplace(document()); + + fetch_algorithms_input.process_response = [this](auto response) mutable { + ScopeGuard guard { [&] { m_load_event_delayer.clear(); } }; + + auto& realm = this->realm(); + auto& global = document().realm().global_object(); + + if (response->is_network_error()) + return; + + if (response->type() == Fetch::Infrastructure::Response::Type::Opaque || response->type() == Fetch::Infrastructure::Response::Type::OpaqueRedirect) { + auto& filtered_response = static_cast<Fetch::Infrastructure::FilteredResponse&>(*response); + response = filtered_response.internal_response(); + } + + auto on_image_data_read = [this](auto image_data) mutable { + m_fetch_controller = nullptr; + + // 6. If an image is thus obtained, the poster frame is that image. Otherwise, there is no poster frame. + auto image = Platform::ImageCodecPlugin::the().decode_image(image_data); + if (!image.has_value() || image->frames.is_empty()) + return; + + m_poster_frame = move(image.release_value().frames[0].bitmap); + }; + + VERIFY(response->body().has_value()); + auto empty_algorithm = [](auto&) {}; + + response->body()->fully_read(realm, move(on_image_data_read), move(empty_algorithm), JS::NonnullGCPtr { global }).release_value_but_fixme_should_propagate_errors(); + }; + + m_fetch_controller = TRY(Fetch::Fetching::fetch(realm, request, Fetch::Infrastructure::FetchAlgorithms::create(vm, move(fetch_algorithms_input)))); + + return {}; +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h index 3de683d52a..1c2d3e59e1 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h @@ -8,8 +8,10 @@ #include <AK/Optional.h> #include <LibGfx/Forward.h> +#include <LibWeb/DOM/DocumentLoadEventDelayer.h> #include <LibWeb/Forward.h> #include <LibWeb/HTML/HTMLMediaElement.h> +#include <LibWeb/WebIDL/ExceptionOr.h> namespace Web::HTML { @@ -37,6 +39,7 @@ public: void set_current_frame(Badge<VideoTrack>, RefPtr<Gfx::Bitmap> frame, double position); VideoFrame const& current_frame() const { return m_current_frame; } + RefPtr<Gfx::Bitmap> const& poster_frame() const { return m_poster_frame; } void set_layout_mouse_position(Badge<Painting::VideoPaintable>, Optional<CSSPixelPoint> mouse_position) { m_mouse_position = move(mouse_position); } Optional<CSSPixelPoint> const& layout_mouse_position(Badge<Painting::VideoPaintable>) const { return m_mouse_position; } @@ -54,18 +57,27 @@ private: virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; + virtual void parse_attribute(DeprecatedFlyString const& name, DeprecatedString const& value) override; + virtual void did_remove_attribute(DeprecatedFlyString const&) override; + virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override; virtual void on_playing() override; virtual void on_paused() override; virtual void on_seek(double, MediaSeekMode) override; + WebIDL::ExceptionOr<void> determine_element_poster_frame(Optional<StringView> const& poster); + JS::GCPtr<HTML::VideoTrack> m_video_track; VideoFrame m_current_frame; + RefPtr<Gfx::Bitmap> m_poster_frame; u32 m_video_width { 0 }; u32 m_video_height { 0 }; + JS::GCPtr<Fetch::Infrastructure::FetchController> m_fetch_controller; + Optional<DOM::DocumentLoadEventDelayer> m_load_event_delayer; + // Cached state for layout Optional<CSSPixelPoint> m_mouse_position; mutable CachedLayoutBoxes m_layout_boxes; |