DOMIntersectionObserver.h (8575B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef DOMIntersectionObserver_h 8 #define DOMIntersectionObserver_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/ServoStyleConsts.h" 12 #include "mozilla/Variant.h" 13 #include "mozilla/dom/IntersectionObserverBinding.h" 14 #include "nsDOMNavigationTiming.h" 15 #include "nsTArray.h" 16 #include "nsTHashSet.h" 17 18 namespace mozilla::dom { 19 20 class DOMIntersectionObserver; 21 22 class DOMIntersectionObserverEntry final : public nsISupports, 23 public nsWrapperCache { 24 ~DOMIntersectionObserverEntry() = default; 25 26 public: 27 DOMIntersectionObserverEntry(nsISupports* aOwner, DOMHighResTimeStamp aTime, 28 RefPtr<DOMRect> aRootBounds, 29 RefPtr<DOMRect> aBoundingClientRect, 30 RefPtr<DOMRect> aIntersectionRect, 31 bool aIsIntersecting, Element* aTarget, 32 double aIntersectionRatio) 33 : mOwner(aOwner), 34 mTime(aTime), 35 mRootBounds(std::move(aRootBounds)), 36 mBoundingClientRect(std::move(aBoundingClientRect)), 37 mIntersectionRect(std::move(aIntersectionRect)), 38 mIsIntersecting(aIsIntersecting), 39 mTarget(aTarget), 40 mIntersectionRatio(aIntersectionRatio) {} 41 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 42 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(DOMIntersectionObserverEntry) 43 44 nsISupports* GetParentObject() const { return mOwner; } 45 46 JSObject* WrapObject(JSContext* aCx, 47 JS::Handle<JSObject*> aGivenProto) override { 48 return IntersectionObserverEntry_Binding::Wrap(aCx, this, aGivenProto); 49 } 50 51 DOMHighResTimeStamp Time() const { return mTime; } 52 53 DOMRect* GetRootBounds() { return mRootBounds; } 54 55 DOMRect* BoundingClientRect() { return mBoundingClientRect; } 56 57 DOMRect* IntersectionRect() { return mIntersectionRect; } 58 59 bool IsIntersecting() const { return mIsIntersecting; } 60 61 double IntersectionRatio() const { return mIntersectionRatio; } 62 63 Element* Target() { return mTarget; } 64 65 protected: 66 nsCOMPtr<nsISupports> mOwner; 67 DOMHighResTimeStamp mTime; 68 RefPtr<DOMRect> mRootBounds; 69 RefPtr<DOMRect> mBoundingClientRect; 70 RefPtr<DOMRect> mIntersectionRect; 71 bool mIsIntersecting; 72 RefPtr<Element> mTarget; 73 double mIntersectionRatio; 74 }; 75 76 #define NS_DOM_INTERSECTION_OBSERVER_IID \ 77 {0x8570a575, 0xe303, 0x4d18, {0xb6, 0xb1, 0x4d, 0x2b, 0x49, 0xd8, 0xef, 0x94}} 78 79 using IntersectionObserverMargin = StyleRect<LengthPercentage>; 80 81 // An input suitable to compute intersections with multiple targets. 82 struct IntersectionInput { 83 // Whether the root is implicit (null, originally). 84 const bool mIsImplicitRoot = false; 85 // The computed root node. For the implicit root, this will be the in-process 86 // root document we can compute coordinates against (along with the remote 87 // document visible rect if appropriate). 88 const nsINode* mRootNode = nullptr; 89 nsIFrame* mRootFrame = nullptr; 90 // The rect of mRootFrame in client coordinates. 91 nsRect mRootRect; 92 // The root margin computed against the root rect. 93 nsMargin mRootMargin; 94 // The scroll margin computed against the root rect. 95 IntersectionObserverMargin mScrollMargin; 96 // If this is in an OOP iframe, the visible rect of the OOP frame. 97 Maybe<nsRect> mRemoteDocumentVisibleRect; 98 }; 99 100 struct IntersectionOutput { 101 const bool mIsSimilarOrigin; 102 const nsRect mRootBounds; 103 const nsRect mTargetRect; 104 const Maybe<nsRect> mIntersectionRect; 105 // See aPreservesAxisAlignedRectangles of 106 // nsLayoutUtils::TransformFrameRectToAncestor(). 107 // https://searchfox.org/firefox-main/rev/e2cbda2dd0f622553b5c825f319832db4863f6a4/layout/base/nsLayoutUtils.h#829-830 108 const bool mPreservesAxisAlignedRectangles; 109 110 bool Intersects() const { return mIntersectionRect.isSome(); } 111 }; 112 113 class DOMIntersectionObserver final : public nsISupports, 114 public nsWrapperCache { 115 virtual ~DOMIntersectionObserver() { Disconnect(); } 116 117 using NativeCallback = void (*)( 118 const Sequence<OwningNonNull<DOMIntersectionObserverEntry>>& aEntries); 119 DOMIntersectionObserver(Document&, NativeCallback); 120 121 public: 122 DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner, 123 dom::IntersectionCallback& aCb); 124 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 125 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMIntersectionObserver) 126 NS_INLINE_DECL_STATIC_IID(NS_DOM_INTERSECTION_OBSERVER_IID) 127 128 static already_AddRefed<DOMIntersectionObserver> Constructor( 129 const GlobalObject&, dom::IntersectionCallback&, ErrorResult&); 130 static already_AddRefed<DOMIntersectionObserver> Constructor( 131 const GlobalObject&, dom::IntersectionCallback&, 132 const IntersectionObserverInit&, ErrorResult&); 133 134 JSObject* WrapObject(JSContext* aCx, 135 JS::Handle<JSObject*> aGivenProto) override { 136 return IntersectionObserver_Binding::Wrap(aCx, this, aGivenProto); 137 } 138 139 nsISupports* GetParentObject() const; 140 141 nsINode* GetRoot() const { return mRoot; } 142 143 void GetRootMargin(nsACString&); 144 bool SetRootMargin(const nsACString&); 145 146 void GetScrollMargin(nsACString&); 147 bool SetScrollMargin(const nsACString&); 148 149 void GetThresholds(nsTArray<double>& aRetVal); 150 void Observe(Element& aTarget); 151 void Unobserve(Element& aTarget); 152 153 void UnlinkTarget(Element& aTarget); 154 void Disconnect(); 155 156 void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal); 157 158 static IntersectionInput ComputeInputForIframeThrottling(const Document&); 159 static IntersectionInput ComputeInput( 160 const Document& aDocument, const nsINode* aRoot, 161 const StyleRect<LengthPercentage>* aRootMargin, 162 const StyleRect<LengthPercentage>* aScrollMargin); 163 164 enum class IsForProximityToViewport : bool { No, Yes }; 165 enum class BoxToUse : uint8_t { 166 Content, 167 Border, 168 OverflowClip, 169 }; 170 static IntersectionOutput Intersect( 171 const IntersectionInput&, const Element&, BoxToUse = BoxToUse::Border, 172 IsForProximityToViewport = IsForProximityToViewport::No); 173 static IntersectionOutput Intersect( 174 const IntersectionInput&, nsIFrame*, BoxToUse = BoxToUse::Border, 175 IsForProximityToViewport = IsForProximityToViewport::No); 176 // Intersects with a given rect, already relative to the root frame. 177 static IntersectionOutput Intersect(const IntersectionInput&, const nsRect&); 178 179 void Update(Document& aDocument, DOMHighResTimeStamp time); 180 MOZ_CAN_RUN_SCRIPT void Notify(); 181 182 static already_AddRefed<DOMIntersectionObserver> CreateLazyLoadObserver( 183 Document&); 184 185 static Maybe<nsRect> EdgeInclusiveIntersection(const nsRect& aRect, 186 const nsRect& aOtherRect); 187 188 protected: 189 void Connect(); 190 void QueueIntersectionObserverEntry(Element* aTarget, 191 DOMHighResTimeStamp time, 192 const Maybe<nsRect>& aRootRect, 193 const nsRect& aTargetRect, 194 const Maybe<nsRect>& aIntersectionRect, 195 bool aIsIntersecting, 196 double aIntersectionRatio); 197 198 nsCOMPtr<nsPIDOMWindowInner> mOwner; 199 RefPtr<Document> mDocument; 200 Variant<RefPtr<dom::IntersectionCallback>, NativeCallback> mCallback; 201 RefPtr<nsINode> mRoot; 202 StyleRect<LengthPercentage> mRootMargin; 203 StyleRect<LengthPercentage> mScrollMargin; 204 AutoTArray<double, 1> mThresholds; 205 206 // These hold raw pointers which are explicitly cleared by UnlinkTarget(). 207 // 208 // We keep a set and an array because we need ordered access, but also 209 // constant time lookup. 210 nsTArray<Element*> mObservationTargets; 211 212 // Value can be: 213 // -2: Makes sure next calculated threshold always differs, leading to a 214 // notification task being scheduled. 215 // -1: Non-intersecting. 216 // >= 0: Intersecting, valid index of aObserver->mThresholds. 217 enum ObservationState : int32_t { Uninitialized = -2, NotIntersecting = -1 }; 218 nsTHashMap<Element*, int32_t> mObservationTargetMap; 219 220 nsTArray<RefPtr<DOMIntersectionObserverEntry>> mQueuedEntries; 221 bool mConnected = false; 222 }; 223 224 } // namespace mozilla::dom 225 226 #endif