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
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_DisplayPortUtils_h__
8 #define mozilla_DisplayPortUtils_h__
9 
10 #include "Units.h"
11 #include "nsDisplayList.h"
12 #include "nsRect.h"
13 
14 #include <cstdint>
15 #include <iosfwd>
16 
17 class nsIContent;
18 class nsIFrame;
19 class nsDisplayListBuilder;
20 class nsPresContext;
21 
22 namespace mozilla {
23 
24 class PresShell;
25 
26 // For GetDisplayPort
27 enum class DisplayportRelativeTo { ScrollPort, ScrollFrame };
28 
29 enum class MaxSizeExceededBehaviour {
30   // Ask GetDisplayPort to assert if the calculated displayport exceeds
31   // the maximum allowed size.
32   Assert,
33   // Ask GetDisplayPort to pretend like there's no displayport at all, if
34   // the calculated displayport exceeds the maximum allowed size.
35   Drop,
36 };
37 
38 // Is the displayport being applied to scrolled content or fixed content?
39 enum class ContentGeometryType { Scrolled, Fixed };
40 
41 struct DisplayPortOptions {
42   // The default options.
43   DisplayportRelativeTo mRelativeTo = DisplayportRelativeTo::ScrollPort;
44   MaxSizeExceededBehaviour mMaxSizeExceededBehaviour =
45       MaxSizeExceededBehaviour::Assert;
46   ContentGeometryType mGeometryType = ContentGeometryType::Scrolled;
47 
48   // Fluent interface for changing the defaults.
WithDisplayPortOptions49   DisplayPortOptions With(DisplayportRelativeTo aRelativeTo) const {
50     DisplayPortOptions result = *this;
51     result.mRelativeTo = aRelativeTo;
52     return result;
53   }
WithDisplayPortOptions54   DisplayPortOptions With(
55       MaxSizeExceededBehaviour aMaxSizeExceededBehaviour) const {
56     DisplayPortOptions result = *this;
57     result.mMaxSizeExceededBehaviour = aMaxSizeExceededBehaviour;
58     return result;
59   }
WithDisplayPortOptions60   DisplayPortOptions With(ContentGeometryType aGeometryType) const {
61     DisplayPortOptions result = *this;
62     result.mGeometryType = aGeometryType;
63     return result;
64   }
65 };
66 
67 struct DisplayPortPropertyData {
DisplayPortPropertyDataDisplayPortPropertyData68   DisplayPortPropertyData(const nsRect& aRect, uint32_t aPriority,
69                           bool aPainted)
70       : mRect(aRect), mPriority(aPriority), mPainted(aPainted) {}
71   nsRect mRect;
72   uint32_t mPriority;
73   bool mPainted;
74 };
75 
76 struct DisplayPortMargins {
77   // The margins relative to the visual scroll offset.
78   ScreenMargin mMargins;
79 
80   // Some information captured at the time the margins are stored.
81   // This ensures that we can express the margins as being relative to
82   // the correct scroll offset when applying them.
83 
84   // APZ's visual scroll offset at the time it requested the margins.
85   CSSPoint mVisualOffset;
86 
87   // The scroll frame's layout scroll offset at the time the margins
88   // were saved.
89   CSSPoint mLayoutOffset;
90 
91   // The scale required to convert between the CSS cordinates of
92   // mVisualOffset and mLayoutOffset, and the Screen coordinates of mMargins.
93   CSSToScreenScale2D mScale;
94 
95   // Create displayport margins requested by APZ, relative to an async visual
96   // offset provided by APZ.
97   static DisplayPortMargins FromAPZ(const ScreenMargin& aMargins,
98                                     const CSSPoint& aVisualOffset,
99                                     const CSSPoint& aLayoutOffset,
100                                     const CSSToScreenScale2D& aScale);
101 
102   // Create displayport port margins for the given scroll frame.
103   // This is for use in cases where we don't have async scroll information from
104   // APZ to use to adjust the margins. The visual and layout offset are set
105   // based on the main thread's view of them. If a scale isn't provided, one
106   // is computed based on the main thread's knowledge.
107   static DisplayPortMargins ForScrollFrame(
108       nsIScrollableFrame* aScrollFrame, const ScreenMargin& aMargins,
109       const Maybe<CSSToScreenScale2D>& aScale = Nothing());
110 
111   // Convenience version of the above that takes a content element.
112   static DisplayPortMargins ForContent(nsIContent* aContent,
113                                        const ScreenMargin& aMargins);
114 
115   // Another convenience version that sets empty margins.
EmptyDisplayPortMargins116   static DisplayPortMargins Empty(nsIContent* aContent) {
117     return ForContent(aContent, ScreenMargin());
118   }
119 
120   // Get the margins relative to the layout viewport.
121   // |aGeometryType| tells us whether the margins are being queried for the
122   // purpose of being applied to scrolled content or fixed content.
123   // |aScrollableFrame| is the scroll frame whose content the margins will be
124   // applied to (or, in the case of fixed content), the scroll frame wrt. which
125   // the content is fixed.
126   ScreenMargin GetRelativeToLayoutViewport(
127       ContentGeometryType aGeometryType,
128       nsIScrollableFrame* aScrollableFrame) const;
129 
130   friend std::ostream& operator<<(std::ostream& aOs,
131                                   const DisplayPortMargins& aMargins);
132 
133  private:
134   CSSPoint ComputeAsyncTranslation(ContentGeometryType aGeometryType,
135                                    nsIScrollableFrame* aScrollableFrame) const;
136 };
137 
138 struct DisplayPortMarginsPropertyData {
DisplayPortMarginsPropertyDataDisplayPortMarginsPropertyData139   DisplayPortMarginsPropertyData(const DisplayPortMargins& aMargins,
140                                  uint32_t aPriority, bool aPainted)
141       : mMargins(aMargins), mPriority(aPriority), mPainted(aPainted) {}
142   DisplayPortMargins mMargins;
143   uint32_t mPriority;
144   bool mPainted;
145 };
146 
147 class DisplayPortUtils {
148  public:
149   /**
150    * Get display port for the given element, relative to the specified entity,
151    * defaulting to the scrollport.
152    */
153   static bool GetDisplayPort(
154       nsIContent* aContent, nsRect* aResult,
155       const DisplayPortOptions& aOptions = DisplayPortOptions());
156 
157   /**
158    * Check whether the given element has a displayport.
159    */
160   static bool HasDisplayPort(nsIContent* aContent);
161 
162   /**
163    * Check whether the given element has a displayport that has already
164    * been sent to the compositor via a layers or WR transaction.
165    */
166   static bool HasPaintedDisplayPort(nsIContent* aContent);
167 
168   /**
169    * Mark the displayport of a given element as having been sent to
170    * the compositor via a layers or WR transaction.
171    */
172   static void MarkDisplayPortAsPainted(nsIContent* aContent);
173 
174   /**
175    * Check whether the given frame has a displayport. It returns false
176    * for scrolled frames and true for the corresponding scroll frame.
177    * Optionally pass the child, and it only returns true if the child is the
178    * scrolled frame for the displayport.
179    */
180   static bool FrameHasDisplayPort(nsIFrame* aFrame,
181                                   const nsIFrame* aScrolledFrame = nullptr);
182 
183   /**
184    * Check whether the given element has a non-minimal displayport.
185    */
186   static bool HasNonMinimalDisplayPort(nsIContent* aContent);
187 
188   /**
189    * Check whether the given element has a non-minimal displayport that also has
190    * non-zero margins. A display port rect is considered non-minimal non-zero.
191    */
192   static bool HasNonMinimalNonZeroDisplayPort(nsIContent* aContent);
193 
194   /**
195    * Check if the given element has a margins based displayport but is missing a
196    * displayport base rect that it needs to properly compute a displayport rect.
197    */
198   static bool IsMissingDisplayPortBaseRect(nsIContent* aContent);
199 
200   /**
201    * Go through the IPC Channel and update displayport margins for content
202    * elements based on UpdateFrame messages. The messages are left in the
203    * queue and will be fully processed when dequeued. The aim is to paint
204    * the most up-to-date displayport without waiting for these message to
205    * go through the message queue.
206    */
207   static void UpdateDisplayPortMarginsFromPendingMessages();
208 
209   /**
210    * @return the display port for the given element which should be used for
211    * visibility testing purposes, relative to the scroll frame.
212    *
213    * If low-precision buffers are enabled, this is the critical display port;
214    * otherwise, it's the same display port returned by GetDisplayPort().
215    */
216   static bool GetDisplayPortForVisibilityTesting(nsIContent* aContent,
217                                                  nsRect* aResult);
218 
219   enum class RepaintMode : uint8_t { Repaint, DoNotRepaint };
220 
221   /**
222    * Invalidate for displayport change.
223    */
224   static void InvalidateForDisplayPortChange(
225       nsIContent* aContent, bool aHadDisplayPort, const nsRect& aOldDisplayPort,
226       const nsRect& aNewDisplayPort,
227       RepaintMode aRepaintMode = RepaintMode::Repaint);
228 
229   /**
230    * Set the display port margins for a content element to be used with a
231    * display port base (see SetDisplayPortBase()).
232    * See also nsIDOMWindowUtils.setDisplayPortMargins.
233    * @param aContent the content element for which to set the margins
234    * @param aPresShell the pres shell for the document containing the element
235    * @param aMargins the margins to set
236    * @param aAlignmentX, alignmentY the amount of pixels to which to align the
237    *                                displayport built by combining the base
238    *                                rect with the margins, in either direction
239    * @param aPriority a priority value to determine which margins take effect
240    *                  when multiple callers specify margins
241    * @param aRepaintMode whether to schedule a paint after setting the margins
242    * @return true if the new margins were applied.
243    */
244   enum class ClearMinimalDisplayPortProperty { No, Yes };
245 
246   static bool SetDisplayPortMargins(
247       nsIContent* aContent, PresShell* aPresShell,
248       const DisplayPortMargins& aMargins,
249       ClearMinimalDisplayPortProperty aClearMinimalDisplayPortProperty,
250       uint32_t aPriority = 0, RepaintMode aRepaintMode = RepaintMode::Repaint);
251 
252   /**
253    * Set the display port base rect for given element to be used with display
254    * port margins.
255    * SetDisplayPortBaseIfNotSet is like SetDisplayPortBase except it only sets
256    * the display port base to aBase if no display port base is currently set.
257    */
258   static void SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase);
259   static void SetDisplayPortBaseIfNotSet(nsIContent* aContent,
260                                          const nsRect& aBase);
261 
262   /**
263    * Get the critical display port for the given element.
264    */
265   static bool GetCriticalDisplayPort(
266       nsIContent* aContent, nsRect* aResult,
267       const DisplayPortOptions& aOptions = DisplayPortOptions());
268 
269   /**
270    * Check whether the given element has a critical display port.
271    */
272   static bool HasCriticalDisplayPort(nsIContent* aContent);
273 
274   /**
275    * If low-precision painting is turned on, delegates to
276    * GetCriticalDisplayPort. Otherwise, delegates to GetDisplayPort.
277    */
278   static bool GetHighResolutionDisplayPort(
279       nsIContent* aContent, nsRect* aResult,
280       const DisplayPortOptions& aOptions = DisplayPortOptions());
281 
282   /**
283    * Remove the displayport for the given element.
284    */
285   static void RemoveDisplayPort(nsIContent* aContent);
286 
287   /**
288    * Return true if aPresContext's viewport has a displayport.
289    */
290   static bool ViewportHasDisplayPort(nsPresContext* aPresContext);
291 
292   /**
293    * Return true if aFrame is a fixed-pos frame and is a child of a viewport
294    * which has a displayport. These frames get special treatment from the
295    * compositor. aDisplayPort, if non-null, is set to the display port rectangle
296    * (relative to the viewport).
297    */
298   static bool IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame);
299 
300   static bool MaybeCreateDisplayPortInFirstScrollFrameEncountered(
301       nsIFrame* aFrame, nsDisplayListBuilder* aBuilder);
302 
303   /**
304    * Calculate a default set of displayport margins for the given scrollframe
305    * and set them on the scrollframe's content element. The margins are set with
306    * the default priority, which may clobber previously set margins. The repaint
307    * mode provided is passed through to the call to SetDisplayPortMargins.
308    * The |aScrollFrame| parameter must be non-null and queryable to an nsIFrame.
309    * @return true iff the call to SetDisplayPortMargins returned true.
310    */
311   static bool CalculateAndSetDisplayPortMargins(
312       nsIScrollableFrame* aScrollFrame, RepaintMode aRepaintMode);
313 
314   /**
315    * If |aScrollFrame| WantsAsyncScroll() and we don't have a scrollable
316    * displayport yet (as tracked by |aBuilder|), calculate and set a
317    * displayport.
318    *
319    * If this is called during display list building pass DoNotRepaint in
320    * aRepaintMode.
321    *
322    * Returns true if there is a displayport on an async scrollable scrollframe
323    * after this call, either because one was just added or it already existed.
324    */
325   static bool MaybeCreateDisplayPort(nsDisplayListBuilder* aBuilder,
326                                      nsIFrame* aScrollFrame,
327                                      RepaintMode aRepaintMode);
328 
329   /**
330    * Sets a zero margin display port on all proper ancestors of aFrame that
331    * are async scrollable.
332    */
333   static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
334       nsIFrame* aFrame);
335 
336   /**
337    * Finds the closest ancestor async scrollable frame from aFrame that has a
338    * displayport and attempts to trigger the displayport expiry on that
339    * ancestor.
340    */
341   static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame);
342 
343   /**
344    * Returns root displayport base rect for |aPresShell|. In the case where
345    * |aPresShell| is in an out-of-process iframe, this function may return
346    * Nothing() if we haven't received the iframe's visible rect from the parent
347    * content.
348    * |aPresShell| should be top level content or in-process root or root in the
349    * browser process.
350    */
351   static Maybe<nsRect> GetRootDisplayportBase(PresShell* aPresShell);
352 };
353 
354 }  // namespace mozilla
355 
356 #endif  // mozilla_DisplayPortUtils_h__
357