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

Add transition-in-hidden-page.html WPT from upstream.

* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/transition-in-hidden-page-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/transition-in-hidden-page.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/window-resize-aborts-transition-before-ready-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/window-resize-aborts-transition-expected.txt:
* Source/WebCore/dom/Document.cpp:
(WebCore::Document::visibilityStateChanged): Make this more robust to allow unregistering clients while iterating.
(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 Jun 11, 2024
1 parent 258ed7a commit 8481719
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
CONSOLE MESSAGE: Unhandled Promise Rejection: InvalidStateError: View transition was skipped because document visibility state is hidden.

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"
Harness Error (FAIL), message = Unhandled rejection: View transition was skipped because document visibility state is hidden.

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
@@ -1,5 +1,5 @@

PASS
FAIL
View transitions: Resizing viewport before animating rejects the ready promise.

assert_true: Transition must be skipped. expected true got false

Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
CONSOLE MESSAGE: Unhandled Promise Rejection: InvalidStateError: View transition was skipped because document visibility state is hidden.

PASS View transitions: Resizing viewport skips the transition
Harness Error (FAIL), message = Unhandled rejection: View transition was skipped because document visibility state is hidden.

TIMEOUT View transitions: Resizing viewport skips the transition Test timed out

Harness Error (FAIL), message = Unhandled rejection: View transition was skipped because document visibility state is hidden.

TIMEOUT View transitions: Resizing viewport skips the transition Test timed out

8 changes: 7 additions & 1 deletion Source/WebCore/dom/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2346,8 +2346,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 @@ -10596,6 +10597,11 @@ RefPtr<ViewTransition> Document::startViewTransition(RefPtr<ViewTransitionUpdate

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

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
17 changes: 14 additions & 3 deletions Source/WebCore/dom/ViewTransition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ ViewTransition::ViewTransition(Document& document, RefPtr<ViewTransitionUpdateCa
, m_updateCallbackDone(createPromiseAndWrapper(document))
, m_finished(createPromiseAndWrapper(document))
{
document.registerForVisibilityStateChangedCallbacks(*this);
}

ViewTransition::~ViewTransition() = default;
Expand Down Expand Up @@ -774,24 +775,34 @@ RenderViewTransitionCapture* ViewTransition::viewTransitionNewPseudoForCapturedE
return nullptr;
}

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

if (protectedDocument()->hidden() && protectedDocument()->activeViewTransition() == this)
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();
}

bool ViewTransition::documentElementIsCaptured() const
{
RefPtr doc = document();
if (!doc)
RefPtr document = this->document();
if (!document)
return false;

RefPtr documentElement = doc->documentElement();
RefPtr documentElement = document->documentElement();
if (!documentElement)
return false;

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 @@ -34,6 +34,7 @@
#include "MutableStyleProperties.h"
#include "Styleable.h"
#include "ViewTransitionUpdateCallback.h"
#include "VisibilityChangeClient.h"
#include <wtf/CheckedRef.h>
#include <wtf/Ref.h>
#include <wtf/text/AtomString.h>
Expand Down Expand Up @@ -132,7 +133,7 @@ struct OrderedNamedElementsMap {
HashMap<AtomString, UniqueRef<CapturedElement>> m_map;
};

class ViewTransition : public RefCounted<ViewTransition>, public CanMakeWeakPtr<ViewTransition>, public ActiveDOMObject {
class ViewTransition : public RefCounted<ViewTransition>, public VisibilityChangeClient, public ActiveDOMObject {
public:
static Ref<ViewTransition> create(Document&, RefPtr<ViewTransitionUpdateCallback>&&);
~ViewTransition();
Expand Down Expand Up @@ -180,6 +181,9 @@ class ViewTransition : public RefCounted<ViewTransition>, public CanMakeWeakPtr<

void clearViewTransition();

// VisibilityChangeClient.
void visibilityStateChanged() final;

// ActiveDOMObject.
void stop() final;

Expand Down

0 comments on commit 8481719

Please sign in to comment.