1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-09 09:34:57 +09:00

LibWeb+WebContent: Add APIs to control video playback state

This allows for the browser process to control the play/pause state,
whether we paint user agent controls on the video, and whether the video
loops when it finishes playing.
This commit is contained in:
Timothy Flynn 2023-05-15 11:17:58 -04:00 committed by Andreas Kling
parent d8f03dda08
commit c82f678fc6
Notes: sideshowbarker 2024-07-17 03:30:41 +09:00
9 changed files with 146 additions and 0 deletions

View file

@ -5,10 +5,12 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ScopeGuard.h>
#include <AK/SourceLocation.h> #include <AK/SourceLocation.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h> #include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <LibWeb/HTML/HTMLVideoElement.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/Page/Page.h> #include <LibWeb/Page/Page.h>
#include <LibWeb/Platform/EventLoopPlugin.h> #include <LibWeb/Platform/EventLoopPlugin.h>
@ -281,4 +283,82 @@ void Page::did_request_video_context_menu(i32 video_id, CSSPixelPoint position,
client().page_did_request_video_context_menu(position, url, target, modifiers, is_playing, has_user_agent_controls, is_looping); client().page_did_request_video_context_menu(position, url, target, modifiers, is_playing, has_user_agent_controls, is_looping);
} }
WebIDL::ExceptionOr<void> Page::toggle_video_play_state()
{
auto video_element = video_context_menu_element();
if (!video_element)
return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution
// context. This pushes one to allow the promise creation hook to run.
auto& environment_settings = video_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (video_element->potentially_playing())
TRY(video_element->pause());
else
TRY(video_element->play());
return {};
}
WebIDL::ExceptionOr<void> Page::toggle_video_loop_state()
{
auto video_element = video_context_menu_element();
if (!video_element)
return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution
// context. This pushes one to allow the promise creation hook to run.
auto& environment_settings = video_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (video_element->has_attribute(HTML::AttributeNames::loop))
video_element->remove_attribute(HTML::AttributeNames::loop);
else
TRY(video_element->set_attribute(HTML::AttributeNames::loop, {}));
return {};
}
WebIDL::ExceptionOr<void> Page::toggle_video_controls_state()
{
auto video_element = video_context_menu_element();
if (!video_element)
return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution
// context. This pushes one to allow the promise creation hook to run.
auto& environment_settings = video_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (video_element->has_attribute(HTML::AttributeNames::controls))
video_element->remove_attribute(HTML::AttributeNames::controls);
else
TRY(video_element->set_attribute(HTML::AttributeNames::controls, {}));
return {};
}
JS::GCPtr<HTML::HTMLVideoElement> Page::video_context_menu_element()
{
if (!m_video_context_menu_element_id.has_value())
return nullptr;
auto* dom_node = DOM::Node::from_id(*m_video_context_menu_element_id);
if (dom_node == nullptr)
return nullptr;
if (!is<HTML::HTMLVideoElement>(dom_node))
return nullptr;
return static_cast<HTML::HTMLVideoElement*>(dom_node);
}
} }

View file

@ -116,10 +116,15 @@ public:
void accept_dialog(); void accept_dialog();
void did_request_video_context_menu(i32 video_id, CSSPixelPoint, AK::URL const&, DeprecatedString const& target, unsigned modifiers, bool is_playing, bool has_user_agent_controls, bool is_looping); void did_request_video_context_menu(i32 video_id, CSSPixelPoint, AK::URL const&, DeprecatedString const& target, unsigned modifiers, bool is_playing, bool has_user_agent_controls, bool is_looping);
WebIDL::ExceptionOr<void> toggle_video_play_state();
WebIDL::ExceptionOr<void> toggle_video_loop_state();
WebIDL::ExceptionOr<void> toggle_video_controls_state();
bool pdf_viewer_supported() const { return m_pdf_viewer_supported; } bool pdf_viewer_supported() const { return m_pdf_viewer_supported; }
private: private:
JS::GCPtr<HTML::HTMLVideoElement> video_context_menu_element();
PageClient& m_client; PageClient& m_client;
JS::Handle<HTML::BrowsingContext> m_top_level_browsing_context; JS::Handle<HTML::BrowsingContext> m_top_level_browsing_context;

View file

