mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
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:
parent
7dbf811c63
commit
1b8d105218
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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 transition’s relevant Realm.
|
||||
callbackPromise =
|
||||
Promise::CreateResolvedWithUndefined(GetParentObject(), aRv);
|
||||
} else {
|
||||
// Step 4: Otherwise set callbackPromise to the result of invoking
|
||||
// transition’s 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
|
||||
// transition’s 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,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -63,6 +63,7 @@ struct EnumTypeFitsWithin
|
||||
|
||||
/**
|
||||
* Get the underlying value of an enum, but typesafe.
|
||||
* TODO: Replace with std::to_underlying when available.
|
||||
*
|
||||
* example:
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
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]
|
||||
expected: [ERROR, OK]
|
||||
[Two different elements with the same name in the old DOM should skip the transition]
|
||||
expected: FAIL
|
||||
|
@ -1,2 +1,2 @@
|
||||
[element-stops-grouping-after-animation.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,3 +1,4 @@
|
||||
[event-pseudo-name.html]
|
||||
expected: TIMEOUT
|
||||
[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]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[hit-test-unpainted-element.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[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]
|
||||
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]
|
||||
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]
|
||||
expected: ERROR
|
||||
[Constructing pageswap event]
|
||||
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]
|
||||
expected: FAIL
|
||||
# Might depend on classic (non-overlay) scrollbars
|
||||
expected:
|
||||
if os == "android": FAIL
|
||||
PASS
|
||||
|
@ -1,3 +1,2 @@
|
||||
[no-crash-set-exception.html]
|
||||
[An exception thrown during a transition shouldn't crash.]
|
||||
expected: FAIL
|
||||
expected: [OK, ERROR]
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
expected:
|
||||
if not fission and (os == "linux") and debug: [OK, CRASH]
|
||||
ERROR
|
||||
[style inheritance of pseudo elements]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -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
|
||||
|
@ -1,2 +0,0 @@
|
||||
[transition-in-empty-iframe.html]
|
||||
expected: FAIL
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-name-removed-mid-transition.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-match-early-mutation.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-match-early.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-match-late-mutation.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-matches.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,4 +1,5 @@
|
||||
[view-transition-types-mutable.html]
|
||||
expected: ERROR
|
||||
[ViewTransition.types is a ViewTransitionTypeSet]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-removed.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-reserved-mutation.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-reserved.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,2 +1,2 @@
|
||||
[view-transition-types-stay.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,3 +1,4 @@
|
||||
[web-animation-pseudo-incorrect-name.html]
|
||||
expected: ERROR
|
||||
[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 )]
|
||||
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
|
||||
|
@ -1,2 +1,2 @@
|
||||
[web-animations-api.html]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
@ -1,3 +1,4 @@
|
||||
[window-resize-aborts-transition.html]
|
||||
expected: [ERROR, TIMEOUT]
|
||||
[View transitions: Resizing viewport skips the transition]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
Loading…
Reference in New Issue
Block a user