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