@ -131,6 +131,21 @@ void ViewImplementation::run_javascript(StringView js_source)
client().async_run_javascript(js_source); client().async_run_javascript(js_source);
} }
void ViewImplementation::toggle_video_play_state()
{
client().async_toggle_video_play_state();
}
void ViewImplementation::toggle_video_loop_state()
{
client().async_toggle_video_loop_state();
}
void ViewImplementation::toggle_video_controls_state()
{
client().async_toggle_video_controls_state();
}
void ViewImplementation::handle_resize() void ViewImplementation::handle_resize()
{ {
resize_backing_stores_if_needed(WindowResizeInProgress::Yes); resize_backing_stores_if_needed(WindowResizeInProgress::Yes);

View file

@ -70,6 +70,10 @@ public:
void run_javascript(StringView); void run_javascript(StringView);
void toggle_video_play_state();
void toggle_video_loop_state();
void toggle_video_controls_state();
virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) = 0; virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) = 0;
virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id, Gfx::IntSize) = 0; virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id, Gfx::IntSize) = 0;
virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) = 0; virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) = 0;

View file

@ -756,6 +756,21 @@ void ConnectionFromClient::prompt_closed(Optional<String> const& response)
m_page_host->prompt_closed(response); m_page_host->prompt_closed(response);
} }
void ConnectionFromClient::toggle_video_play_state()
{
m_page_host->toggle_video_play_state().release_value_but_fixme_should_propagate_errors();
}
void ConnectionFromClient::toggle_video_loop_state()
{
m_page_host->toggle_video_loop_state().release_value_but_fixme_should_propagate_errors();
}
void ConnectionFromClient::toggle_video_controls_state()
{
m_page_host->toggle_video_controls_state().release_value_but_fixme_should_propagate_errors();
}
void ConnectionFromClient::inspect_accessibility_tree() void ConnectionFromClient::inspect_accessibility_tree()
{ {
if (auto* doc = page().top_level_browsing_context().active_document()) { if (auto* doc = page().top_level_browsing_context().active_document()) {

View file

@ -95,6 +95,10 @@ private:
virtual void confirm_closed(bool accepted) override; virtual void confirm_closed(bool accepted) override;
virtual void prompt_closed(Optional<String> const& response) override; virtual void prompt_closed(Optional<String> const& response) override;
virtual void toggle_video_play_state() override;
virtual void toggle_video_loop_state() override;
virtual void toggle_video_controls_state() override;
virtual Messages::WebContentServer::TakeDocumentScreenshotResponse take_document_screenshot() override; virtual Messages::WebContentServer::TakeDocumentScreenshotResponse take_document_screenshot() override;
virtual Messages::WebContentServer::GetLocalStorageEntriesResponse get_local_storage_entries() override; virtual Messages::WebContentServer::GetLocalStorageEntriesResponse get_local_storage_entries() override;

View file

@ -334,6 +334,21 @@ void PageHost::prompt_closed(Optional<String> response)
page().prompt_closed(move(response)); page().prompt_closed(move(response));
} }
Web::WebIDL::ExceptionOr<void> PageHost::toggle_video_play_state()
{
return page().toggle_video_play_state();
}
Web::WebIDL::ExceptionOr<void> PageHost::toggle_video_loop_state()
{
return page().toggle_video_loop_state();
}
Web::WebIDL::ExceptionOr<void> PageHost::toggle_video_controls_state()
{
return page().toggle_video_controls_state();
}
void PageHost::page_did_request_accept_dialog() void PageHost::page_did_request_accept_dialog()
{ {
m_client.async_did_request_accept_dialog(); m_client.async_did_request_accept_dialog();

View file

@ -49,6 +49,10 @@ public:
void confirm_closed(bool accepted); void confirm_closed(bool accepted);
void prompt_closed(Optional<String> response); void prompt_closed(Optional<String> response);
Web::WebIDL::ExceptionOr<void> toggle_video_play_state();
Web::WebIDL::ExceptionOr<void> toggle_video_loop_state();
Web::WebIDL::ExceptionOr<void> toggle_video_controls_state();
[[nodiscard]] Gfx::Color background_color() const; [[nodiscard]] Gfx::Color background_color() const;
private: private:

View file

@ -76,4 +76,8 @@ endpoint WebContentServer
alert_closed() =| alert_closed() =|
confirm_closed(bool accepted) =| confirm_closed(bool accepted) =|
prompt_closed(Optional<String> response) =| prompt_closed(Optional<String> response) =|
toggle_video_play_state() =|
toggle_video_loop_state() =|
toggle_video_controls_state() =|
} }