mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-22 09:18:55 +00:00
LibWeb: Implement plumbing for view transitions
This implements large parts of the CSS view-transitions-1 spec.
This commit is contained in:
parent
4f0e8236a0
commit
56739b4b16
Notes:
github-actions[bot]
2025-09-07 12:59:13 +00:00
Author: https://github.com/Psychpsyo
Commit: 56739b4b16
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5155
Reviewed-by: https://github.com/AtkinsSJ ✅
Reviewed-by: https://github.com/alimpfard
Reviewed-by: https://github.com/gmta
Reviewed-by: https://github.com/kalenikaliaksandr ✅
22 changed files with 1516 additions and 4 deletions
|
@ -948,6 +948,7 @@ set(SOURCES
|
||||||
URLPattern/URLPattern.cpp
|
URLPattern/URLPattern.cpp
|
||||||
UserTiming/PerformanceMark.cpp
|
UserTiming/PerformanceMark.cpp
|
||||||
UserTiming/PerformanceMeasure.cpp
|
UserTiming/PerformanceMeasure.cpp
|
||||||
|
ViewTransition/ViewTransition.cpp
|
||||||
WebAssembly/Global.cpp
|
WebAssembly/Global.cpp
|
||||||
WebAssembly/Instance.cpp
|
WebAssembly/Instance.cpp
|
||||||
WebAssembly/Memory.cpp
|
WebAssembly/Memory.cpp
|
||||||
|
|
|
@ -262,8 +262,13 @@ u32 Selector::specificity() const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SimpleSelector::Type::TagName:
|
case SimpleSelector::Type::TagName:
|
||||||
|
// count the number of type selectors and pseudo-elements in the selector (= C)
|
||||||
|
++tag_names;
|
||||||
|
break;
|
||||||
case SimpleSelector::Type::PseudoElement:
|
case SimpleSelector::Type::PseudoElement:
|
||||||
// count the number of type selectors and pseudo-elements in the selector (= C)
|
// count the number of type selectors and pseudo-elements in the selector (= C)
|
||||||
|
// FIXME: This needs special handling for view transition pseudos:
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#named-view-transition-pseudo
|
||||||
++tag_names;
|
++tag_names;
|
||||||
break;
|
break;
|
||||||
case SimpleSelector::Type::Universal:
|
case SimpleSelector::Type::Universal:
|
||||||
|
|
|
@ -135,6 +135,7 @@
|
||||||
#include <LibWeb/HTML/Scripting/Agent.h>
|
#include <LibWeb/HTML/Scripting/Agent.h>
|
||||||
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
||||||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||||
|
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||||
#include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
|
#include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
|
||||||
#include <LibWeb/HTML/SharedResourceRequest.h>
|
#include <LibWeb/HTML/SharedResourceRequest.h>
|
||||||
#include <LibWeb/HTML/Storage.h>
|
#include <LibWeb/HTML/Storage.h>
|
||||||
|
@ -465,6 +466,7 @@ Document::Document(JS::Realm& realm, URL::URL const& url, TemporaryDocumentForFr
|
||||||
, m_url(url)
|
, m_url(url)
|
||||||
, m_temporary_document_for_fragment_parsing(temporary_document_for_fragment_parsing)
|
, m_temporary_document_for_fragment_parsing(temporary_document_for_fragment_parsing)
|
||||||
, m_editing_host_manager(EditingHostManager::create(realm, *this))
|
, m_editing_host_manager(EditingHostManager::create(realm, *this))
|
||||||
|
, m_dynamic_view_transition_style_sheet(parse_css_stylesheet(CSS::Parser::ParsingParams(realm), ""sv, {}))
|
||||||
, m_style_invalidator(realm.heap().allocate<StyleInvalidator>())
|
, m_style_invalidator(realm.heap().allocate<StyleInvalidator>())
|
||||||
{
|
{
|
||||||
m_legacy_platform_object_flags = PlatformObject::LegacyPlatformObjectFlags {
|
m_legacy_platform_object_flags = PlatformObject::LegacyPlatformObjectFlags {
|
||||||
|
@ -606,6 +608,12 @@ void Document::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_adopted_style_sheets);
|
visitor.visit(m_adopted_style_sheets);
|
||||||
visitor.visit(m_script_blocking_style_sheet_set);
|
visitor.visit(m_script_blocking_style_sheet_set);
|
||||||
|
|
||||||
|
visitor.visit(m_active_view_transition);
|
||||||
|
visitor.visit(m_dynamic_view_transition_style_sheet);
|
||||||
|
|
||||||
|
for (auto& view_transition : m_update_callback_queue)
|
||||||
|
visitor.visit(view_transition);
|
||||||
|
|
||||||
visitor.visit(m_top_layer_elements);
|
visitor.visit(m_top_layer_elements);
|
||||||
visitor.visit(m_top_layer_pending_removals);
|
visitor.visit(m_top_layer_pending_removals);
|
||||||
visitor.visit(m_showing_auto_popover_list);
|
visitor.visit(m_showing_auto_popover_list);
|
||||||
|
@ -3376,7 +3384,8 @@ void Document::update_the_visibility_state(HTML::VisibilityState visibility_stat
|
||||||
|
|
||||||
// FIXME: 4. Run the screen orientation change steps with document.
|
// FIXME: 4. Run the screen orientation change steps with document.
|
||||||
|
|
||||||
// FIXME: 5. Run the view transition page visibility change steps with document.
|
// 5. Run the view transition page visibility change steps with document.
|
||||||
|
view_transition_page_visibility_change_steps();
|
||||||
|
|
||||||
// 6. Run any page visibility change steps which may be defined in other specifications, with visibility state and
|
// 6. Run any page visibility change steps which may be defined in other specifications, with visibility state and
|
||||||
// document.
|
// document.
|
||||||
|
@ -6038,6 +6047,10 @@ void Document::for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_dynamic_view_transition_style_sheet) {
|
||||||
|
callback(*m_dynamic_view_transition_style_sheet, {});
|
||||||
|
}
|
||||||
|
|
||||||
for_each_shadow_root([&](auto& shadow_root) {
|
for_each_shadow_root([&](auto& shadow_root) {
|
||||||
shadow_root.for_each_css_style_sheet([&](auto& style_sheet) {
|
shadow_root.for_each_css_style_sheet([&](auto& style_sheet) {
|
||||||
if (!style_sheet.disabled())
|
if (!style_sheet.disabled())
|
||||||
|
@ -6665,6 +6678,98 @@ void Document::set_onvisibilitychange(WebIDL::CallbackType* value)
|
||||||
set_event_handler_attribute(HTML::EventNames::visibilitychange, value);
|
set_event_handler_attribute(HTML::EventNames::visibilitychange, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
|
||||||
|
// FIXME: Calling document.startViewTransition() without arguments throws TypeError instead of calling this.
|
||||||
|
GC::Ptr<ViewTransition::ViewTransition> Document::start_view_transition(ViewTransition::ViewTransitionUpdateCallback update_callback)
|
||||||
|
{
|
||||||
|
// The method steps for startViewTransition(updateCallback) are as follows:
|
||||||
|
|
||||||
|
// 1. Let transition be a new ViewTransition object in this’s relevant Realm.
|
||||||
|
auto& realm = this->realm();
|
||||||
|
auto transition = ViewTransition::ViewTransition::create(realm);
|
||||||
|
|
||||||
|
// 2. If updateCallback is provided, set transition’s update callback to updateCallback.
|
||||||
|
if (update_callback != nullptr)
|
||||||
|
transition->set_update_callback(update_callback);
|
||||||
|
|
||||||
|
// 3. Let document be this’s relevant global object’s associated document.
|
||||||
|
auto& document = as<HTML::Window>(relevant_global_object(*this)).associated_document();
|
||||||
|
|
||||||
|
// 4. If document’s visibility state is "hidden", then skip transition with an "InvalidStateError" DOMException,
|
||||||
|
// and return transition.
|
||||||
|
if (m_visibility_state == HTML::VisibilityState::Hidden) {
|
||||||
|
transition->skip_the_view_transition(WebIDL::InvalidStateError::create(realm, "The document's visibility state is \"hidden\""_utf16));
|
||||||
|
return transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. If document’s active view transition is not null, then skip that view transition with an "AbortError"
|
||||||
|
// DOMException in this’s relevant Realm.
|
||||||
|
if (document.m_active_view_transition)
|
||||||
|
document.m_active_view_transition->skip_the_view_transition(WebIDL::AbortError::create(realm, "Document.startViewTransition() was called"_utf16));
|
||||||
|
|
||||||
|
// 6. Set document’s active view transition to transition.
|
||||||
|
m_active_view_transition = transition;
|
||||||
|
|
||||||
|
// 7. Return transition.
|
||||||
|
return transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#perform-pending-transition-operations
|
||||||
|
void Document::perform_pending_transition_operations()
|
||||||
|
{
|
||||||
|
// To perform pending transition operations given a Document document, perform the following steps:
|
||||||
|
|
||||||
|
// 1. If document’s active view transition is not null, then:
|
||||||
|
if (m_active_view_transition) {
|
||||||
|
// 1. If document’s active view transition’s phase is "pending-capture", then setup view transition for
|
||||||
|
// document’s active view transition.
|
||||||
|
if (m_active_view_transition->phase() == ViewTransition::ViewTransition::Phase::PendingCapture)
|
||||||
|
m_active_view_transition->setup_view_transition();
|
||||||
|
// 2. Otherwise, if document’s active view transition’s phase is "animating", then handle transition frame for
|
||||||
|
// document’s active view transition.
|
||||||
|
else if (m_active_view_transition->phase() == ViewTransition::ViewTransition::Phase::Animating)
|
||||||
|
m_active_view_transition->handle_transition_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#flush-the-update-callback-queue
|
||||||
|
void Document::flush_the_update_callback_queue()
|
||||||
|
{
|
||||||
|
// To flush the update callback queue given a Document document:
|
||||||
|
|
||||||
|
// 1. For each transition in document’s update callback queue, call the update callback given transition.
|
||||||
|
for (auto& transition : m_update_callback_queue) {
|
||||||
|
transition->call_the_update_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Set document’s update callback queue to an empty list.
|
||||||
|
m_update_callback_queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#view-transition-page-visibility-change-steps
|
||||||
|
void Document::view_transition_page_visibility_change_steps()
|
||||||
|
{
|
||||||
|
// The view transition page-visibility change steps given Document document are:
|
||||||
|
|
||||||
|
// 1. Queue a global task on the DOM manipulation task source, given document’s relevant global object, to
|
||||||
|
// perform the following steps:
|
||||||
|
HTML::queue_global_task(HTML::Task::Source::DOMManipulation, HTML::relevant_global_object(*this), GC::create_function(realm().heap(), [&] {
|
||||||
|
HTML::TemporaryExecutionContext context(realm());
|
||||||
|
// 1. If document’s visibility state is "hidden", then:
|
||||||
|
if (m_visibility_state == HTML::VisibilityState::Hidden) {
|
||||||
|
// 1. If document’s active view transition is not null, then skip document’s active view transition with an
|
||||||
|
// "InvalidStateError" DOMException.
|
||||||
|
if (m_active_view_transition) {
|
||||||
|
m_active_view_transition->skip_the_view_transition(WebIDL::InvalidStateError::create(realm(), "The document's visibility state is \"hidden\"."_utf16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. Otherwise, assert: active view transition is null.
|
||||||
|
else {
|
||||||
|
VERIFY(!m_active_view_transition);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
ElementByIdMap& Document::element_by_id() const
|
ElementByIdMap& Document::element_by_id() const
|
||||||
{
|
{
|
||||||
if (!m_element_by_id)
|
if (!m_element_by_id)
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include <LibWeb/InvalidateDisplayList.h>
|
#include <LibWeb/InvalidateDisplayList.h>
|
||||||
#include <LibWeb/ResizeObserver/ResizeObserver.h>
|
#include <LibWeb/ResizeObserver/ResizeObserver.h>
|
||||||
#include <LibWeb/TrustedTypes/InjectionSink.h>
|
#include <LibWeb/TrustedTypes/InjectionSink.h>
|
||||||
|
#include <LibWeb/ViewTransition/ViewTransition.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
#include <LibWeb/WebIDL/ObservableArray.h>
|
#include <LibWeb/WebIDL/ObservableArray.h>
|
||||||
|
|
||||||
|
@ -860,6 +861,22 @@ public:
|
||||||
[[nodiscard]] WebIDL::CallbackType* onvisibilitychange();
|
[[nodiscard]] WebIDL::CallbackType* onvisibilitychange();
|
||||||
void set_onvisibilitychange(WebIDL::CallbackType*);
|
void set_onvisibilitychange(WebIDL::CallbackType*);
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
|
||||||
|
GC::Ptr<ViewTransition::ViewTransition> start_view_transition(ViewTransition::ViewTransitionUpdateCallback update_callback);
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#perform-pending-transition-operations
|
||||||
|
void perform_pending_transition_operations();
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#flush-the-update-callback-queue
|
||||||
|
void flush_the_update_callback_queue();
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#view-transition-page-visibility-change-steps
|
||||||
|
void view_transition_page_visibility_change_steps();
|
||||||
|
|
||||||
|
GC::Ptr<ViewTransition::ViewTransition> active_view_transition() const { return m_active_view_transition; }
|
||||||
|
void set_active_view_transition(GC::Ptr<ViewTransition::ViewTransition> view_transition) { m_active_view_transition = view_transition; }
|
||||||
|
void set_rendering_suppression_for_view_transitions(bool value) { m_rendering_suppression_for_view_transitions = value; }
|
||||||
|
GC::Ptr<CSS::CSSStyleSheet> dynamic_view_transition_style_sheet() const { return m_dynamic_view_transition_style_sheet; }
|
||||||
|
void set_show_view_transition_tree(bool value) { m_show_view_transition_tree = value; }
|
||||||
|
Vector<GC::Ptr<ViewTransition::ViewTransition>>& update_callback_queue() { return m_update_callback_queue; }
|
||||||
|
|
||||||
void reset_cursor_blink_cycle();
|
void reset_cursor_blink_cycle();
|
||||||
|
|
||||||
GC::Ref<EditingHostManager> editing_host_manager() const { return *m_editing_host_manager; }
|
GC::Ref<EditingHostManager> editing_host_manager() const { return *m_editing_host_manager; }
|
||||||
|
@ -1289,6 +1306,21 @@ private:
|
||||||
// https://html.spec.whatwg.org/multipage/dom.html#render-blocking-element-set
|
// https://html.spec.whatwg.org/multipage/dom.html#render-blocking-element-set
|
||||||
HashTable<GC::Ref<Element>> m_render_blocking_elements;
|
HashTable<GC::Ref<Element>> m_render_blocking_elements;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-active-view-transition
|
||||||
|
GC::Ptr<ViewTransition::ViewTransition> m_active_view_transition;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-rendering-suppression-for-view-transitions
|
||||||
|
bool m_rendering_suppression_for_view_transitions { false };
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-dynamic-view-transition-style-sheet
|
||||||
|
GC::Ptr<CSS::CSSStyleSheet> m_dynamic_view_transition_style_sheet;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-show-view-transition-tree
|
||||||
|
bool m_show_view_transition_tree { false };
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-update-callback-queue
|
||||||
|
Vector<GC::Ptr<ViewTransition::ViewTransition>> m_update_callback_queue = {};
|
||||||
|
|
||||||
HashTable<WeakPtr<Node>> m_pending_nodes_for_style_invalidation_due_to_presence_of_has;
|
HashTable<WeakPtr<Node>> m_pending_nodes_for_style_invalidation_due_to_presence_of_has;
|
||||||
|
|
||||||
GC::Ref<StyleInvalidator> m_style_invalidator;
|
GC::Ref<StyleInvalidator> m_style_invalidator;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#import <HTML/HTMLScriptElement.idl>
|
#import <HTML/HTMLScriptElement.idl>
|
||||||
#import <HTML/Location.idl>
|
#import <HTML/Location.idl>
|
||||||
#import <Selection/Selection.idl>
|
#import <Selection/Selection.idl>
|
||||||
|
#import <ViewTransition/ViewTransition.idl>
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#document
|
// https://dom.spec.whatwg.org/#document
|
||||||
// https://html.spec.whatwg.org/multipage/dom.html#the-document-object
|
// https://html.spec.whatwg.org/multipage/dom.html#the-document-object
|
||||||
|
@ -157,6 +158,9 @@ interface Document : Node {
|
||||||
[LegacyLenientThis] attribute EventHandler onreadystatechange;
|
[LegacyLenientThis] attribute EventHandler onreadystatechange;
|
||||||
attribute EventHandler onvisibilitychange;
|
attribute EventHandler onvisibilitychange;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#additions-to-document-api
|
||||||
|
ViewTransition startViewTransition(optional ViewTransitionUpdateCallback updateCallback);
|
||||||
|
|
||||||
// https://www.w3.org/TR/SVG2/struct.html#InterfaceDocumentExtensions
|
// https://www.w3.org/TR/SVG2/struct.html#InterfaceDocumentExtensions
|
||||||
readonly attribute SVGSVGElement? rootElement;
|
readonly attribute SVGSVGElement? rootElement;
|
||||||
};
|
};
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
#include <LibWeb/Namespace.h>
|
#include <LibWeb/Namespace.h>
|
||||||
#include <LibWeb/Page/Page.h>
|
#include <LibWeb/Page/Page.h>
|
||||||
#include <LibWeb/Painting/PaintableBox.h>
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
|
#include <LibWeb/Painting/StackingContext.h>
|
||||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||||
#include <LibWeb/SVG/SVGAElement.h>
|
#include <LibWeb/SVG/SVGAElement.h>
|
||||||
#include <LibWeb/Selection/Selection.h>
|
#include <LibWeb/Selection/Selection.h>
|
||||||
|
@ -3975,6 +3976,44 @@ Optional<String> Element::lang() const
|
||||||
return maybe_lang.release_value();
|
return maybe_lang.release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-images-4/#element-not-rendered
|
||||||
|
bool Element::not_rendered() const
|
||||||
|
{
|
||||||
|
// An element is not rendered if it does not have an associated box.
|
||||||
|
if (!layout_node() || !paintable_box())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-scoped-view-transition-name
|
||||||
|
Optional<FlyString> Element::document_scoped_view_transition_name()
|
||||||
|
{
|
||||||
|
// To get the document-scoped view transition name for an Element element:
|
||||||
|
|
||||||
|
// 1. Let scopedViewTransitionName be the computed value of view-transition-name for element.
|
||||||
|
auto scoped_view_transition_name = computed_properties()->view_transition_name();
|
||||||
|
|
||||||
|
// 2. If scopedViewTransitionName is associated with element’s node document, then return
|
||||||
|
// scopedViewTransitionName.
|
||||||
|
// FIXME: Properly handle tree-scoping of the name here.
|
||||||
|
// (see https://drafts.csswg.org/css-view-transitions-1/#propdef-view-transition-name , "Each view transition name is a tree-scoped name.")
|
||||||
|
if (true) {
|
||||||
|
return scoped_view_transition_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Otherwise, return none.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-image
|
||||||
|
// To capture the image given an element element, perform the following steps. They return an image.
|
||||||
|
RefPtr<Gfx::ImmutableBitmap> Element::capture_the_image()
|
||||||
|
{
|
||||||
|
// FIXME: Actually implement this.
|
||||||
|
return Gfx::ImmutableBitmap::create(MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize(1, 1))));
|
||||||
|
}
|
||||||
|
|
||||||
void Element::set_pointer_capture(WebIDL::Long pointer_id)
|
void Element::set_pointer_capture(WebIDL::Long pointer_id)
|
||||||
{
|
{
|
||||||
(void)pointer_id;
|
(void)pointer_id;
|
||||||
|
|
|
@ -515,6 +515,18 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool captured_in_a_view_transition() const { return m_captured_in_a_view_transition; }
|
||||||
|
void set_captured_in_a_view_transition(bool value) { m_captured_in_a_view_transition = value; }
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-images-4/#element-not-rendered
|
||||||
|
bool not_rendered() const;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#document-scoped-view-transition-name
|
||||||
|
Optional<FlyString> document_scoped_view_transition_name();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-image
|
||||||
|
RefPtr<Gfx::ImmutableBitmap> capture_the_image();
|
||||||
|
|
||||||
void set_pointer_capture(WebIDL::Long pointer_id);
|
void set_pointer_capture(WebIDL::Long pointer_id);
|
||||||
void release_pointer_capture(WebIDL::Long pointer_id);
|
void release_pointer_capture(WebIDL::Long pointer_id);
|
||||||
bool has_pointer_capture(WebIDL::Long pointer_id);
|
bool has_pointer_capture(WebIDL::Long pointer_id);
|
||||||
|
@ -642,6 +654,9 @@ private:
|
||||||
// https://drafts.csswg.org/css-contain/#proximity-to-the-viewport
|
// https://drafts.csswg.org/css-contain/#proximity-to-the-viewport
|
||||||
ProximityToTheViewport m_proximity_to_the_viewport { ProximityToTheViewport::NotDetermined };
|
ProximityToTheViewport m_proximity_to_the_viewport { ProximityToTheViewport::NotDetermined };
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#captured-in-a-view-transition
|
||||||
|
bool m_captured_in_a_view_transition { false };
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/grouping-content.html#ordinal-value
|
// https://html.spec.whatwg.org/multipage/grouping-content.html#ordinal-value
|
||||||
Optional<i32> m_ordinal_value;
|
Optional<i32> m_ordinal_value;
|
||||||
bool m_is_contained_in_list_subtree { false };
|
bool m_is_contained_in_list_subtree { false };
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
#include <LibGC/CellAllocator.h>
|
#include <LibGC/CellAllocator.h>
|
||||||
#include <LibJS/Heap/Cell.h>
|
#include <LibJS/Heap/Cell.h>
|
||||||
|
#include <LibWeb/CSS/CascadedProperties.h>
|
||||||
#include <LibWeb/CSS/StyleProperty.h>
|
#include <LibWeb/CSS/StyleProperty.h>
|
||||||
#include <LibWeb/Export.h>
|
#include <LibWeb/Export.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
@ -53,9 +54,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-view-transitions/#pseudo-element-tree
|
// https://drafts.csswg.org/css-view-transitions/#pseudo-element-tree
|
||||||
class PseudoElementTreeNode final
|
class PseudoElementTreeNode
|
||||||
: public PseudoElement
|
: public PseudoElement
|
||||||
, TreeNode<PseudoElementTreeNode> {
|
, public TreeNode<PseudoElementTreeNode> {
|
||||||
GC_CELL(PseudoElementTreeNode, PseudoElement);
|
GC_CELL(PseudoElementTreeNode, PseudoElement);
|
||||||
GC_DECLARE_ALLOCATOR(PseudoElementTreeNode);
|
GC_DECLARE_ALLOCATOR(PseudoElementTreeNode);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1123,6 +1123,12 @@ class PerformanceMeasure;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Web::ViewTransition {
|
||||||
|
|
||||||
|
class ViewTransition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace Web::WebAssembly {
|
namespace Web::WebAssembly {
|
||||||
|
|
||||||
class Global;
|
class Global;
|
||||||
|
|
|
@ -458,7 +458,10 @@ void EventLoop::update_the_rendering()
|
||||||
|
|
||||||
// FIXME: 17. For each doc of docs, if the focused area of doc is not a focusable area, then run the focusing steps for doc's viewport, and set doc's relevant global object's navigation API's focus changed during ongoing navigation to false.
|
// FIXME: 17. For each doc of docs, if the focused area of doc is not a focusable area, then run the focusing steps for doc's viewport, and set doc's relevant global object's navigation API's focus changed during ongoing navigation to false.
|
||||||
|
|
||||||
// FIXME: 18. For each doc of docs, perform pending transition operations for doc. [CSSVIEWTRANSITIONS]
|
// 18. For each doc of docs, perform pending transition operations for doc. [CSSVIEWTRANSITIONS]
|
||||||
|
for (auto& document : docs) {
|
||||||
|
document->perform_pending_transition_operations();
|
||||||
|
}
|
||||||
|
|
||||||
// 19. For each doc of docs, run the update intersection observations steps for doc, passing in the relative high resolution time given now and doc's relevant global object as the timestamp. [INTERSECTIONOBSERVER]
|
// 19. For each doc of docs, run the update intersection observations steps for doc, passing in the relative high resolution time given now and doc's relevant global object as the timestamp. [INTERSECTIONOBSERVER]
|
||||||
for (auto& document : docs) {
|
for (auto& document : docs) {
|
||||||
|
|
|
@ -2587,6 +2587,22 @@ void Navigable::paste(String const& text)
|
||||||
m_event_handler.handle_paste(text);
|
m_event_handler.handle_paste(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#snapshot-containing-block
|
||||||
|
CSSPixelRect Navigable::snapshot_containing_block()
|
||||||
|
{
|
||||||
|
// The snapshot containing block is a rectangle that covers all areas of the window that could potentially display
|
||||||
|
// page content (and is therefore consistent regardless of root scrollbars or interactive widgets).
|
||||||
|
|
||||||
|
// Within a child navigable, the snapshot containing block is the union of the navigable’s viewport with any scrollbar gutters.
|
||||||
|
// FIXME: Actually get the correct rectangle here.
|
||||||
|
return viewport_rect();
|
||||||
|
}
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#snapshot-containing-block-size
|
||||||
|
CSSPixelSize Navigable::snapshot_containing_block_size()
|
||||||
|
{
|
||||||
|
return this->snapshot_containing_block().size();
|
||||||
|
}
|
||||||
|
|
||||||
void Navigable::register_navigation_observer(Badge<NavigationObserver>, NavigationObserver& navigation_observer)
|
void Navigable::register_navigation_observer(Badge<NavigationObserver>, NavigationObserver& navigation_observer)
|
||||||
{
|
{
|
||||||
auto result = m_navigation_observers.set(navigation_observer);
|
auto result = m_navigation_observers.set(navigation_observer);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <AK/HashTable.h>
|
#include <AK/HashTable.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <AK/Tuple.h>
|
||||||
#include <LibJS/Heap/Cell.h>
|
#include <LibJS/Heap/Cell.h>
|
||||||
#include <LibWeb/Bindings/NavigationPrototype.h>
|
#include <LibWeb/Bindings/NavigationPrototype.h>
|
||||||
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
||||||
|
@ -200,6 +201,11 @@ public:
|
||||||
Web::EventHandler& event_handler() { return m_event_handler; }
|
Web::EventHandler& event_handler() { return m_event_handler; }
|
||||||
Web::EventHandler const& event_handler() const { return m_event_handler; }
|
Web::EventHandler const& event_handler() const { return m_event_handler; }
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#snapshot-containing-block
|
||||||
|
CSSPixelRect snapshot_containing_block();
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#snapshot-containing-block-size
|
||||||
|
CSSPixelSize snapshot_containing_block_size();
|
||||||
|
|
||||||
bool has_session_history_entry_and_ready_for_navigation() const { return m_has_session_history_entry_and_ready_for_navigation; }
|
bool has_session_history_entry_and_ready_for_navigation() const { return m_has_session_history_entry_and_ready_for_navigation; }
|
||||||
void set_has_session_history_entry_and_ready_for_navigation();
|
void set_has_session_history_entry_and_ready_for_navigation();
|
||||||
|
|
||||||
|
|
1050
Libraries/LibWeb/ViewTransition/ViewTransition.cpp
Normal file
1050
Libraries/LibWeb/ViewTransition/ViewTransition.cpp
Normal file
File diff suppressed because it is too large
Load diff
173
Libraries/LibWeb/ViewTransition/ViewTransition.h
Normal file
173
Libraries/LibWeb/ViewTransition/ViewTransition.h
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Psychpsyo <psychpsyo@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/Tuple.h>
|
||||||
|
#include <LibGfx/Bitmap.h>
|
||||||
|
#include <LibWeb/Bindings/PlatformObject.h>
|
||||||
|
#include <LibWeb/Bindings/ViewTransitionPrototype.h>
|
||||||
|
#include <LibWeb/CSS/Enums.h>
|
||||||
|
#include <LibWeb/CSS/Filter.h>
|
||||||
|
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||||
|
#include <LibWeb/CSS/Transformation.h>
|
||||||
|
#include <LibWeb/DOM/PseudoElement.h>
|
||||||
|
#include <LibWeb/Forward.h>
|
||||||
|
#include <LibWeb/PixelUnits.h>
|
||||||
|
|
||||||
|
namespace Web::ViewTransition {
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#named-view-transition-pseudo
|
||||||
|
class NamedViewTransitionPseudoElement
|
||||||
|
: public DOM::PseudoElementTreeNode {
|
||||||
|
GC_CELL(NamedViewTransitionPseudoElement, DOM::PseudoElementTreeNode);
|
||||||
|
GC_DECLARE_ALLOCATOR(NamedViewTransitionPseudoElement);
|
||||||
|
|
||||||
|
NamedViewTransitionPseudoElement(CSS::PseudoElement, FlyString);
|
||||||
|
|
||||||
|
CSS::PseudoElement m_type;
|
||||||
|
|
||||||
|
// Several of the view transition pseudo-elements are named view transition pseudo-elements, which are
|
||||||
|
// functional tree-abiding view transition pseudo-elements associated with a view transition name.
|
||||||
|
FlyString m_view_transition_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#::view-transition-old
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#::view-transition-new
|
||||||
|
class ReplacedNamedViewTransitionPseudoElement
|
||||||
|
: public NamedViewTransitionPseudoElement {
|
||||||
|
GC_CELL(ReplacedNamedViewTransitionPseudoElement, NamedViewTransitionPseudoElement);
|
||||||
|
GC_DECLARE_ALLOCATOR(ReplacedNamedViewTransitionPseudoElement);
|
||||||
|
|
||||||
|
ReplacedNamedViewTransitionPseudoElement(CSS::PseudoElement, FlyString, RefPtr<Gfx::ImmutableBitmap>);
|
||||||
|
|
||||||
|
RefPtr<Gfx::ImmutableBitmap> m_content;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#captured-element
|
||||||
|
struct CapturedElement : public JS::Cell {
|
||||||
|
GC_CELL(CapturedElement, JS::Cell)
|
||||||
|
GC_DECLARE_ALLOCATOR(CapturedElement);
|
||||||
|
|
||||||
|
RefPtr<Gfx::ImmutableBitmap> old_image {};
|
||||||
|
CSSPixels old_width = 0;
|
||||||
|
CSSPixels old_height = 0;
|
||||||
|
// FIXME: Make this an identity transform function by default.
|
||||||
|
CSS::Transformation old_transform = CSS::Transformation(CSS::TransformFunction::Translate, Vector<CSS::TransformValue>());
|
||||||
|
Optional<CSS::WritingMode> old_writing_mode {};
|
||||||
|
Optional<CSS::Direction> old_direction {};
|
||||||
|
// FIXME: old_text_orientation
|
||||||
|
Optional<CSS::MixBlendMode> old_mix_blend_mode {};
|
||||||
|
CSS::Filter old_backdrop_filter {};
|
||||||
|
Optional<CSS::PreferredColorScheme> old_color_scheme {};
|
||||||
|
GC::Ptr<DOM::Element> new_element {};
|
||||||
|
|
||||||
|
GC::Ptr<CSS::CSSKeyframesRule> group_keyframes {};
|
||||||
|
GC::Ptr<CSS::CSSStyleRule> group_animation_name_rule {};
|
||||||
|
GC::Ptr<CSS::CSSStyleRule> group_styles_rule {};
|
||||||
|
GC::Ptr<CSS::CSSStyleRule> image_pair_isolation_rule {};
|
||||||
|
GC::Ptr<CSS::CSSStyleRule> image_animation_name_rule {};
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#callbackdef-viewtransitionupdatecallback
|
||||||
|
using ViewTransitionUpdateCallback = GC::Ptr<WebIDL::CallbackType>;
|
||||||
|
|
||||||
|
class ViewTransition final : public Bindings::PlatformObject {
|
||||||
|
WEB_PLATFORM_OBJECT(ViewTransition, Bindings::PlatformObject);
|
||||||
|
GC_DECLARE_ALLOCATOR(ViewTransition);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static GC::Ref<ViewTransition> create(JS::Realm&);
|
||||||
|
virtual ~ViewTransition() override = default;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-viewtransition-updatecallbackdone
|
||||||
|
GC::Ref<WebIDL::Promise> update_callback_done() const { return m_update_callback_done_promise; }
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-viewtransition-ready
|
||||||
|
GC::Ref<WebIDL::Promise> ready() const { return m_ready_promise; }
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-viewtransition-finished
|
||||||
|
GC::Ref<WebIDL::Promise> finished() const { return m_finished_promise; }
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#dom-viewtransition-skiptransition
|
||||||
|
void skip_transition();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#setup-view-transition
|
||||||
|
void setup_view_transition();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#activate-view-transition
|
||||||
|
void activate_view_transition();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-old-state
|
||||||
|
ErrorOr<void> capture_the_old_state();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#capture-the-new-state
|
||||||
|
ErrorOr<void> capture_the_new_state();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#setup-transition-pseudo-elements
|
||||||
|
void setup_transition_pseudo_elements();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#call-the-update-callback
|
||||||
|
void call_the_update_callback();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#schedule-the-update-callback
|
||||||
|
void schedule_the_update_callback();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#skip-the-view-transition
|
||||||
|
void skip_the_view_transition(JS::Value reason);
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#handle-transition-frame
|
||||||
|
void handle_transition_frame();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#update-pseudo-element-styles
|
||||||
|
ErrorOr<void> update_pseudo_element_styles();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#clear-view-transition
|
||||||
|
void clear_view_transition();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-phase
|
||||||
|
enum class Phase : u8 {
|
||||||
|
PendingCapture,
|
||||||
|
UpdateCallbackCalled,
|
||||||
|
Animating,
|
||||||
|
Done,
|
||||||
|
};
|
||||||
|
Phase phase() const { return m_phase; }
|
||||||
|
void set_update_callback(ViewTransitionUpdateCallback callback) { m_update_callback = callback; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ViewTransition(JS::Realm&, GC::Ref<WebIDL::Promise>, GC::Ref<WebIDL::Promise>, GC::Ref<WebIDL::Promise>);
|
||||||
|
virtual void initialize(JS::Realm&) override;
|
||||||
|
|
||||||
|
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-named-elements
|
||||||
|
HashMap<FlyString, GC::Ptr<CapturedElement>> m_named_elements = {};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-phase
|
||||||
|
Phase m_phase = Phase::PendingCapture;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-update-callback
|
||||||
|
ViewTransitionUpdateCallback m_update_callback = {};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-ready-promise
|
||||||
|
GC::Ref<WebIDL::Promise> m_ready_promise;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-update-callback-done-promise
|
||||||
|
GC::Ref<WebIDL::Promise> m_update_callback_done_promise;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-finished-promise
|
||||||
|
GC::Ref<WebIDL::Promise> m_finished_promise;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-transition-root-pseudo-element
|
||||||
|
GC::Ref<DOM::PseudoElementTreeNode> m_transition_root_pseudo_element;
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-initial-snapshot-containing-block-size
|
||||||
|
Optional<CSSPixelSize> m_initial_snapshot_containing_block_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
11
Libraries/LibWeb/ViewTransition/ViewTransition.idl
Normal file
11
Libraries/LibWeb/ViewTransition/ViewTransition.idl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#callbackdef-viewtransitionupdatecallback
|
||||||
|
callback ViewTransitionUpdateCallback = Promise<any> ();
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition
|
||||||
|
[Exposed=Window]
|
||||||
|
interface ViewTransition {
|
||||||
|
readonly attribute Promise<undefined> updateCallbackDone;
|
||||||
|
readonly attribute Promise<undefined> ready;
|
||||||
|
readonly attribute Promise<undefined> finished;
|
||||||
|
undefined skipTransition();
|
||||||
|
};
|
|
@ -424,6 +424,7 @@ libweb_js_bindings(UIEvents/WheelEvent)
|
||||||
libweb_js_bindings(URLPattern/URLPattern)
|
libweb_js_bindings(URLPattern/URLPattern)
|
||||||
libweb_js_bindings(UserTiming/PerformanceMark)
|
libweb_js_bindings(UserTiming/PerformanceMark)
|
||||||
libweb_js_bindings(UserTiming/PerformanceMeasure)
|
libweb_js_bindings(UserTiming/PerformanceMeasure)
|
||||||
|
libweb_js_bindings(ViewTransition/ViewTransition)
|
||||||
libweb_js_bindings(WebAssembly/Global)
|
libweb_js_bindings(WebAssembly/Global)
|
||||||
libweb_js_bindings(WebAssembly/Instance)
|
libweb_js_bindings(WebAssembly/Instance)
|
||||||
libweb_js_bindings(WebAssembly/Memory)
|
libweb_js_bindings(WebAssembly/Memory)
|
||||||
|
|
|
@ -137,6 +137,7 @@ static bool is_platform_object(Type const& type)
|
||||||
"VTTRegion"sv,
|
"VTTRegion"sv,
|
||||||
"VideoTrack"sv,
|
"VideoTrack"sv,
|
||||||
"VideoTrackList"sv,
|
"VideoTrackList"sv,
|
||||||
|
"ViewTransition"sv,
|
||||||
"WebGL2RenderingContext"sv,
|
"WebGL2RenderingContext"sv,
|
||||||
"WebGLActiveInfo"sv,
|
"WebGLActiveInfo"sv,
|
||||||
"WebGLBuffer"sv,
|
"WebGLBuffer"sv,
|
||||||
|
|
|
@ -43,6 +43,7 @@ static constexpr Array libweb_interface_namespaces = {
|
||||||
"Streams"sv,
|
"Streams"sv,
|
||||||
"UIEvents"sv,
|
"UIEvents"sv,
|
||||||
"URLPattern"sv,
|
"URLPattern"sv,
|
||||||
|
"ViewTransition"sv,
|
||||||
"WebAudio"sv,
|
"WebAudio"sv,
|
||||||
"WebGL"sv,
|
"WebGL"sv,
|
||||||
"WebIDL"sv,
|
"WebIDL"sv,
|
||||||
|
|
|
@ -41,6 +41,7 @@ source_set("DOM") {
|
||||||
"ParentNode.cpp",
|
"ParentNode.cpp",
|
||||||
"Position.cpp",
|
"Position.cpp",
|
||||||
"ProcessingInstruction.cpp",
|
"ProcessingInstruction.cpp",
|
||||||
|
"PseudoElement.cpp",
|
||||||
"QualifiedName.cpp",
|
"QualifiedName.cpp",
|
||||||
"Range.cpp",
|
"Range.cpp",
|
||||||
"ShadowRoot.cpp",
|
"ShadowRoot.cpp",
|
||||||
|
|
|
@ -359,6 +359,7 @@ standard_idl_files = [
|
||||||
"//Userland/Libraries/LibWeb/UIEvents/WheelEvent.idl",
|
"//Userland/Libraries/LibWeb/UIEvents/WheelEvent.idl",
|
||||||
"//Userland/Libraries/LibWeb/UserTiming/PerformanceMark.idl",
|
"//Userland/Libraries/LibWeb/UserTiming/PerformanceMark.idl",
|
||||||
"//Userland/Libraries/LibWeb/UserTiming/PerformanceMeasure.idl",
|
"//Userland/Libraries/LibWeb/UserTiming/PerformanceMeasure.idl",
|
||||||
|
"//Userland/Libraries/LibWeb/ViewTransition/ViewTransition.idl",
|
||||||
"//Userland/Libraries/LibWeb/WebAssembly/Instance.idl",
|
"//Userland/Libraries/LibWeb/WebAssembly/Instance.idl",
|
||||||
"//Userland/Libraries/LibWeb/WebAssembly/Memory.idl",
|
"//Userland/Libraries/LibWeb/WebAssembly/Memory.idl",
|
||||||
"//Userland/Libraries/LibWeb/WebAssembly/Module.idl",
|
"//Userland/Libraries/LibWeb/WebAssembly/Module.idl",
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="reftest-wait">
|
||||||
|
<title>Update callback should only be called once</title>
|
||||||
|
<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/#call-dom-update-callback-algorithm">
|
||||||
|
<link rel="match" href="../../../../expected/wpt-import/css/reference/ref-filled-green-100px-square.xht">
|
||||||
|
<meta name="fuzzy" content="maxDifference=0-1; totalPixels=0-500">
|
||||||
|
<style>
|
||||||
|
#target {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
::view-transition-group(*),
|
||||||
|
::view-transition-image-pair(*),
|
||||||
|
::view-transition-old(*),
|
||||||
|
::view-transition-new(*) {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||||
|
<div id="target"></div>
|
||||||
|
<script>
|
||||||
|
addEventListener("load", () => {
|
||||||
|
let updateCallbackCalled = false;
|
||||||
|
document.startViewTransition(function() {
|
||||||
|
target.style.backgroundColor = updateCallbackCalled ? "red" : "green";
|
||||||
|
updateCallbackCalled = true;
|
||||||
|
document.startViewTransition(() => {});
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
document.documentElement.classList.remove("reftest-wait");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -476,6 +476,7 @@ VTTRegion
|
||||||
ValidityState
|
ValidityState
|
||||||
VideoTrack
|
VideoTrack
|
||||||
VideoTrackList
|
VideoTrackList
|
||||||
|
ViewTransition
|
||||||
VisualViewport
|
VisualViewport
|
||||||
WeakMap
|
WeakMap
|
||||||
WeakRef
|
WeakRef
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue