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:
* LayoutTests/platform/mac-wk2/TestExpectations: Remove duplicate test expectations to reduce confusion.
* 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 7c521eb
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 13 deletions.
3 changes: 1 addition & 2 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -7468,12 +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
Expand All @@ -7494,6 +7492,7 @@ imported/w3c/web-platform-tests/css/css-view-transitions/navigation/chromium-pai
imported/w3c/web-platform-tests/css/css-view-transitions/navigation/root-element-transition-iframe-with-startVT-on-main.html [ ImageOnlyFailure ]
# https://github.com/w3c/csswg-drafts/issues/10800
imported/w3c/web-platform-tests/css/css-view-transitions/navigation/pageswap-long-delay.html [ Failure ]
webkit.org/b/278028 imported/w3c/web-platform-tests/css/css-view-transitions/navigation/hide-before-reveal.html [ Failure ]

# prerender not supported.
imported/w3c/web-platform-tests/css/css-view-transitions/navigation/prerender-removed-during-navigation.html [ 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
3 changes: 0 additions & 3 deletions LayoutTests/platform/mac-wk2/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -1789,9 +1789,6 @@ imported/w3c/web-platform-tests/css/css-view-transitions/new-content-intrinsic-a
tiled-drawing/scrolling/overflow/overflow-scrolled-down-tile-coverage.html [ Pass Failure ]
tiled-drawing/scrolling/overflow/overflow-scrolled-up-tile-coverage.html [ Pass Failure ]

# rdar://133772823 ([Sonoma wk2] imported/w3c/web-platform-tests/css/css-view-transitions/navigation/hide-before-reveal.html is a flaky failure
[ Sonoma+ ] imported/w3c/web-platform-tests/css/css-view-transitions/navigation/hide-before-reveal.html [ Pass Failure ]

# webkit.org/b/278178 [macOS Debug] imported/w3c/web-platform-tests/css/cssom-view/scroll-behavior-smooth-positions.html is a flaky failure
imported/w3c/web-platform-tests/css/cssom-view/scroll-behavior-smooth-positions.html [ Pass Failure ]

Expand Down
18 changes: 15 additions & 3 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 @@ -8068,7 +8069,7 @@ void Document::dispatchPagehideEvent(PageshowEventPersistence persisted)
// https://www.w3.org/TR/css-view-transitions-2/#vt-rule-algo
std::variant<Document::SkipTransition, Vector<AtomString>> Document::resolveViewTransitionRule()
{
if (visibilityState() == VisibilityState::Hidden)
if (hidden())
return SkipTransition { };

auto rule = styleScope().resolver().viewTransitionRule();
Expand All @@ -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 @@ -10828,6 +10834,7 @@ void Document::flushDeferredRenderingIsSuppressedForViewTransitionChanges()
}
}

// https://drafts.csswg.org/css-view-transitions/#ViewTransition-prepare
RefPtr<ViewTransition> Document::startViewTransition(StartViewTransitionCallbackOptions&& callbackOptions)
{
if (!globalObject())
Expand All @@ -10851,6 +10858,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
15 changes: 15 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,26 @@ RenderViewTransitionCapture* ViewTransition::viewTransitionNewPseudoForCapturedE
return nullptr;
}

// https://drafts.csswg.org/css-view-transitions/#page-visibility-change-steps
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 });
} else
ASSERT(!protectedDocument()->activeViewTransition());
}

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 7c521eb

Please sign in to comment.