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
Родитель 7dbf811c63
Коммит 1b8d105218
79 изменённых файлов: 515 добавлений и 157 удалений

Просмотреть файл

@ -243,6 +243,7 @@
#include "mozilla/dom/URL.h" #include "mozilla/dom/URL.h"
#include "mozilla/dom/UseCounterMetrics.h" #include "mozilla/dom/UseCounterMetrics.h"
#include "mozilla/dom/UserActivation.h" #include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/ViewTransition.h"
#include "mozilla/dom/WakeLockJS.h" #include "mozilla/dom/WakeLockJS.h"
#include "mozilla/dom/WakeLockSentinel.h" #include "mozilla/dom/WakeLockSentinel.h"
#include "mozilla/dom/WindowBinding.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(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager) 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(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
@ -17782,10 +17785,49 @@ void Document::ClearStaleServoData() {
} }
} }
ViewTransition* Document::StartViewTransition( // https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
const Optional<OwningNonNull<ViewTransitionUpdateCallback>>&) { already_AddRefed<ViewTransition> Document::StartViewTransition(
// TODO(emilio): Not yet implemented const Optional<OwningNonNull<ViewTransitionUpdateCallback>>& aCallback) {
return nullptr; // 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) { Selection* Document::GetSelection(ErrorResult& aRv) {

Просмотреть файл

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

Просмотреть файл

@ -4,11 +4,17 @@
#include "ViewTransition.h" #include "ViewTransition.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/ViewTransitionBinding.h" #include "mozilla/dom/ViewTransitionBinding.h"
#include "nsITimer.h"
namespace mozilla::dom { 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_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ViewTransition)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -20,31 +26,297 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ViewTransition)
ViewTransition::ViewTransition(Document& aDoc, ViewTransition::ViewTransition(Document& aDoc,
ViewTransitionUpdateCallback* aCb) ViewTransitionUpdateCallback* aCb)
: mDocument(&aDoc) {} : mDocument(&aDoc), mUpdateCallback(aCb) {}
ViewTransition::~ViewTransition() = default; ViewTransition::~ViewTransition() { ClearTimeoutTimer(); }
nsISupports* ViewTransition::GetParentObject() const { nsIGlobalObject* ViewTransition::GetParentObject() const {
return ToSupports(mDocument.get()); return mDocument ? mDocument->GetParentObject() : nullptr;
} }
Promise* ViewTransition::UpdateCallbackDone() { Promise* ViewTransition::GetUpdateCallbackDone(ErrorResult& aRv) {
// TODO(emilio): Not yet implemented. if (!mUpdateCallbackDonePromise) {
return nullptr; mUpdateCallbackDonePromise = Promise::Create(GetParentObject(), aRv);
}
return mUpdateCallbackDonePromise;
} }
Promise* ViewTransition::Ready() { Promise* ViewTransition::GetReady(ErrorResult& aRv) {
// TODO(emilio): Not yet implemented. if (!mReadyPromise) {
return nullptr; mReadyPromise = Promise::Create(GetParentObject(), aRv);
}
return mReadyPromise;
} }
Promise* ViewTransition::Finished() { Promise* ViewTransition::GetFinished(ErrorResult& aRv) {
// TODO(emilio): Not yet implemented. if (!mFinishedPromise) {
return nullptr; mFinishedPromise = Promise::Create(GetParentObject(), aRv);
}
return mFinishedPromise;
} }
void ViewTransition::SkipTransition() { void ViewTransition::CallUpdateCallbackIgnoringErrors(CallIfDone aCallIfDone) {
// TODO(emilio): Not yet implemented. 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, JSObject* ViewTransition::WrapObject(JSContext* aCx,

Просмотреть файл

@ -7,32 +7,85 @@
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
namespace mozilla::dom { class nsIGlobalObject;
class nsITimer;
namespace mozilla {
class ErrorResult;
namespace dom {
class Promise;
class Document; class Document;
class Promise;
class ViewTransitionUpdateCallback; 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 { class ViewTransition final : public nsISupports, public nsWrapperCache {
public: public:
using Phase = ViewTransitionPhase;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ViewTransition) NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ViewTransition)
ViewTransition(Document&, ViewTransitionUpdateCallback*); ViewTransition(Document&, ViewTransitionUpdateCallback*);
Promise* UpdateCallbackDone(); Promise* GetUpdateCallbackDone(ErrorResult&);
Promise* Ready(); Promise* GetReady(ErrorResult&);
Promise* Finished(); Promise* GetFinished(ErrorResult&);
void SkipTransition();
nsISupports* GetParentObject() const; void SkipTransition(SkipTransitionReason = SkipTransitionReason::JS);
void PerformPendingOperations();
nsIGlobalObject* GetParentObject() const;
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override; JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
private: 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(); ~ViewTransition();
// Stored for the whole lifetime of the object (until CC).
RefPtr<Document> mDocument; 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 #endif

Просмотреть файл

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

Просмотреть файл

@ -1364,6 +1364,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
mResizeSuppressed(false), mResizeSuppressed(false),
mNeedToUpdateIntersectionObservations(false), mNeedToUpdateIntersectionObservations(false),
mNeedToUpdateResizeObservers(false), mNeedToUpdateResizeObservers(false),
mNeedToUpdateViewTransitions(false),
mNeedToRunFrameRequestCallbacks(false), mNeedToRunFrameRequestCallbacks(false),
mNeedToUpdateAnimations(false), mNeedToUpdateAnimations(false),
mMightNeedMediaQueryListenerUpdate(false), mMightNeedMediaQueryListenerUpdate(false),
@ -1965,6 +1966,9 @@ auto nsRefreshDriver::GetReasonsToTick() const -> TickReasons {
if (mNeedToUpdateResizeObservers) { if (mNeedToUpdateResizeObservers) {
reasons |= TickReasons::eNeedsToNotifyResizeObservers; reasons |= TickReasons::eNeedsToNotifyResizeObservers;
} }
if (mNeedToUpdateViewTransitions) {
reasons |= TickReasons::eNeedsToUpdateViewTransitions;
}
if (mNeedToUpdateAnimations) { if (mNeedToUpdateAnimations) {
reasons |= TickReasons::eNeedsToUpdateAnimations; reasons |= TickReasons::eNeedsToUpdateAnimations;
} }
@ -2014,6 +2018,9 @@ void nsRefreshDriver::AppendTickReasonsToString(TickReasons aReasons,
if (aReasons & TickReasons::eNeedsToNotifyResizeObservers) { if (aReasons & TickReasons::eNeedsToNotifyResizeObservers) {
aStr.AppendLiteral(" NeedsToNotifyResizeObservers"); aStr.AppendLiteral(" NeedsToNotifyResizeObservers");
} }
if (aReasons & TickReasons::eNeedsToUpdateViewTransitions) {
aStr.AppendLiteral(" NeedsToUpdateViewTransitions");
}
if (aReasons & TickReasons::eNeedsToUpdateAnimations) { if (aReasons & TickReasons::eNeedsToUpdateAnimations) {
aStr.AppendLiteral(" NeedsToUpdateAnimations"); 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) { void nsRefreshDriver::UpdateIntersectionObservations(TimeStamp aNowTime) {
AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Compute intersections", LAYOUT); AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Compute intersections", LAYOUT);
mPresContext->Document()->UpdateIntersections(aNowTime); mPresContext->Document()->UpdateIntersections(aNowTime);
@ -2799,7 +2815,13 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
return StopTimer(); 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. // steps for doc.
UpdateIntersectionObservations(aNowTime); UpdateIntersectionObservations(aNowTime);

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -4460,6 +4460,14 @@
mirror: always mirror: always
rust: true 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? # Is support for WebVR APIs enabled?
# Disabled everywhere, but not removed. # Disabled everywhere, but not removed.
- name: dom.vr.enabled - name: dom.vr.enabled

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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