Bug 1728955: part 3) Add logging to `nsBaseClipboard`. r=masayuki
[gecko.git] / dom / base / ResizeObserver.h
bloba4f3e0ab8f15119b77bea01ba5fdd10f90ee4477
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_ResizeObserver_h
8 #define mozilla_dom_ResizeObserver_h
10 #include "gfxPoint.h"
11 #include "js/TypeDecls.h"
12 #include "mozilla/AppUnits.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/LinkedList.h"
15 #include "mozilla/WritingModes.h"
16 #include "mozilla/dom/DOMRect.h"
17 #include "mozilla/dom/BindingDeclarations.h"
18 #include "mozilla/dom/ResizeObserverBinding.h"
19 #include "nsCoord.h"
20 #include "nsCycleCollectionParticipant.h"
21 #include "nsRefPtrHashtable.h"
22 #include "nsTArray.h"
23 #include "nsWrapperCache.h"
25 // XXX Avoid including this here by moving function bodies to the cpp file
26 #include "nsPIDOMWindow.h"
28 namespace mozilla {
29 class ErrorResult;
31 namespace dom {
33 class Element;
35 // The logical size in pixels.
36 // Note: if LogicalPixelSize have usages other than ResizeObserver in the
37 // future, it might be better to change LogicalSize into a template class, and
38 // use it to implement LogicalPixelSize.
39 class LogicalPixelSize {
40 public:
41 LogicalPixelSize() = default;
42 LogicalPixelSize(WritingMode aWM, const gfx::Size& aSize) {
43 mSize = aSize;
44 if (aWM.IsVertical()) {
45 std::swap(mSize.width, mSize.height);
49 bool operator==(const LogicalPixelSize& aOther) const {
50 return mSize == aOther.mSize;
52 bool operator!=(const LogicalPixelSize& aOther) const {
53 return !(*this == aOther);
56 float ISize() const { return mSize.width; }
57 float BSize() const { return mSize.height; }
58 float& ISize() { return mSize.width; }
59 float& BSize() { return mSize.height; }
61 private:
62 // |mSize.width| represents inline-size and |mSize.height| represents
63 // block-size.
64 gfx::Size mSize;
67 // For the internal implementation in ResizeObserver. Normally, this is owned by
68 // ResizeObserver.
69 class ResizeObservation final : public LinkedListElement<ResizeObservation> {
70 public:
71 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ResizeObservation)
72 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ResizeObservation)
74 ResizeObservation(Element&, ResizeObserver&, ResizeObserverBoxOptions,
75 WritingMode);
77 Element* Target() const { return mTarget; }
79 ResizeObserverBoxOptions BoxOptions() const { return mObservedBox; }
81 /**
82 * Returns whether the observed target element size differs from the saved
83 * mLastReportedSize.
85 bool IsActive() const;
87 /**
88 * Update current mLastReportedSize with size from aSize.
90 void UpdateLastReportedSize(const gfx::Size& aSize);
92 enum class RemoveFromObserver : bool { No, Yes };
93 void Unlink(RemoveFromObserver);
95 protected:
96 ~ResizeObservation() { Unlink(RemoveFromObserver::No); };
98 nsCOMPtr<Element> mTarget;
100 // Weak, observer always outlives us.
101 ResizeObserver* mObserver;
103 const ResizeObserverBoxOptions mObservedBox;
105 // The latest recorded of observed target.
106 // This will be CSS pixels for border-box/content-box, or device pixels for
107 // device-pixel-content-box.
108 // Note: We use default constructor for this because we want to start with a
109 // (0, 0) size, per the spec.
110 LogicalPixelSize mLastReportedSize;
114 * ResizeObserver interfaces and algorithms are based on
115 * https://drafts.csswg.org/resize-observer/#api
117 class ResizeObserver final : public nsISupports, public nsWrapperCache {
118 public:
119 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
120 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserver)
122 ResizeObserver(nsCOMPtr<nsPIDOMWindowInner>&& aOwner, Document* aDocument,
123 ResizeObserverCallback& aCb)
124 : mOwner(std::move(aOwner)), mDocument(aDocument), mCallback(&aCb) {
125 MOZ_ASSERT(mOwner, "Need a non-null owner window");
126 MOZ_ASSERT(mDocument, "Need a non-null doc");
127 MOZ_ASSERT(mDocument == mOwner->GetExtantDoc());
130 nsISupports* GetParentObject() const { return mOwner; }
132 JSObject* WrapObject(JSContext* aCx,
133 JS::Handle<JSObject*> aGivenProto) override {
134 return ResizeObserver_Binding::Wrap(aCx, this, aGivenProto);
137 static already_AddRefed<ResizeObserver> Constructor(
138 const GlobalObject& aGlobal, ResizeObserverCallback& aCb,
139 ErrorResult& aRv);
141 void Observe(Element& aTarget, const ResizeObserverOptions& aOptions,
142 ErrorResult& aRv);
144 void Unobserve(Element& target, ErrorResult& aRv);
146 void Disconnect();
149 * Gather all observations which have an observed target with size changed
150 * since last BroadcastActiveObservations() in this ResizeObserver.
151 * An observation will be skipped if the depth of its observed target is less
152 * or equal than aDepth. All gathered observations will be added to
153 * mActiveTargets.
155 void GatherActiveObservations(uint32_t aDepth);
158 * Returns whether this ResizeObserver has any active observations
159 * since last GatherActiveObservations().
161 bool HasActiveObservations() const { return !mActiveTargets.IsEmpty(); }
164 * Returns whether this ResizeObserver has any skipped observations
165 * since last GatherActiveObservations().
167 bool HasSkippedObservations() const { return mHasSkippedTargets; }
170 * Invoke the callback function in JavaScript for all active observations
171 * and pass the sequence of ResizeObserverEntry so JavaScript can access them.
172 * The active observations' mLastReportedSize fields will be updated, and
173 * mActiveTargets will be cleared. It also returns the shallowest depth of
174 * elements from active observations or numeric_limits<uint32_t>::max() if
175 * there are not any active observations.
177 MOZ_CAN_RUN_SCRIPT uint32_t BroadcastActiveObservations();
179 protected:
180 ~ResizeObserver() { Disconnect(); }
182 nsCOMPtr<nsPIDOMWindowInner> mOwner;
183 // The window's document at the time of ResizeObserver creation.
184 RefPtr<Document> mDocument;
185 RefPtr<ResizeObserverCallback> mCallback;
186 nsTArray<RefPtr<ResizeObservation>> mActiveTargets;
187 // The spec uses a list to store the skipped targets. However, it seems what
188 // we want is to check if there are any skipped targets (i.e. existence).
189 // Therefore, we use a boolean value to represent the existence of skipped
190 // targets.
191 bool mHasSkippedTargets;
193 // Combination of HashTable and LinkedList so we can iterate through
194 // the elements of HashTable in order of insertion time, so we can deliver
195 // observations in the correct order
196 // FIXME: it will be nice if we have our own data structure for this in the
197 // future, and mObservationMap should be considered the "owning" storage for
198 // the observations, so it'd be better to drop mObservationList later.
199 nsRefPtrHashtable<nsPtrHashKey<Element>, ResizeObservation> mObservationMap;
200 LinkedList<ResizeObservation> mObservationList;
204 * ResizeObserverEntry is the entry that contains the information for observed
205 * elements. This object is the one that's visible to JavaScript in callback
206 * function that is fired by ResizeObserver.
208 class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
209 public:
210 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
211 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverEntry)
213 ResizeObserverEntry(nsISupports* aOwner, Element& aTarget,
214 const gfx::Size& aBorderBoxSize,
215 const gfx::Size& aContentBoxSize,
216 const gfx::Size& aDevicePixelContentBoxSize)
217 : mOwner(aOwner), mTarget(&aTarget) {
218 MOZ_ASSERT(mOwner, "Need a non-null owner");
219 MOZ_ASSERT(mTarget, "Need a non-null target element");
221 SetBorderBoxSize(aBorderBoxSize);
222 SetContentRectAndSize(aContentBoxSize);
223 SetDevicePixelContentSize(aDevicePixelContentBoxSize);
226 nsISupports* GetParentObject() const { return mOwner; }
228 JSObject* WrapObject(JSContext* aCx,
229 JS::Handle<JSObject*> aGivenProto) override {
230 return ResizeObserverEntry_Binding::Wrap(aCx, this, aGivenProto);
233 Element* Target() const { return mTarget; }
236 * Returns the DOMRectReadOnly of target's content rect so it can be
237 * accessed from JavaScript in callback function of ResizeObserver.
239 DOMRectReadOnly* ContentRect() const { return mContentRect; }
242 * Returns target's logical border-box size, content-box size, and
243 * device-pixel-content-box as an array of ResizeObserverSize.
245 void GetBorderBoxSize(nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;
246 void GetContentBoxSize(nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;
247 void GetDevicePixelContentBoxSize(
248 nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;
250 private:
251 ~ResizeObserverEntry() = default;
253 // Set borderBoxSize.
254 void SetBorderBoxSize(const gfx::Size& aSize);
255 // Set contentRect and contentBoxSize.
256 void SetContentRectAndSize(const gfx::Size& aSize);
257 // Set devicePixelContentBoxSize.
258 void SetDevicePixelContentSize(const gfx::Size& aSize);
260 nsCOMPtr<nsISupports> mOwner;
261 nsCOMPtr<Element> mTarget;
263 RefPtr<DOMRectReadOnly> mContentRect;
264 RefPtr<ResizeObserverSize> mBorderBoxSize;
265 RefPtr<ResizeObserverSize> mContentBoxSize;
266 RefPtr<ResizeObserverSize> mDevicePixelContentBoxSize;
269 class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
270 public:
271 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
272 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverSize)
274 ResizeObserverSize(nsISupports* aOwner, const gfx::Size& aSize,
275 const WritingMode aWM)
276 : mOwner(aOwner), mSize(aWM, aSize) {
277 MOZ_ASSERT(mOwner, "Need a non-null owner");
280 nsISupports* GetParentObject() const { return mOwner; }
282 JSObject* WrapObject(JSContext* aCx,
283 JS::Handle<JSObject*> aGivenProto) override {
284 return ResizeObserverSize_Binding::Wrap(aCx, this, aGivenProto);
287 double InlineSize() const { return mSize.ISize(); }
288 double BlockSize() const { return mSize.BSize(); }
290 protected:
291 ~ResizeObserverSize() = default;
293 nsCOMPtr<nsISupports> mOwner;
294 // The logical size value:
295 // 1. content-box/border-box: in CSS pixels.
296 // 2. device-pixel-content-box: in device pixels.
297 const LogicalPixelSize mSize;
300 } // namespace dom
301 } // namespace mozilla
303 #endif // mozilla_dom_ResizeObserver_h