mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Use a queue to process fullscreen request completions
Instead of immediately firing fullscreenchange, defer that until WebContent's client has confirmed that it is in fullscreen for the content. The fullscreenchange is fired by the viewport change, so in cases where the fullscreen transition is instantaneous (i.e. the fullscreen state is entered at the exact moment the viewport expands), the resize event should precede the fullscreenchange event, as the spec requires. This fixes the WPT element-request-fullscreen-timing.html test, which was previously succeeding by accident because we were immediately fullscreenchange upon requestFullscreen() being called, instead of following spec and doing the viewport (window) resize in parallel. The WPT test was actually initially intended to assert that the fullscreenchange event follows the resize event, but the WPT runner didn't actually have a different resolution for normal vs fullscreen viewports, so the resize event doesn't actually fire in their setup. In our headless mode, the default viewport is 800x600, and the fullscreen viewport is 1920x1080, so we do fire a resize event when entering fullscreen. Therefore, that imported test is reverted to assert that the resize precedes the fullscreenchange.
This commit is contained in:
committed by
Gregory Bertilson
parent
ae9537a53c
commit
2e54c18fb3
Notes:
github-actions[bot]
2026-03-17 23:59:34 +00:00
Author: https://github.com/Zaggy1024 Commit: https://github.com/LadybirdBrowser/ladybird/commit/2e54c18fb3a Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/8461
@@ -6994,60 +6994,7 @@ GC::Ref<WebIDL::Promise> Document::exit_fullscreen()
|
||||
}
|
||||
|
||||
// 8. Return promise, and run the remaining steps in parallel.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [&realm, doc, promise, resize] {
|
||||
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
// FIXME: 9. Run the fully unlock the screen orientation steps with doc.
|
||||
|
||||
// 10. If resize is true, resize doc’s viewport to its "normal" dimensions.
|
||||
// NB: Fullscreen API is affected by site-isolation and will require additional work once site-isolation is implemented.
|
||||
if (resize)
|
||||
doc->page().client().page_did_request_exit_fullscreen();
|
||||
|
||||
// 11. If doc’s fullscreen element is null, then resolve promise with undefined and terminate these steps.
|
||||
if (!doc->fullscreen_element()) {
|
||||
WebIDL::resolve_promise(realm, promise, JS::js_undefined());
|
||||
return;
|
||||
}
|
||||
|
||||
// 12. Let exitDocs be the result of collecting documents to unfullscreen given doc.
|
||||
auto exit_docs = doc->collect_documents_to_unfullscreen();
|
||||
|
||||
// 13. Let descendantDocs be an ordered set consisting of doc’s descendant navigables' active documents whose
|
||||
// fullscreen element is non-null, if any, in tree order.
|
||||
auto descendant_docs = realm.heap().allocate<GC::HeapVector<GC::Ref<Document>>>();
|
||||
for (auto& descendant : doc->descendant_navigables()) {
|
||||
if (descendant->active_document()->fullscreen_element())
|
||||
descendant_docs->elements().append(*descendant->active_document());
|
||||
}
|
||||
|
||||
// 14. For each exitDoc in exitDocs:
|
||||
for (auto& exit_doc : exit_docs->elements()) {
|
||||
// 1. Append (fullscreenchange, exitDoc’s fullscreen element) to exitDoc’s list of pending fullscreen events.
|
||||
exit_doc->append_pending_fullscreen_change(PendingFullscreenEvent::Type::Change, *exit_doc->fullscreen_element());
|
||||
|
||||
// 2. If resize is true, unfullscreen exitDoc.
|
||||
if (resize)
|
||||
exit_doc->unfullscreen();
|
||||
// 3. Otherwise, unfullscreen exitDoc’s fullscreen element.
|
||||
else
|
||||
exit_doc->unfullscreen_element(*exit_doc->fullscreen_element());
|
||||
}
|
||||
|
||||
// 15. For each descendantDoc in descendantDocs:
|
||||
for (auto& descendant_doc : descendant_docs->elements()) {
|
||||
// 1. Append (fullscreenchange, descendantDoc’s fullscreen element) to descendantDoc’s list of pending fullscreen events.
|
||||
descendant_doc->append_pending_fullscreen_change(PendingFullscreenEvent::Type::Change, *descendant_doc->fullscreen_element());
|
||||
|
||||
// 2. Unfullscreen descendantDoc.
|
||||
descendant_doc->unfullscreen();
|
||||
}
|
||||
|
||||
// Note: The order in which documents are unfullscreened is not observable, because run the fullscreen steps is
|
||||
// invoked in tree order.
|
||||
|
||||
// 16. Resolve promise with undefined.
|
||||
WebIDL::resolve_promise(realm, promise, JS::js_undefined());
|
||||
}));
|
||||
page().enqueue_fullscreen_exit(doc, resize, promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
@@ -1007,6 +1007,9 @@ public:
|
||||
GC::Ref<WebIDL::Promise> exit_fullscreen();
|
||||
|
||||
void unfullscreen_element(GC::Ref<Element> element);
|
||||
void unfullscreen();
|
||||
bool is_simple_fullscreen_document() const;
|
||||
GC::Ref<GC::HeapVector<GC::Ref<Document>>> collect_documents_to_unfullscreen();
|
||||
|
||||
auto& script_blocking_style_sheet_set() { return m_script_blocking_style_sheet_set; }
|
||||
auto const& script_blocking_style_sheet_set() const { return m_script_blocking_style_sheet_set; }
|
||||
@@ -1057,9 +1060,6 @@ private:
|
||||
|
||||
void evaluate_media_rules();
|
||||
|
||||
bool is_simple_fullscreen_document() const;
|
||||
GC::Ref<GC::HeapVector<GC::Ref<Document>>> collect_documents_to_unfullscreen();
|
||||
|
||||
enum class AddLineFeed {
|
||||
Yes,
|
||||
No,
|
||||
@@ -1096,8 +1096,6 @@ private:
|
||||
|
||||
void ensure_cookie_version_index(URL::URL const& new_url, URL::URL const& old_url = {});
|
||||
|
||||
void unfullscreen();
|
||||
|
||||
GC::Ref<Page> m_page;
|
||||
GC::Ptr<CSS::StyleComputer> m_style_computer;
|
||||
GC::Ptr<CSS::FontComputer> m_font_computer;
|
||||
|
||||
@@ -2462,81 +2462,7 @@ GC::Ref<WebIDL::Promise> Element::request_fullscreen(FullscreenRequester fullscr
|
||||
}
|
||||
|
||||
// 7. Return promise, and run the remaining steps in parallel.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [&realm, error, pending_doc, requesting_element = GC::Ref { *this }, promise]() mutable {
|
||||
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
// NB: Fullscreen API is affected by site-isolation and will require additional work once site-isolation is implemented.
|
||||
|
||||
// 8. If error is false, then resize pendingDoc’s node navigable’s top-level traversable’s active document’s
|
||||
// viewport’s dimensions FIXME: optionally taking into account options["navigationUI"]:
|
||||
if (error == RequestFullscreenError::False)
|
||||
pending_doc->page().client().page_did_request_fullscreen_window();
|
||||
|
||||
// 9. If any of the following conditions are false, then set error to true:
|
||||
// * This’s node document is pendingDoc.
|
||||
// * The fullscreen element ready check for this returns true.
|
||||
if (pending_doc != requesting_element->owner_document())
|
||||
error = RequestFullscreenError::ElementNodeDocIsNotPendingDoc;
|
||||
if (!requesting_element->is_element_ready_for_fullscreen())
|
||||
error = RequestFullscreenError::ElementReadyCheckFailed;
|
||||
|
||||
// 10. If error is true:
|
||||
if (error != RequestFullscreenError::False) {
|
||||
// 1. Append (fullscreenerror, this) to pendingDoc’s list of pending fullscreen events.
|
||||
pending_doc->append_pending_fullscreen_change(PendingFullscreenEvent::Type::Error, requesting_element);
|
||||
|
||||
// 2. Reject promise with a TypeError exception and terminate these steps.
|
||||
WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, request_fullscreen_error_to_string(error)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 11. Let fullscreenElements be an ordered set initially consisting of this.
|
||||
auto fullscreen_elements = realm.heap().allocate<GC::HeapVector<GC::Ref<Element>>>();
|
||||
fullscreen_elements->elements().append(requesting_element);
|
||||
|
||||
// 12. While true:
|
||||
while (true) {
|
||||
// 1. Let last be the last item of fullscreenElements.
|
||||
auto last = fullscreen_elements->elements().last();
|
||||
|
||||
// 2. Let container be last’s node navigable’s container.
|
||||
auto container = last->navigable()->container();
|
||||
|
||||
// 3. If container is null, then break.
|
||||
if (!container)
|
||||
break;
|
||||
|
||||
// 4. Append container to fullscreenElements.
|
||||
fullscreen_elements->elements().append(*container);
|
||||
}
|
||||
|
||||
// 13. For each element in fullscreenElements:
|
||||
for (auto& element : fullscreen_elements->elements()) {
|
||||
// 1. Let doc be element’s node document.
|
||||
auto& doc = element->document();
|
||||
|
||||
// 2. If element is doc’s fullscreen element, continue.
|
||||
if (doc.fullscreen_element() == element) {
|
||||
// Note: No need to notify observers when nothing has changed.
|
||||
continue;
|
||||
}
|
||||
|
||||
// 3. If element is this and this is an iframe element, then set element’s iframe fullscreen flag.
|
||||
if (element == requesting_element && requesting_element->is_html_iframe_element()) {
|
||||
auto& iframe_element = static_cast<HTML::HTMLIFrameElement&>(*element);
|
||||
iframe_element.set_iframe_fullscreen_flag(true);
|
||||
}
|
||||
|
||||
// 4. Fullscreen element within doc.
|
||||
doc.fullscreen_element_within_doc(element);
|
||||
|
||||
// 5. Append (fullscreenchange, element) to doc’s list of pending fullscreen events.
|
||||
doc.append_pending_fullscreen_change(PendingFullscreenEvent::Type::Change, element);
|
||||
}
|
||||
|
||||
// 14. Resolve promise with undefined
|
||||
WebIDL::resolve_promise(realm, promise, JS::js_undefined());
|
||||
}));
|
||||
pending_doc->page().enqueue_fullscreen_enter(*this, *pending_doc, error, promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
@@ -272,6 +272,9 @@ public:
|
||||
};
|
||||
GC::Ref<WebIDL::Promise> request_fullscreen(FullscreenRequester = FullscreenRequester::Bindings);
|
||||
|
||||
RequestFullscreenError is_element_allowed_to_enter_fullscreen(FullscreenRequester) const;
|
||||
bool is_element_ready_for_fullscreen() const;
|
||||
|
||||
void set_fullscreen_flag(bool is_fullscreen) { m_fullscreen_flag = is_fullscreen; }
|
||||
bool is_fullscreen_element() const { return m_fullscreen_flag; }
|
||||
|
||||
@@ -606,8 +609,6 @@ private:
|
||||
void invalidate_style_after_attribute_change(FlyString const& attribute_name, Optional<String> const& old_value, Optional<String> const& new_value);
|
||||
|
||||
void exit_fullscreen_on_element_removal();
|
||||
RequestFullscreenError is_element_allowed_to_enter_fullscreen(FullscreenRequester) const;
|
||||
bool is_element_ready_for_fullscreen() const;
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ptr<Node>> insert_adjacent(StringView where, GC::Ref<Node> node);
|
||||
|
||||
|
||||
@@ -9,12 +9,15 @@
|
||||
#include <AK/SourceLocation.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/Clipboard/SystemClipboard.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/HTML/BrowsingContext.h>
|
||||
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
||||
#include <LibWeb/HTML/HTMLIFrameElement.h>
|
||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/HTML/HTMLMediaElement.h>
|
||||
#include <LibWeb/HTML/HTMLSelectElement.h>
|
||||
@@ -51,6 +54,16 @@ void Page::visit_edges(JS::Cell::Visitor& visitor)
|
||||
visitor.visit(m_window_rect_observer);
|
||||
visitor.visit(m_on_pending_dialog_closed);
|
||||
visitor.visit(m_pending_clipboard_requests);
|
||||
m_pending_fullscreen_operations.for_each([&](auto const& operation) {
|
||||
operation.visit([&](PendingFullscreenEnter const& enter_operation) {
|
||||
visitor.visit(enter_operation.element);
|
||||
visitor.visit(enter_operation.pending_doc);
|
||||
visitor.visit(enter_operation.promise); },
|
||||
[&](PendingFullscreenExit const& exit_operation) {
|
||||
visitor.visit(exit_operation.doc);
|
||||
visitor.visit(exit_operation.promise);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
HTML::Navigable& Page::focused_navigable()
|
||||
@@ -851,6 +864,209 @@ void Page::update_find_in_page_selection(Vector<GC::Root<DOM::Range>> matches)
|
||||
}
|
||||
}
|
||||
|
||||
void Page::enqueue_fullscreen_enter(GC::Ref<DOM::Element> element, GC::Ref<DOM::Document> pending_doc, DOM::RequestFullscreenError error, GC::Ref<WebIDL::Promise> promise)
|
||||
{
|
||||
m_pending_fullscreen_operations.enqueue(PendingFullscreenEnter { element, pending_doc, error, promise });
|
||||
// NOTE: Processing is deferred because the spec says "run the remaining steps in parallel",
|
||||
// meaning the caller's synchronous JS should complete before we process the operation.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this]() {
|
||||
process_pending_fullscreen_operations();
|
||||
}));
|
||||
}
|
||||
|
||||
void Page::enqueue_fullscreen_exit(GC::Ref<DOM::Document> doc, bool resize, GC::Ref<WebIDL::Promise> promise)
|
||||
{
|
||||
m_pending_fullscreen_operations.enqueue(PendingFullscreenExit { doc, resize, promise });
|
||||
// NOTE: Processing is deferred because the spec says "run the remaining steps in parallel",
|
||||
// meaning the caller's synchronous JS should complete before we process the operation.
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this]() {
|
||||
process_pending_fullscreen_operations();
|
||||
}));
|
||||
}
|
||||
|
||||
void Page::process_pending_fullscreen_operations()
|
||||
{
|
||||
// FIXME: The Fullscreen API interacts with the top-level traversable's viewport. With site-isolation,
|
||||
// an iframe's content process won't have direct access to this Page, so fullscreen operations
|
||||
// will need to be routed through IPC to the top-level process.
|
||||
|
||||
// NOTE: Resolving/rejecting promises during processing may trigger JS microtasks that re-enter
|
||||
// this function (e.g., JS calls exitFullscreen() after a requestFullscreen() promise resolves).
|
||||
// The outer call's while loop will pick up newly enqueued items.
|
||||
if (m_processing_fullscreen_operations)
|
||||
return;
|
||||
m_processing_fullscreen_operations = true;
|
||||
ScopeGuard guard = [this] { m_processing_fullscreen_operations = false; };
|
||||
|
||||
while (!m_pending_fullscreen_operations.is_empty()) {
|
||||
auto& front = m_pending_fullscreen_operations.head();
|
||||
|
||||
auto processed = front.visit(
|
||||
[&](PendingFullscreenEnter& enter) -> bool {
|
||||
// https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
|
||||
|
||||
// 8. If error is false, then resize pendingDoc's node navigable's top-level traversable's
|
||||
// active document's viewport's dimensions, optionally taking into account
|
||||
// options["navigationUI"]:
|
||||
if (enter.error == DOM::RequestFullscreenError::False) {
|
||||
if (m_viewport_is_fullscreen == ViewportIsFullscreen::No) {
|
||||
if (!m_fullscreen_ipc_sent_to_ui) {
|
||||
m_client->page_did_request_fullscreen_window();
|
||||
m_fullscreen_ipc_sent_to_ui = true;
|
||||
}
|
||||
// NB: Stop processing here and wait for a change in the fullscreen state if we aren't
|
||||
// in the desired state yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 9. If any of the following conditions are false, then set error to true:
|
||||
// * This's node document is pendingDoc.
|
||||
// * The fullscreen element ready check for this returns true.
|
||||
if (enter.element->owner_document() != enter.pending_doc.ptr())
|
||||
enter.error = DOM::RequestFullscreenError::ElementNodeDocIsNotPendingDoc;
|
||||
else if (!enter.element->is_element_ready_for_fullscreen())
|
||||
enter.error = DOM::RequestFullscreenError::ElementReadyCheckFailed;
|
||||
}
|
||||
|
||||
auto& realm = enter.element->realm();
|
||||
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
|
||||
// 10. If error is true:
|
||||
if (enter.error != DOM::RequestFullscreenError::False) {
|
||||
// 1. Append (fullscreenerror, this) to pendingDoc's list of pending fullscreen events.
|
||||
enter.pending_doc->append_pending_fullscreen_change(DOM::PendingFullscreenEvent::Type::Error, enter.element);
|
||||
|
||||
// 2. Reject promise with a TypeError exception and terminate these steps.
|
||||
WebIDL::reject_promise(realm, enter.promise, JS::TypeError::create(realm, DOM::request_fullscreen_error_to_string(enter.error)));
|
||||
return true;
|
||||
}
|
||||
|
||||
// 11. Let fullscreenElements be an ordered set initially consisting of this.
|
||||
auto fullscreen_elements = realm.heap().allocate<GC::HeapVector<GC::Ref<DOM::Element>>>();
|
||||
fullscreen_elements->elements().append(enter.element);
|
||||
|
||||
// 12. While true:
|
||||
while (true) {
|
||||
// 1. Let last be the last item of fullscreenElements.
|
||||
auto last = fullscreen_elements->elements().last();
|
||||
|
||||
// 2. Let container be last's node navigable's container.
|
||||
auto container = last->navigable()->container();
|
||||
|
||||
// 3. If container is null, then break.
|
||||
if (!container)
|
||||
break;
|
||||
|
||||
// 4. Append container to fullscreenElements.
|
||||
fullscreen_elements->elements().append(*container);
|
||||
}
|
||||
|
||||
// 13. For each element in fullscreenElements:
|
||||
for (auto& element : fullscreen_elements->elements()) {
|
||||
// 1. Let doc be element's node document.
|
||||
auto& doc = element->document();
|
||||
|
||||
// 2. If element is doc's fullscreen element, continue.
|
||||
if (doc.fullscreen_element() == element)
|
||||
continue;
|
||||
|
||||
// 3. If element is this and this is an iframe element, then set element's iframe fullscreen flag.
|
||||
if (element == enter.element && is<HTML::HTMLIFrameElement>(*enter.element))
|
||||
as<HTML::HTMLIFrameElement>(*element).set_iframe_fullscreen_flag(true);
|
||||
|
||||
// 4. Fullscreen element within doc.
|
||||
doc.fullscreen_element_within_doc(element);
|
||||
|
||||
// 5. Append (fullscreenchange, element) to doc's list of pending fullscreen events.
|
||||
doc.append_pending_fullscreen_change(DOM::PendingFullscreenEvent::Type::Change, element);
|
||||
}
|
||||
|
||||
// 14. Resolve promise with undefined
|
||||
WebIDL::resolve_promise(realm, enter.promise, JS::js_undefined());
|
||||
return true;
|
||||
},
|
||||
[&](PendingFullscreenExit& exit) -> bool {
|
||||
auto& realm = exit.doc->realm();
|
||||
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
|
||||
// https://fullscreen.spec.whatwg.org/#exit-fullscreen
|
||||
|
||||
// FIXME: 9. Run the fully unlock the screen orientation steps with doc.
|
||||
|
||||
// 10. If resize is true, resize doc's viewport to its "normal" dimensions.
|
||||
if (exit.resize && m_viewport_is_fullscreen == ViewportIsFullscreen::Yes) {
|
||||
if (!m_fullscreen_ipc_sent_to_ui) {
|
||||
m_client->page_did_request_exit_fullscreen();
|
||||
m_fullscreen_ipc_sent_to_ui = true;
|
||||
}
|
||||
// NB: Stop processing here and wait for a change in the fullscreen state if we aren't
|
||||
// in the desired state yet.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 11. If doc's fullscreen element is null, then resolve promise with undefined and terminate these
|
||||
// steps.
|
||||
if (!exit.doc->fullscreen_element()) {
|
||||
WebIDL::resolve_promise(realm, exit.promise, JS::js_undefined());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 12. Let exitDocs be the result of collecting documents to unfullscreen given doc.
|
||||
auto exit_docs = exit.doc->collect_documents_to_unfullscreen();
|
||||
|
||||
// 13. Let descendantDocs be an ordered set consisting of doc's descendant navigables' active documents
|
||||
// whose fullscreen element is non-null, if any, in tree order.
|
||||
auto descendant_docs = realm.heap().allocate<GC::HeapVector<GC::Ref<DOM::Document>>>();
|
||||
for (auto& descendant : exit.doc->descendant_navigables()) {
|
||||
if (descendant->active_document()->fullscreen_element())
|
||||
descendant_docs->elements().append(*descendant->active_document());
|
||||
}
|
||||
|
||||
// 14. For each exitDoc in exitDocs:
|
||||
for (auto& exit_doc : exit_docs->elements()) {
|
||||
// 1. Append (fullscreenchange, exitDoc's fullscreen element) to exitDoc's list of pending
|
||||
// fullscreen events.
|
||||
exit_doc->append_pending_fullscreen_change(DOM::PendingFullscreenEvent::Type::Change, *exit_doc->fullscreen_element());
|
||||
|
||||
// 2. If resize is true, unfullscreen exitDoc.
|
||||
if (exit.resize)
|
||||
exit_doc->unfullscreen();
|
||||
// 3. Otherwise, unfullscreen exitDoc's fullscreen element.
|
||||
else
|
||||
exit_doc->unfullscreen_element(*exit_doc->fullscreen_element());
|
||||
}
|
||||
|
||||
// 15. For each descendantDoc in descendantDocs:
|
||||
for (auto& descendant_doc : descendant_docs->elements()) {
|
||||
// 1. Append (fullscreenchange, descendantDoc's fullscreen element) to descendantDoc's list of
|
||||
// pending fullscreen events.
|
||||
descendant_doc->append_pending_fullscreen_change(DOM::PendingFullscreenEvent::Type::Change, *descendant_doc->fullscreen_element());
|
||||
|
||||
// 2. Unfullscreen descendantDoc.
|
||||
descendant_doc->unfullscreen();
|
||||
}
|
||||
|
||||
// 16. Resolve promise with undefined.
|
||||
WebIDL::resolve_promise(realm, exit.promise, JS::js_undefined());
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!processed)
|
||||
break;
|
||||
|
||||
m_pending_fullscreen_operations.dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
void Page::set_viewport_is_fullscreen(ViewportIsFullscreen is_fullscreen)
|
||||
{
|
||||
if (m_viewport_is_fullscreen == is_fullscreen)
|
||||
return;
|
||||
m_viewport_is_fullscreen = is_fullscreen;
|
||||
m_fullscreen_ipc_sent_to_ui = false;
|
||||
process_pending_fullscreen_operations();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibGC/Root.h>
|
||||
#include <LibGC/Weak.h>
|
||||
#include <LibGfx/Cursor.h>
|
||||
@@ -31,6 +33,7 @@
|
||||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/PreferredContrast.h>
|
||||
#include <LibWeb/CSS/PreferredMotion.h>
|
||||
#include <LibWeb/DOM/RequestFullscreenError.h>
|
||||
#include <LibWeb/Export.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
@@ -43,6 +46,7 @@
|
||||
#include <LibWeb/Loader/FileRequest.h>
|
||||
#include <LibWeb/Page/EventResult.h>
|
||||
#include <LibWeb/Page/InputEvent.h>
|
||||
#include <LibWeb/Page/ViewportIsFullscreen.h>
|
||||
#include <LibWeb/Painting/ChromeMetrics.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
#include <LibWeb/StorageAPI/StorageEndpoint.h>
|
||||
@@ -244,6 +248,13 @@ public:
|
||||
bool listen_for_dom_mutations() const { return m_listen_for_dom_mutations; }
|
||||
void set_listen_for_dom_mutations(bool listen_for_dom_mutations) { m_listen_for_dom_mutations = listen_for_dom_mutations; }
|
||||
|
||||
void enqueue_fullscreen_enter(GC::Ref<DOM::Element>, GC::Ref<DOM::Document>, DOM::RequestFullscreenError, GC::Ref<WebIDL::Promise>);
|
||||
void enqueue_fullscreen_exit(GC::Ref<DOM::Document> doc, bool resize, GC::Ref<WebIDL::Promise>);
|
||||
void process_pending_fullscreen_operations();
|
||||
|
||||
ViewportIsFullscreen viewport_is_fullscreen() const { return m_viewport_is_fullscreen; }
|
||||
void set_viewport_is_fullscreen(ViewportIsFullscreen);
|
||||
|
||||
private:
|
||||
explicit Page(GC::Ref<PageClient>);
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
@@ -322,6 +333,26 @@ private:
|
||||
URL::URL m_last_find_in_page_url;
|
||||
|
||||
bool m_listen_for_dom_mutations { false };
|
||||
|
||||
struct PendingFullscreenEnter {
|
||||
GC::Ref<DOM::Element> element;
|
||||
GC::Ref<DOM::Document> pending_doc;
|
||||
DOM::RequestFullscreenError error;
|
||||
GC::Ref<WebIDL::Promise> promise;
|
||||
};
|
||||
|
||||
struct PendingFullscreenExit {
|
||||
GC::Ref<DOM::Document> doc;
|
||||
bool resize;
|
||||
GC::Ref<WebIDL::Promise> promise;
|
||||
};
|
||||
|
||||
using PendingFullscreenOperation = Variant<PendingFullscreenEnter, PendingFullscreenExit>;
|
||||
|
||||
Queue<PendingFullscreenOperation> m_pending_fullscreen_operations;
|
||||
ViewportIsFullscreen m_viewport_is_fullscreen { ViewportIsFullscreen::No };
|
||||
bool m_fullscreen_ipc_sent_to_ui { false };
|
||||
bool m_processing_fullscreen_operations { false };
|
||||
};
|
||||
|
||||
enum class DisplayListPlayerType {
|
||||
|
||||
@@ -196,10 +196,12 @@ void ConnectionFromClient::traverse_the_history_by_delta(u64 page_id, i32 delta)
|
||||
page->page().traverse_the_history_by_delta(delta);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio, Web::ViewportIsFullscreen)
|
||||
void ConnectionFromClient::set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio, Web::ViewportIsFullscreen is_fullscreen)
|
||||
{
|
||||
if (auto page = this->page(page_id); page.has_value())
|
||||
if (auto page = this->page(page_id); page.has_value()) {
|
||||
page->set_viewport(size, device_pixel_ratio);
|
||||
page->page().set_viewport_is_fullscreen(is_fullscreen);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionFromClient::ready_to_paint(u64 page_id)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
resize 1920x1080
|
||||
fullscreenchange
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
#target { width: 100px; height: 100px; }
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest(done => {
|
||||
const target = document.getElementById("target");
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
println(`resize ${window.innerWidth}x${window.innerHeight}`);
|
||||
});
|
||||
|
||||
document.addEventListener("fullscreenchange", () => {
|
||||
println("fullscreenchange");
|
||||
});
|
||||
|
||||
target.addEventListener("click", () => {
|
||||
target.requestFullscreen().then(() => {
|
||||
requestAnimationFrame(() => {
|
||||
document.exitFullscreen().then(() => done());
|
||||
});
|
||||
}, (err) => {
|
||||
println(`promise rejected: ${err}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
internals.click(5, 5);
|
||||
});
|
||||
</script>
|
||||
@@ -22,8 +22,7 @@ promise_test(async t => {
|
||||
if (event.type == 'fullscreenchange') {
|
||||
step_timeout(t.unreached_func('timer callback'));
|
||||
requestAnimationFrame(t.step_func_done(() => {
|
||||
// Removed 'resize' expectation for now, see https://crbug.com/381127087.
|
||||
assert_array_equals(events, ['fullscreenchange'], 'event order');
|
||||
assert_array_equals(events, ['resize', 'fullscreenchange'], 'event order');
|
||||
resolve();
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user