Skip to content

Commit

Permalink
[view-transitions] Skip view transition on hidden pages
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=271248
rdar://125017653

Reviewed by NOBODY (OOPS!).

Follow:
- https://drafts.csswg.org/css-view-transitions-1/#page-visibility-change-steps
- w3c/csswg-drafts#9543
- w3c/csswg-drafts#10815

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/transition-in-hidden-page-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/transition-in-hidden-page.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/window-resize-aborts-transition-before-ready.html:
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/window-resize-aborts-transition.html:
* Source/WebCore/dom/Document.cpp:
(WebCore::Document::visibilityStateChanged): Make this more robust to allow unregistering clients while iterating.
(WebCore::Document::reveal):
(WebCore::Document::clearInboundViewTransitionParams):
(WebCore::Document::startViewTransition):
* Source/WebCore/dom/ViewTransition.cpp:
(WebCore::ViewTransition::ViewTransition):
(WebCore::ViewTransition::skipViewTransition):
(WebCore::ViewTransition::stop):
(WebCore::ViewTransition::visibilityStateChanged):
* Source/WebCore/dom/ViewTransition.h:
  • Loading branch information
nt1m committed Sep 3, 2024
1 parent 3c3c719 commit 2e66344
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 10 deletions.
3 changes: 0 additions & 3 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -7468,13 +7468,10 @@ imported/w3c/web-platform-tests/css/css-view-transitions/paint-holding-in-iframe
imported/w3c/web-platform-tests/css/css-view-transitions/iframe-transition.sub.html [ Skip ]
imported/w3c/web-platform-tests/css/css-view-transitions/fragmented-during-transition-skips.html [ Skip ]
imported/w3c/web-platform-tests/css/css-view-transitions/root-element-display-none-during-transition-crash.html [ Skip ]
imported/w3c/web-platform-tests/css/css-view-transitions/transition-in-hidden-page.html [ Skip ]

# Flakes
imported/w3c/web-platform-tests/css/css-view-transitions/synchronous-callback-skipped-before-run.html [ Failure Pass ]
imported/w3c/web-platform-tests/css/css-view-transitions/old-content-intrinsic-aspect-ratio.html [ ImageOnlyFailure Pass ]
imported/w3c/web-platform-tests/css/css-view-transitions/navigation/hide-before-reveal.html [ Failure Pass ]
imported/w3c/web-platform-tests/css/css-view-transitions/navigation/pageswap-ctor.html [ Failure Pass ]

# Reftests with variants are not supported
imported/w3c/web-platform-tests/css/css-view-transitions/web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group(%20%20%20%20%20%20first%20) [ Skip ]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

FAIL A view transition should be immediately skipped if started when document is hidden assert_unreached: Should have rejected: undefined Reached unreachable code
FAIL A view transition should be skipped when a document becomes hidden while processing update callback assert_equals: expected "rejected" but got "fulfilled"
FAIL A view transition should be skipped when a document becomes hidden while animating assert_equals: expected "finished" but got "timeout"
PASS A view transition should be immediately skipped if started when document is hidden
PASS A view transition should be skipped when a document becomes hidden while processing update callback
PASS A view transition should be skipped when a document becomes hidden while animating

Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
await wsc.minimize();
assert_true(document.hidden);
const transition = document.startViewTransition();
await wsc.restore();
await promise_rejects_dom(t, "InvalidStateError", transition.ready);
await wsc.restore();
}, "A view transition should be immediately skipped if started when document is hidden");

promise_test(async t => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
popup_win = window.open('about:blank', 'popup', 'width=300,height=300');
});

if (popup_win.document.visibilityState == "hidden") {
await new Promise((resolve) => {
popup_win.document.addEventListener("visibilitychange", resolve, { once: true });
});
}

