summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp98
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h12
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;