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