/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_ViewTransition_h #define mozilla_dom_ViewTransition_h #include "mozilla/Attributes.h" #include "mozilla/layers/IpcResourceUpdateQueue.h" #include "nsAtomHashKeys.h" #include "nsClassHashtable.h" #include "nsRect.h" #include "nsRefPtrHashtable.h" #include "nsWrapperCache.h" class nsIGlobalObject; class nsITimer; namespace mozilla { class ErrorResult; struct Keyframe; struct PseudoStyleRequest; struct StyleLockedDeclarationBlock; namespace layers { class RenderRootStateManager; } namespace wr { struct ImageKey; class IpcResourceUpdateQueue; } // namespace wr namespace dom { extern LazyLogModule gViewTransitionsLog; #define VT_LOG(...) \ MOZ_LOG(mozilla::dom::gViewTransitionsLog, mozilla::LogLevel::Debug, \ (__VA_ARGS__)) #ifdef DEBUG # define VT_LOG_DEBUG(...) VT_LOG(__VA_ARGS__) #else # define VT_LOG_DEBUG(...) #endif class Document; class Element; class Promise; class ViewTransitionUpdateCallback; enum class SkipTransitionReason : uint8_t { JS, DocumentHidden, RootRemoved, ClobberedActiveTransition, Timeout, UpdateCallbackRejected, DuplicateTransitionNameCapturingOldState, DuplicateTransitionNameCapturingNewState, PseudoUpdateFailure, Resize, PageSwap, // Can happen due to various recoverable internal errors such as GPU process // crashes or GPU device resets. ResetRendering, }; // 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* GetUpdateCallbackDone(ErrorResult&); Promise* GetReady(ErrorResult&); Promise* GetFinished(ErrorResult&); void SkipTransition(SkipTransitionReason = SkipTransitionReason::JS); MOZ_CAN_RUN_SCRIPT void PerformPendingOperations(); // Get the snapshot containing block, which is the top-layer for rendering the // view transition tree. Element* GetSnapshotContainingBlock() const { return mSnapshotContainingBlock; } // Get ::view-transition pseudo element, which is the view transition tree // root. We find the pseudo element of this tree from this node. Element* GetViewTransitionTreeRoot() const; // Get all currently captured element frames if accessible. void GetCapturedFrames(nsTArray& aCapturedFrames) const; Maybe GetOldInkOverflowRect(nsAtom* aName) const; Maybe GetNewInkOverflowRect(nsAtom* aName) const; Maybe GetOldBorderBoxSize(nsAtom* aName) const; Maybe GetNewBorderBoxSize(nsAtom* aName) const; Maybe GetOldActiveRect(nsAtom* aName) const; Maybe GetNewActiveRect(nsAtom* aName) const; // Use this to generate the old state image key for use in a stacking context. // Do not use the returned image key in an image display item, use // ReadOldImageKey instead. const wr::ImageKey* GetOrCreateOldImageKey(nsAtom* aName, layers::RenderRootStateManager*, wr::IpcResourceUpdateQueue&) const; // Use this to get the already-created old state image key for use in an image // display item. // This marks the old state image key as used which influences the how eagerly // it can be deleted. const wr::ImageKey* ReadOldImageKey(nsAtom* aName, layers::RenderRootStateManager*, wr::IpcResourceUpdateQueue&) const; const wr::ImageKey* GetNewImageKey(nsAtom* aName) const; const wr::ImageKey* GetImageKeyForCapturedFrame( nsIFrame* aFrame, layers::RenderRootStateManager*, wr::IpcResourceUpdateQueue&) const; void UpdateActiveRectForCapturedFrame( nsIFrame* capturedFrame, const gfx::MatrixScales& aInheritedScale, nsRect& aOutCapturedRect); Element* FindPseudo(const PseudoStyleRequest&) const; const StyleLockedDeclarationBlock* GetDynamicRuleFor(const Element&) const; static constexpr nsLiteralString kGroupAnimPrefix = u"-ua-view-transition-group-anim-"_ns; [[nodiscard]] bool GetGroupKeyframes(nsAtom* aAnimationName, const StyleComputedTimingFunction&, nsTArray&); bool MatchClassList(nsAtom*, const nsTArray&) const; nsIGlobalObject* GetParentObject() const; JSObject* WrapObject(JSContext*, JS::Handle aGivenProto) override; struct CapturedElement; static nsRect SnapshotContainingBlockRect(nsPresContext*); static nsRect CapturedInkOverflowRectForFrame(nsIFrame*, bool aIsRoot); MOZ_CAN_RUN_SCRIPT void CallUpdateCallback(ErrorResult&); private: MOZ_CAN_RUN_SCRIPT void MaybeScheduleUpdateCallback(); void Activate(); void ClearActiveTransition(bool aIsDocumentHidden); void Timeout(); MOZ_CAN_RUN_SCRIPT void Setup(); [[nodiscard]] MOZ_CAN_RUN_SCRIPT Maybe CaptureOldState(); [[nodiscard]] Maybe CaptureNewState(); void SetupTransitionPseudoElements(); [[nodiscard]] bool UpdatePseudoElementStyles(bool aNeedsInvalidation); void ClearNamedElements(); void HandleFrame(); bool CheckForActiveAnimations() const; void SkipTransition(SkipTransitionReason, JS::Handle); void ClearTimeoutTimer(); nsRect SnapshotContainingBlockRect() const; Maybe GetElementIdentifier(Element* aElement) const; uint64_t EnsureElementIdentifier(Element* aElement); already_AddRefed DocumentScopedTransitionNameFor(nsIFrame* aFrame); ~ViewTransition(); // Stored for the whole lifetime of the object (until CC). RefPtr mDocument; RefPtr mUpdateCallback; // https://drafts.csswg.org/css-view-transitions/#viewtransition-named-elements using NamedElements = nsClassHashtable; NamedElements mNamedElements; // mNamedElements is an unordered map, we need to keep the tree order. This // also keeps the strong reference to the view-transition-name which may be // auto-generated for this view transition. AutoTArray, 8> mNames; using OldCaptureFramesArray = AutoTArray>, 32>; // Short lived array pointer used to mark old captures for DL building. OldCaptureFramesArray* mOldCaptureElements = nullptr; // The element identifier for the elements which need the auto-generated // view-transition-name. The lifetime of those element identifiers is // element’s node document’s active view transition. // Note: Use a non-owning element pointer because we never dereference it. // It is just a key to map an id. The size of this hashmap is fixed after we // capture the old state and new state, using ElementIdentifiers = nsTHashMap; ElementIdentifiers mElementIdentifiers; // https://drafts.csswg.org/css-view-transitions/#viewtransition-initial-snapshot-containing-block-size nsSize mInitialSnapshotContainingBlockSize; // Allocated lazily, but same object once allocated (again until CC). RefPtr mUpdateCallbackDonePromise; RefPtr mReadyPromise; RefPtr mFinishedPromise; static void TimeoutCallback(nsITimer*, void*); RefPtr mTimeoutTimer; Phase mPhase = Phase::PendingCapture; // The wrapper of the pseudo-elements tree, to make sure it is always // out-of-flow. This is the top-layer for rendering the view transition tree. // So in general, its child (and only one) is the transition root // pseudo-element. RefPtr mSnapshotContainingBlock; }; } // namespace dom } // namespace mozilla #endif