// Resize the window while the update callback is running (i.e. before
// capturing the new state).
let transition = popup_win.document.startViewTransition(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
html::view-transition-old(*) {animation-duration: 10s;opacity: 1;}
</style>`;

if (popupDoc.visibilityState == "hidden") {
await new Promise((resolve) => {
popupDoc.addEventListener("visibilitychange", resolve, { once: true });
});
}

// Start a transition inside the popup.
let transition = popupDoc.startViewTransition(() => {
popupDoc.documentElement.classList.add('new');
Expand Down
15 changes: 13 additions & 2 deletions Source/WebCore/dom/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2343,8 +2343,9 @@ void Document::visibilityStateChanged()
{
// https://w3c.github.io/page-visibility/#reacting-to-visibilitychange-changes
queueTaskToDispatchEvent(TaskSource::UserInteraction, Event::create(eventNames().visibilitychangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No));
for (auto& client : m_visibilityStateCallbackClients)
m_visibilityStateCallbackClients.forEach([](auto& client) {
client.visibilityStateChanged();
});

#if ENABLE(MEDIA_STREAM) && PLATFORM(IOS_FAMILY)
if (auto mediaSessionManager = PlatformMediaSessionManager::sharedManagerIfExists()) {
Expand Down Expand Up @@ -8089,7 +8090,7 @@ void Document::reveal()

PageRevealEvent::Init init;

RefPtr<ViewTransition> inboundTransition = ViewTransition::resolveInboundCrossDocumentViewTransition(*this, std::exchange(m_inboundViewTransitionParams, nullptr));
RefPtr inboundTransition = ViewTransition::resolveInboundCrossDocumentViewTransition(*this, std::exchange(m_inboundViewTransitionParams, nullptr));
if (inboundTransition)
init.viewTransition = inboundTransition;

Expand All @@ -8110,6 +8111,11 @@ void Document::transferViewTransitionParams(Document& newDocument)
newDocument.m_inboundViewTransitionParams = std::exchange(m_inboundViewTransitionParams, nullptr);
}

void Document::clearInboundViewTransitionParams()
{
m_inboundViewTransitionParams = nullptr;
}

void Document::dispatchPageswapEvent(bool canTriggerCrossDocumentViewTransition, RefPtr<NavigationActivation>&& activation)
{
if (!settings().crossDocumentViewTransitionsEnabled())
Expand Down Expand Up @@ -10851,6 +10857,11 @@ RefPtr<ViewTransition> Document::startViewTransition(StartViewTransitionCallback

Ref viewTransition = ViewTransition::createSamePage(*this, WTFMove(updateCallback), WTFMove(activeTypes));

if (hidden()) {
viewTransition->skipViewTransition(Exception { ExceptionCode::InvalidStateError, "View transition was skipped because document visibility state is hidden."_s });
return viewTransition;
}

if (RefPtr activeViewTransition = m_activeViewTransition)
activeViewTransition->skipViewTransition(Exception { ExceptionCode::AbortError, "Old view transition aborted by new view transition."_s });

Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/dom/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,10 @@ class Document
void dispatchPageshowEvent(PageshowEventPersistence);
void dispatchPagehideEvent(PageshowEventPersistence);
void dispatchPageswapEvent(bool canTriggerCrossDocumentViewTransition, RefPtr<NavigationActivation>&&);

void transferViewTransitionParams(Document&);
void clearInboundViewTransitionParams();

WEBCORE_EXPORT void enqueueSecurityPolicyViolationEvent(SecurityPolicyViolationEventInit&&);
void enqueueHashchangeEvent(const String& oldURL, const String& newURL);
void dispatchPopstateEvent(RefPtr<SerializedScriptValue>&& stateObject);
Expand Down
13 changes: 13 additions & 0 deletions Source/WebCore/dom/ViewTransition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ ViewTransition::ViewTransition(Document& document, RefPtr<ViewTransitionUpdateCa
, m_finished(createPromiseAndWrapper(document))
, m_types(ViewTransitionTypeSet::create(document, WTFMove(initialActiveTypes)))
{
document.registerForVisibilityStateChangedCallbacks(*this);
}

ViewTransition::ViewTransition(Document& document, Vector<AtomString>&& initialActiveTypes)
Expand Down Expand Up @@ -859,12 +860,24 @@ RenderViewTransitionCapture* ViewTransition::viewTransitionNewPseudoForCapturedE
return nullptr;
}

void ViewTransition::visibilityStateChanged()
{
if (!document())
return;

if (protectedDocument()->hidden() && protectedDocument()->activeViewTransition() == this) {
protectedDocument()->clearInboundViewTransitionParams();
skipViewTransition(Exception { ExceptionCode::InvalidStateError, "Skipping view transition because document visibility state has become hidden."_s });
}
}

void ViewTransition::stop()
{
if (!document())
return;

m_phase = ViewTransitionPhase::Done;
document()->unregisterForVisibilityStateChangedCallbacks(*this);

if (document()->activeViewTransition() == this)
clearViewTransition();
Expand Down
6 changes: 5 additions & 1 deletion Source/WebCore/dom/ViewTransition.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "Styleable.h"
#include "ViewTransitionTypeSet.h"
#include "ViewTransitionUpdateCallback.h"
#include "VisibilityChangeClient.h"
#include <wtf/CheckedRef.h>
#include <wtf/Ref.h>
#include <wtf/TZoneMalloc.h>
Expand Down Expand Up @@ -150,7 +151,7 @@ struct ViewTransitionParams {
float initialPageZoom;
};

class ViewTransition : public RefCounted<ViewTransition>, public CanMakeWeakPtr<ViewTransition>, public ActiveDOMObject {
class ViewTransition : public RefCounted<ViewTransition>, public VisibilityChangeClient, public ActiveDOMObject {
WTF_MAKE_TZONE_ALLOCATED(ViewTransition);
public:
static Ref<ViewTransition> createSamePage(Document&, RefPtr<ViewTransitionUpdateCallback>&&, Vector<AtomString>&&);
Expand Down Expand Up @@ -208,6 +209,9 @@ class ViewTransition : public RefCounted<ViewTransition>, public CanMakeWeakPtr<

void clearViewTransition();

// VisibilityChangeClient.
void visibilityStateChanged() final;

// ActiveDOMObject.
void stop() final;

Expand Down

0 comments on commit 2e66344

Please sign in to comment.