Bug 1916311 - [css-view-transitions] Initial pass at DOM API internals. r=boris,webidl,smaug

This is still fairly incomplete (i.e. no capturing, etc), but it allows
a transition to "start", and then finish (on the next frame always, for
now) or timeout, appropriately.

I think it's in a reviewable shape, given that. There's one known
divergence from the spec, which is described in
https://github.com/w3c/csswg-drafts/issues/10822

Differential Revision: https://phabricator.services.mozilla.com/D220843
This commit is contained in:
Emilio Cobos Álvarez 2024-09-04 20:24:55 +00:00
parent 7dbf811c63
commit 1b8d105218
79 changed files with 515 additions and 157 deletions

View File

@ -243,6 +243,7 @@
#include "mozilla/dom/URL.h"
#include "mozilla/dom/UseCounterMetrics.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/ViewTransition.h"
#include "mozilla/dom/WakeLockJS.h"
#include "mozilla/dom/WakeLockSentinel.h"
#include "mozilla/dom/WindowBinding.h"
@ -2586,6 +2587,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
@ -2715,6 +2717,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
@ -17782,10 +17785,49 @@ void Document::ClearStaleServoData() {
}
}
ViewTransition* Document::StartViewTransition(
const Optional<OwningNonNull<ViewTransitionUpdateCallback>>&) {
// TODO(emilio): Not yet implemented
return nullptr;
// https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
already_AddRefed<ViewTransition> Document::StartViewTransition(
const Optional<OwningNonNull<ViewTransitionUpdateCallback>>& aCallback) {
// Steps 1-3
RefPtr transition = new ViewTransition(
*this, aCallback.WasPassed() ? &aCallback.Value() : nullptr);
if (Hidden()) {
// Step 4:
//
// If document's visibility state is "hidden", then skip transition with an
// "InvalidStateError" DOMException, and return transition.
transition->SkipTransition(SkipTransitionReason::DocumentHidden);
return transition.forget();
}
if (mActiveViewTransition) {
// Step 5:
// If document's active view transition is not null, then skip that view
// transition with an "AbortError" DOMException in this's relevant Realm.
mActiveViewTransition->SkipTransition(
SkipTransitionReason::ClobberedActiveTransition);
}
// Step 6: Set document's active view transition to transition.
mActiveViewTransition = transition;
if (mPresShell) {
if (nsRefreshDriver* rd = mPresShell->GetRefreshDriver()) {
rd->EnsureViewTransitionOperationsHappen();
}
}
// Step 7: return transition
return transition.forget();
}
void Document::ClearActiveViewTransition() { mActiveViewTransition = nullptr; }
void Document::PerformPendingViewTransitionOperations() {
if (mActiveViewTransition) {
mActiveViewTransition->PerformPendingOperations();
}
EnumerateSubDocuments([](Document& aDoc) {
aDoc.PerformPendingViewTransitionOperations();
return CallState::Continue;
});
}
Selection* Document::GetSelection(ErrorResult& aRv) {

View File

@ -3818,8 +3818,13 @@ class Document : public nsINode,
MOZ_CAN_RUN_SCRIPT void
DetermineProximityToViewportAndNotifyResizeObservers();
ViewTransition* StartViewTransition(
already_AddRefed<ViewTransition> StartViewTransition(
const Optional<OwningNonNull<ViewTransitionUpdateCallback>>&);
ViewTransition* GetActiveViewTransition() const {
return mActiveViewTransition;
}
void ClearActiveViewTransition();
void PerformPendingViewTransitionOperations();
// Getter for PermissionDelegateHandler. Performs lazy initialization.
PermissionDelegateHandler* GetPermissionDelegateHandler();
@ -5359,6 +5364,9 @@ class Document : public nsINode,
RefPtr<HTMLAllCollection> mAll;
// https://drafts.csswg.org/css-view-transitions-1/#document-active-view-transition
RefPtr<ViewTransition> mActiveViewTransition;
nsTHashSet<RefPtr<WorkerDocumentListener>> mWorkerListeners;
// Pres shell resolution saved before entering fullscreen mode.

View File

@ -4,11 +4,17 @@
#include "ViewTransition.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/ViewTransitionBinding.h"
#include "nsITimer.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ViewTransition, mDocument)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ViewTransition, mDocument,
mUpdateCallback,
mUpdateCallbackDonePromise, mReadyPromise,
mFinishedPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ViewTransition)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -20,31 +26,297 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ViewTransition)
ViewTransition::ViewTransition(Document& aDoc,
ViewTransitionUpdateCallback* aCb)
: mDocument(&aDoc) {}
: mDocument(&aDoc), mUpdateCallback(aCb) {}
ViewTransition::~ViewTransition() = default;
ViewTransition::~ViewTransition() { ClearTimeoutTimer(); }
nsISupports* ViewTransition::GetParentObject() const {
return ToSupports(mDocument.get());
nsIGlobalObject* ViewTransition::GetParentObject() const {
return mDocument ? mDocument->GetParentObject() : nullptr;
}
Promise* ViewTransition::UpdateCallbackDone() {
// TODO(emilio): Not yet implemented.
return nullptr;
Promise* ViewTransition::GetUpdateCallbackDone(ErrorResult& aRv) {
if (!mUpdateCallbackDonePromise) {
mUpdateCallbackDonePromise = Promise::Create(GetParentObject(), aRv);
}
return mUpdateCallbackDonePromise;
}
Promise* ViewTransition::Ready() {
// TODO(emilio): Not yet implemented.
return nullptr;
Promise* ViewTransition::GetReady(ErrorResult& aRv) {
if (!mReadyPromise) {
mReadyPromise = Promise::Create(GetParentObject(), aRv);
}
return mReadyPromise;
}
Promise* ViewTransition::Finished() {
// TODO(emilio): Not yet implemented.
return nullptr;
Promise* ViewTransition::GetFinished(ErrorResult& aRv) {
if (!mFinishedPromise) {
mFinishedPromise = Promise::Create(GetParentObject(), aRv);
}
return mFinishedPromise;
}
void ViewTransition::SkipTransition() {
// TODO(emilio): Not yet implemented.
void ViewTransition::CallUpdateCallbackIgnoringErrors(CallIfDone aCallIfDone) {
if (aCallIfDone == CallIfDone::No && mPhase == Phase::Done) {
return;
}
CallUpdateCallback(IgnoreErrors());
}
// https://drafts.csswg.org/css-view-transitions-1/#call-the-update-callback
void ViewTransition::CallUpdateCallback(ErrorResult& aRv) {
MOZ_ASSERT(mDocument);
// Step 1: Assert: transition's phase is "done", or before
// "update-callback-called".
MOZ_ASSERT(mPhase == Phase::Done ||
UnderlyingValue(mPhase) <
UnderlyingValue(Phase::UpdateCallbackCalled));
// Step 5: If transition's phase is not "done", then set transition's phase
// to "update-callback-called".
//
// NOTE(emilio): This is swapped with the spec because the spec is broken,
// see https://github.com/w3c/csswg-drafts/issues/10822
if (mPhase != Phase::Done) {
mPhase = Phase::UpdateCallbackCalled;
}
// Step 2: Let callbackPromise be null.
RefPtr<Promise> callbackPromise;
if (!mUpdateCallback) {
// Step 3: If transition's update callback is null, then set callbackPromise
// to a promise resolved with undefined, in transitions relevant Realm.
callbackPromise =
Promise::CreateResolvedWithUndefined(GetParentObject(), aRv);
} else {
// Step 4: Otherwise set callbackPromise to the result of invoking
// transitions update callback. MOZ_KnownLive because the callback can only
// go away when we get CCd.
callbackPromise = MOZ_KnownLive(mUpdateCallback)->Call(aRv);
}
if (aRv.Failed()) {
// TODO(emilio): Do we need extra error handling here?
return;
}
MOZ_ASSERT(callbackPromise);
// Step 8: React to callbackPromise with fulfillSteps and rejectSteps.
callbackPromise->AddCallbacksWithCycleCollectedArgs(
[](JSContext*, JS::Handle<JS::Value>, ErrorResult& aRv,
ViewTransition* aVt) {
// Step 6: Let fulfillSteps be to following steps:
if (Promise* ucd = aVt->GetUpdateCallbackDone(aRv)) {
// 6.1: Resolve transition's update callback done promise with
// undefined.
ucd->MaybeResolveWithUndefined();
}
if (aVt->mPhase == Phase::Done) {
// "Skip a transition" step 8. We need to resolve "finished" after
// update-callback-done.
if (Promise* finished = aVt->GetFinished(aRv)) {
finished->MaybeResolveWithUndefined();
}
}
aVt->Activate();
},
[](JSContext*, JS::Handle<JS::Value> aReason, ErrorResult& aRv,
ViewTransition* aVt) {
// Step 7: Let rejectSteps be to following steps:
if (Promise* ucd = aVt->GetUpdateCallbackDone(aRv)) {
// 7.1: Reject transition's update callback done promise with reason.
ucd->MaybeReject(aReason);
}
// 7.2: If transition's phase is "done", then return.
if (aVt->mPhase == Phase::Done) {
// "Skip a transition" step 8. We need to resolve "finished" after
// update-callback-done.
if (Promise* finished = aVt->GetFinished(aRv)) {
finished->MaybeReject(aReason);
}
return;
}
// 7.3: Mark as handled transition's ready promise.
if (Promise* ready = aVt->GetReady(aRv)) {
MOZ_ALWAYS_TRUE(ready->SetAnyPromiseIsHandled());
}
aVt->SkipTransition(SkipTransitionReason::UpdateCallbackRejected,
aReason);
},
RefPtr(this));
// Step 9: To skip a transition after a timeout, the user agent may perform
// the following steps in parallel:
MOZ_ASSERT(!mTimeoutTimer);
ClearTimeoutTimer(); // Be safe just in case.
mTimeoutTimer = NS_NewTimer();
mTimeoutTimer->InitWithNamedFuncCallback(
TimeoutCallback, this, StaticPrefs::dom_viewTransitions_timeout_ms(),
nsITimer::TYPE_ONE_SHOT, "ViewTransition::TimeoutCallback");
}
void ViewTransition::ClearTimeoutTimer() {
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
mTimeoutTimer = nullptr;
}
}
void ViewTransition::TimeoutCallback(nsITimer* aTimer, void* aClosure) {
RefPtr vt = static_cast<ViewTransition*>(aClosure);
MOZ_DIAGNOSTIC_ASSERT(aTimer == vt->mTimeoutTimer);
vt->Timeout();
}
void ViewTransition::Timeout() {
ClearTimeoutTimer();
if (mPhase != Phase::Done && mDocument) {
SkipTransition(SkipTransitionReason::Timeout);
}
}
// https://drafts.csswg.org/css-view-transitions-1/#activate-view-transition
void ViewTransition::Activate() {
// Step 1: If transition's phase is "done", then return.
if (mPhase == Phase::Done) {
return;
}
// TODO(emilio): Steps 2-7.
// Step 8: Set transition's phase to "animating".
mPhase = Phase::Animating;
// Step 9: Resolve transition's ready promise.
if (Promise* ready = GetReady(IgnoreErrors())) {
ready->MaybeResolveWithUndefined();
}
}
// https://drafts.csswg.org/css-view-transitions/#perform-pending-transition-operations
void ViewTransition::PerformPendingOperations() {
MOZ_ASSERT(mDocument);
MOZ_ASSERT(mDocument->GetActiveViewTransition() == this);
switch (mPhase) {
case Phase::PendingCapture:
return Setup();
case Phase::Animating:
return HandleFrame();
default:
break;
}
}
// https://drafts.csswg.org/css-view-transitions/#setup-view-transition
void ViewTransition::Setup() {
// TODO(emilio): Steps 1-3: Capture old state.
//
// Step 4: Queue a global task on the DOM manipulation task source, given
// transition's relevant global object, to perform the following steps:
// 4.1: If transition's phase is "done", then abort these steps. That is
// achieved via CallIfDone::No.
// 4.2: call the update callback.
mDocument->Dispatch(NewRunnableMethod<CallIfDone>(
"ViewTransition::CallUpdateCallbackFromSetup", this,
&ViewTransition::CallUpdateCallbackIgnoringErrors, CallIfDone::No));
}
// https://drafts.csswg.org/css-view-transitions-1/#handle-transition-frame
void ViewTransition::HandleFrame() {
// TODO(emilio): Steps 1-3: Compute active animations.
bool hasActiveAnimations = false;
// Step 4: If hasActiveAnimations is false:
if (!hasActiveAnimations) {
// 4.1: Set transition's phase to "done".
mPhase = Phase::Done;
// 4.2: Clear view transition transition.
ClearActiveTransition();
// 4.3: Resolve transition's finished promise.
if (Promise* finished = GetFinished(IgnoreErrors())) {
finished->MaybeResolveWithUndefined();
}
return;
}
// TODO(emilio): Steps 5-6 (check CB size, update pseudo styles).
}
// https://drafts.csswg.org/css-view-transitions-1/#clear-view-transition
void ViewTransition::ClearActiveTransition() {
// Steps 1-2
MOZ_ASSERT(mDocument);
MOZ_ASSERT(mDocument->GetActiveViewTransition() == this);
// TODO(emilio): Step 3 (clear named elements)
// TODO(emilio): Step 4 (clear show transition tree flag)
mDocument->ClearActiveViewTransition();
}
void ViewTransition::SkipTransition(SkipTransitionReason aReason) {
SkipTransition(aReason, JS::UndefinedHandleValue);
}
// https://drafts.csswg.org/css-view-transitions-1/#skip-the-view-transition
// https://drafts.csswg.org/css-view-transitions-1/#dom-viewtransition-skiptransition
void ViewTransition::SkipTransition(
SkipTransitionReason aReason,
JS::Handle<JS::Value> aUpdateCallbackRejectReason) {
MOZ_ASSERT(mDocument);
MOZ_ASSERT_IF(aReason != SkipTransitionReason::JS, mPhase != Phase::Done);
MOZ_ASSERT_IF(aReason != SkipTransitionReason::UpdateCallbackRejected,
aUpdateCallbackRejectReason == JS::UndefinedHandleValue);
if (mPhase == Phase::Done) {
return;
}
// Step 3: If transition's phase is before "update-callback-called", then
// queue a global task on the DOM manipulation task source, given
// transitions relevant global object, to call the update callback of
// transition.
if (UnderlyingValue(mPhase) < UnderlyingValue(Phase::UpdateCallbackCalled)) {
mDocument->Dispatch(NewRunnableMethod<CallIfDone>(
"ViewTransition::CallUpdateCallbackFromSkip", this,
&ViewTransition::CallUpdateCallbackIgnoringErrors, CallIfDone::Yes));
}
// Step 4: Set rendering suppression for view transitions to false.
// TODO(emilio): We don't have that flag yet.
// Step 5: If document's active view transition is transition, Clear view
// transition transition.
if (mDocument->GetActiveViewTransition() == this) {
ClearActiveTransition();
}
// Step 6: Set transition's phase to "done".
mPhase = Phase::Done;
// Step 7: Reject transition's ready promise with reason.
if (Promise* readyPromise = GetReady(IgnoreErrors())) {
switch (aReason) {
case SkipTransitionReason::JS:
readyPromise->MaybeRejectWithAbortError(
"Skipped ViewTransition due to skipTransition() call");
break;
case SkipTransitionReason::ClobberedActiveTransition:
readyPromise->MaybeRejectWithAbortError(
"Skipped ViewTransition due to another transition starting");
break;
case SkipTransitionReason::DocumentHidden:
readyPromise->MaybeRejectWithAbortError(
"Skipped ViewTransition due to document being hidden");
break;
case SkipTransitionReason::Timeout:
readyPromise->MaybeRejectWithAbortError(
"Skipped ViewTransition due to timeout");
break;
case SkipTransitionReason::UpdateCallbackRejected:
readyPromise->MaybeReject(aUpdateCallbackRejectReason);
break;
}
}
// Step 8: Resolve transition's finished promise with the result of reacting
// to transition's update callback done promise.
//
// This is done in CallUpdateCallback()
}
JSObject* ViewTransition::WrapObject(JSContext* aCx,

View File

@ -7,32 +7,85 @@
#include "nsWrapperCache.h"
namespace mozilla::dom {
class nsIGlobalObject;
class nsITimer;
namespace mozilla {
class ErrorResult;
namespace dom {
class Promise;
class Document;
class Promise;
class ViewTransitionUpdateCallback;
enum class SkipTransitionReason : uint8_t {
JS,
DocumentHidden,
ClobberedActiveTransition,
Timeout,
UpdateCallbackRejected,
};
// https://drafts.csswg.org/css-view-transitions-1/#viewtransition-phase
enum class ViewTransitionPhase : uint8_t {
PendingCapture = 0,
UpdateCallbackCalled,
Animating,
Done,
};
class ViewTransition final : public nsISupports, public nsWrapperCache {
public:
using Phase = ViewTransitionPhase;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ViewTransition)
ViewTransition(Document&, ViewTransitionUpdateCallback*);
Promise* UpdateCallbackDone();
Promise* Ready();
Promise* Finished();
void SkipTransition();
Promise* GetUpdateCallbackDone(ErrorResult&);
Promise* GetReady(ErrorResult&);
Promise* GetFinished(ErrorResult&);
nsISupports* GetParentObject() const;
void SkipTransition(SkipTransitionReason = SkipTransitionReason::JS);
void PerformPendingOperations();
nsIGlobalObject* GetParentObject() const;
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
private:
enum class CallIfDone : bool { No, Yes };
MOZ_CAN_RUN_SCRIPT void CallUpdateCallbackIgnoringErrors(CallIfDone);
MOZ_CAN_RUN_SCRIPT void CallUpdateCallback(ErrorResult&);
void Activate();
void ClearActiveTransition();
void Timeout();
void Setup();
void HandleFrame();
void SkipTransition(SkipTransitionReason, JS::Handle<JS::Value>);
void ClearTimeoutTimer();
~ViewTransition();
// Stored for the whole lifetime of the object (until CC).
RefPtr<Document> mDocument;
RefPtr<ViewTransitionUpdateCallback> mUpdateCallback;
// Allocated lazily, but same object once allocated (again until CC).
RefPtr<Promise> mUpdateCallbackDonePromise;
RefPtr<Promise> mReadyPromise;
RefPtr<Promise> mFinishedPromise;
static void TimeoutCallback(nsITimer*, void*);
RefPtr<nsITimer> mTimeoutTimer;
Phase mPhase = Phase::PendingCapture;
};
} // namespace mozilla::dom
} // namespace dom
} // namespace mozilla
#endif

View File

@ -7,8 +7,8 @@
[Exposed=Window, Pref="dom.viewTransitions.enabled"]
interface ViewTransition {
readonly attribute Promise<undefined> updateCallbackDone;
readonly attribute Promise<undefined> ready;
readonly attribute Promise<undefined> finished;
[Throws] readonly attribute Promise<undefined> updateCallbackDone;
[Throws] readonly attribute Promise<undefined> ready;
[Throws] readonly attribute Promise<undefined> finished;
undefined skipTransition();
};

View File

@ -1364,6 +1364,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
mResizeSuppressed(false),
mNeedToUpdateIntersectionObservations(false),
mNeedToUpdateResizeObservers(false),
mNeedToUpdateViewTransitions(false),
mNeedToRunFrameRequestCallbacks(false),
mNeedToUpdateAnimations(false),
mMightNeedMediaQueryListenerUpdate(false),
@ -1965,6 +1966,9 @@ auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons {
if (mNeedToUpdateResizeObservers) {
reasons |= TickReasons::eNeedsToNotifyResizeObservers;
}
if (mNeedToUpdateViewTransitions) {
reasons |= TickReasons::eNeedsToUpdateViewTransitions;
}
if (mNeedToUpdateAnimations) {
reasons |= TickReasons::eNeedsToUpdateAnimations;
}
@ -2014,6 +2018,9 @@ void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons,
if (aReasons & TickReasons::eNeedsToNotifyResizeObservers) {
aStr.AppendLiteral(" NeedsToNotifyResizeObservers");
}
if (aReasons & TickReasons::eNeedsToUpdateViewTransitions) {
aStr.AppendLiteral(" NeedsToUpdateViewTransitions");
}
if (aReasons & TickReasons::eNeedsToUpdateAnimations) {
aStr.AppendLiteral(" NeedsToUpdateAnimations");
}
@ -2236,6 +2243,15 @@ void nsRefreshDriver::RunFullscreenSteps() {
}
}
void nsRefreshDriver::PerformPendingViewTransitionOperations() {
if (!mNeedToUpdateViewTransitions) {
return;
}
mNeedToUpdateViewTransitions = false;
AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("View Transitions", LAYOUT);
mPresContext->Document()->PerformPendingViewTransitionOperations();
}
void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime) {
AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Compute intersections", LAYOUT);
mPresContext->Document()->UpdateIntersections(aNowTime);
@ -2799,7 +2815,13 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
return StopTimer();
}
// Step 17. For each doc of docs, run the update intersection observations
// TODO(emilio): Step 17, focus fix-up should happen here.
// Step 18: For each doc of docs, perform pending transition operations for
// doc.
PerformPendingViewTransitionOperations();
// Step 19. For each doc of docs, run the update intersection observations
// steps for doc.
UpdateIntersectionObservations(aNowTime);

View File

@ -406,6 +406,11 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
mNeedToUpdateResizeObservers = true;
}
void EnsureViewTransitionOperationsHappen() {
EnsureTimerStarted();
mNeedToUpdateViewTransitions = true;
}
void EnsureAnimationUpdate() {
EnsureTimerStarted();
mNeedToUpdateAnimations = true;
@ -442,6 +447,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
eRootNeedsMoreTicksForUserInput = 1 << 9,
eNeedsToUpdateAnimations = 1 << 10,
eNeedsToRunFrameRequestCallbacks = 1 << 11,
eNeedsToUpdateViewTransitions = 1 << 12,
};
void AddForceNotifyContentfulPaintPresContext(nsPresContext* aPresContext);
@ -495,6 +501,7 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
mozilla::TimeStamp aNowTime);
void UpdateIntersectionObservations(mozilla::TimeStamp aNowTime);
void UpdateRelevancyOfContentVisibilityAutoFrames();
void PerformPendingViewTransitionOperations();
MOZ_CAN_RUN_SCRIPT void
DetermineProximityToViewportAndNotifyResizeObservers();
void MaybeIncreaseMeasuredTicksSinceLoading();
@ -638,10 +645,13 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
// all our documents.
bool mNeedToUpdateIntersectionObservations : 1;
// True if we need to flush in order to update intersection observations in
// all our documents.
// True if we need to flush in order to update resize observations in all
// our documents.
bool mNeedToUpdateResizeObservers : 1;
// True if we may need to perform pending view transition operations.
bool mNeedToUpdateViewTransitions : 1;
// True if we may need to run any frame callback.
bool mNeedToRunFrameRequestCallbacks : 1;

View File

@ -63,6 +63,7 @@ struct EnumTypeFitsWithin
/**
* Get the underlying value of an enum, but typesafe.
* TODO: Replace with std::to_underlying when available.
*
* example:
*

View File

@ -4460,6 +4460,14 @@
mirror: always
rust: true
# Timeout for view transitions.
# TODO(emilio): Figure out the right time-out, Blink uses between 4 and 15
# seconds.
- name: dom.viewTransitions.timeout-ms
type: uint32_t
value: 10000
mirror: always
# Is support for WebVR APIs enabled?
# Disabled everywhere, but not removed.
- name: dom.vr.enabled

View File

@ -1,2 +0,0 @@
[active-view-transition-on-non-root.html]
expected: FAIL

View File

@ -1,2 +1,2 @@
[active-view-transition-type-on-non-root.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[backdrop-filter-captured.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[dialog-in-rtl-iframe.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[document-element-detached-crash.html]
expected: TIMEOUT

View File

@ -1,3 +1,4 @@
[duplicate-tag-rejects-capture.html]
expected: [ERROR, OK]
[Two different elements with the same name in the old DOM should skip the transition]
expected: FAIL

View File

@ -1,2 +1,2 @@
[element-stops-grouping-after-animation.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,3 +1,4 @@
[event-pseudo-name.html]
expected: TIMEOUT
[verifies pseudo name includes a tag]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[fractional-translation-from-position.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[fractional-translation-from-transform.html]
expected: FAIL

View File

@ -1,2 +1,2 @@
[fragmented-during-transition-skips.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[hit-test-unpainted-element.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[hit-test-unrelated-element.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[iframe-new-has-scrollbar.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[iframe-old-has-scrollbar.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[iframe-transition-destroyed-document-crash.html]
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[iframe-transition.sub.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[list-style-position-style-change-crash.html]
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[massive-element-below-viewport-partially-onscreen-new.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[massive-element-below-viewport-partially-onscreen-old.html]
expected: FAIL

View File

@ -1,2 +1,5 @@
[massive-element-left-of-viewport-partially-onscreen-new.html]
expected: FAIL
# Might need meta viewport.
expected:
if os == "android": FAIL
PASS

View File

@ -1,2 +1,5 @@
[massive-element-left-of-viewport-partially-onscreen-old.html]
expected: FAIL
# Might need meta viewport.
expected:
if os == "android": FAIL
PASS

View File

@ -1,2 +0,0 @@
[massive-element-right-of-viewport-partially-onscreen-new.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[massive-element-right-of-viewport-partially-onscreen-old.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[named-element-with-fix-pos-child-new.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[named-element-with-fix-pos-child-old.html]
expected: FAIL

View File

@ -1,4 +1,5 @@
[pageswap-ctor.html]
expected: ERROR
[Constructing pageswap event]
expected: FAIL

View File

@ -1,2 +0,0 @@
[new-and-old-sizes-match.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[new-content-from-root-display-none.html]
expected: FAIL

View File

@ -1,2 +1,5 @@
[new-content-has-scrollbars.html]
expected: FAIL
# Might depend on classic (non-overlay) scrollbars
expected:
if os == "android": FAIL
PASS

View File

@ -1,3 +1,2 @@
[no-crash-set-exception.html]
[An exception thrown during a transition shouldn't crash.]
expected: FAIL
expected: [OK, ERROR]

View File

@ -1,3 +1,4 @@
[no-crash-view-transition-in-massive-iframe.html]
expected: TIMEOUT
[startViewTransition in massive iframe shouldn't crash.]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +0,0 @@
[no-root-capture.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[no-white-flash-before-activation.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[nothing-captured.html]
expected: FAIL

View File

@ -1,5 +0,0 @@
[only-child-on-root-element-with-view-transition.html]
expected:
if not fission and (os == "linux") and debug: [OK, CRASH]
[:only-child is not supported on view-transition]
expected: FAIL

View File

@ -1,3 +0,0 @@
[only-child-view-transition.html]
[:only-child is not supported on view-transition]
expected: FAIL

View File

@ -1,3 +0,0 @@
[paused-animation-at-end.html]
[view transition is not over if animations are paused]
expected: FAIL

View File

@ -1,5 +1,6 @@
[pseudo-computed-style-stays-in-sync-with-new-element.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
ERROR
[computed style on pseudo-element stays in sync with the DOM element]
expected: FAIL
expected: TIMEOUT

View File

@ -1,9 +1,10 @@
[pseudo-get-computed-style.html]
expected: ERROR
[position property of pseudo elements]
expected: FAIL
expected: TIMEOUT
[properties of pseudo elements outside of transition]
expected: FAIL
[properties of pseudo elements outside of transition]
expected: FAIL
expected: NOTRUN

View File

@ -1,5 +0,0 @@
[ready_resolves_after_dom_before_raf.html]
expected:
if not fission and (os == "linux") and debug: [OK, CRASH]
[updateCallbackDone resolves, then ready resolves with no rAF in between]
expected: FAIL

View File

@ -1,2 +0,0 @@
[rotated-cat-off-top-edge.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[rtl-with-scrollbar.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[scroller-child-abspos.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[scroller-child.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[scroller.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[shadow-part-with-name-overridden-by-important.html]
expected: FAIL

View File

@ -1,5 +1,6 @@
[style-inheritance.html]
expected:
if not fission and (os == "linux") and debug: [OK, CRASH]
ERROR
[style inheritance of pseudo elements]
expected: FAIL
expected: TIMEOUT

View File

@ -1,3 +1,2 @@
[synchronous-callback-skipped-before-run.html]
[finished promise should be resolved if skipTransition() is invoked before a synchronous updateCallbackDone callback is dispatched]
expected: FAIL
expected: ERROR

View File

@ -1,2 +0,0 @@
[transition-in-empty-iframe.html]
expected: FAIL

View File

@ -1,9 +1,10 @@
[transition-in-hidden-page.html]
expected: [TIMEOUT, ERROR]
[A view transition should be immediately skipped if started when document is hidden]
expected: FAIL
[A view transition should be skipped when a document becomes hidden while processing update callback]
expected: FAIL
expected: TIMEOUT
[A view transition should be skipped when a document becomes hidden while animating]
expected: FAIL
expected: NOTRUN

View File

@ -1,5 +1,4 @@
[transition-skipped-after-animation-started.html]
expected:
if (os == "android") and fission: [TIMEOUT, OK]
expected: TIMEOUT
[skipTransition() after animations have started running should resolve finished promise]
expected: FAIL
expected: TIMEOUT

View File

@ -1,5 +1,4 @@
[transition-skipped-from-invalid-callback.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
expected: TIMEOUT
[transition skipped because callback has invalid syntax]
expected: FAIL
expected: TIMEOUT

View File

@ -1,5 +1,4 @@
[unset-and-initial-view-transition-name.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
expected: TIMEOUT
[validates that view-transition-name: unset or initial are ignored]
expected: FAIL
expected: TIMEOUT

View File

@ -1,3 +1,4 @@
[view-transition-name-on-removed-element.html]
expected: TIMEOUT
[view-transition-name on an element removed by script should not be visited when discovering named elements]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-name-removed-mid-transition.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-match-early-mutation.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-match-early.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-match-late-mutation.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-matches.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,4 +1,5 @@
[view-transition-types-mutable.html]
expected: ERROR
[ViewTransition.types is a ViewTransitionTypeSet]
expected: FAIL

View File

@ -1,2 +1,2 @@
[view-transition-types-removed.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-reserved-mutation.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-reserved.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[view-transition-types-stay.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,3 +1,4 @@
[web-animation-pseudo-incorrect-name.html]
expected: ERROR
[animation created with incorrect name]
expected: FAIL
expected: TIMEOUT

View File

@ -1,17 +1,17 @@
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group(first )]
expected: FAIL
expected: TIMEOUT
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group(first)]
expected: FAIL
expected: TIMEOUT
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group( first )]
expected: FAIL
expected: TIMEOUT
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group(first]
expected: FAIL
expected: TIMEOUT
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group( first]
expected: FAIL
expected: TIMEOUT
[web-animations-api-parse-pseudo-argument.html?first-pseudo=::view-transition-group( first)]
expected: FAIL
expected: TIMEOUT

View File

@ -1,2 +1,2 @@
[web-animations-api.html]
expected: FAIL
expected: TIMEOUT

View File

@ -1,3 +1,4 @@
[window-resize-aborts-transition.html]
expected: [ERROR, TIMEOUT]
[View transitions: Resizing viewport skips the transition]
expected: FAIL
expected: TIMEOUT