DisplayPortUtils.h (17782B)
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 <cstdint> 11 #include <iosfwd> 12 13 #include "Units.h" 14 #include "nsDisplayList.h" 15 #include "nsRect.h" 16 17 class nsIContent; 18 class nsIFrame; 19 class nsPresContext; 20 21 namespace mozilla { 22 23 class nsDisplayListBuilder; 24 class PresShell; 25 class ScrollContainerFrame; 26 27 // For GetDisplayPort 28 enum class DisplayportRelativeTo { ScrollPort, ScrollFrame }; 29 30 // Is the displayport being applied to scrolled content or fixed content? 31 enum class ContentGeometryType { Scrolled, Fixed }; 32 33 struct DisplayPortOptions { 34 // The default options. 35 DisplayportRelativeTo mRelativeTo = DisplayportRelativeTo::ScrollPort; 36 ContentGeometryType mGeometryType = ContentGeometryType::Scrolled; 37 38 // Fluent interface for changing the defaults. 39 DisplayPortOptions With(DisplayportRelativeTo aRelativeTo) const { 40 DisplayPortOptions result = *this; 41 result.mRelativeTo = aRelativeTo; 42 return result; 43 } 44 DisplayPortOptions With(ContentGeometryType aGeometryType) const { 45 DisplayPortOptions result = *this; 46 result.mGeometryType = aGeometryType; 47 return result; 48 } 49 }; 50 51 struct DisplayPortPropertyData { 52 DisplayPortPropertyData(const nsRect& aRect, uint32_t aPriority, 53 bool aPainted) 54 : mRect(aRect), mPriority(aPriority), mPainted(aPainted) {} 55 nsRect mRect; 56 uint32_t mPriority; 57 bool mPainted; 58 }; 59 60 struct DisplayPortMargins { 61 // The margins relative to the visual scroll offset. 62 ScreenMargin mMargins; 63 64 // Some information captured at the time the margins are stored. 65 // This ensures that we can express the margins as being relative to 66 // the correct scroll offset when applying them. 67 68 // APZ's visual scroll offset at the time it requested the margins. 69 CSSPoint mVisualOffset; 70 71 // The scroll frame's layout scroll offset at the time the margins 72 // were saved. 73 CSSPoint mLayoutOffset; 74 75 // Create displayport margins requested by APZ, relative to an async visual 76 // offset provided by APZ. 77 static DisplayPortMargins FromAPZ(const ScreenMargin& aMargins, 78 const CSSPoint& aVisualOffset, 79 const CSSPoint& aLayoutOffset); 80 81 // Create displayport port margins for the given scroll container frame. 82 // This is for use in cases where we don't have async scroll information from 83 // APZ to use to adjust the margins. The visual and layout offset are set 84 // based on the main thread's view of them. 85 static DisplayPortMargins ForScrollContainerFrame( 86 ScrollContainerFrame* aScrollContainerFrame, 87 const ScreenMargin& aMargins); 88 89 // Convenience version of the above that takes a content element. 90 static DisplayPortMargins ForContent(nsIContent* aContent, 91 const ScreenMargin& aMargins); 92 93 // Another convenience version that sets empty margins. 94 static DisplayPortMargins Empty(nsIContent* aContent) { 95 return ForContent(aContent, ScreenMargin()); 96 } 97 98 // Get the margins relative to the layout viewport. 99 // |aGeometryType| tells us whether the margins are being queried for the 100 // purpose of being applied to scrolled content or fixed content. 101 // |aScrollableFrame| is the scroll frame whose content the margins will be 102 // applied to (or, in the case of fixed content), the scroll frame wrt. which 103 // the content is fixed. 104 ScreenMargin GetRelativeToLayoutViewport( 105 ContentGeometryType aGeometryType, 106 ScrollContainerFrame* aScrollContainerFrame, 107 const CSSToScreenScale2D& aDisplayportScale) const; 108 109 friend std::ostream& operator<<(std::ostream& aOs, 110 const DisplayPortMargins& aMargins); 111 112 private: 113 CSSPoint ComputeAsyncTranslation( 114 ContentGeometryType aGeometryType, 115 ScrollContainerFrame* aScrollContainerFrame) const; 116 }; 117 118 struct DisplayPortMarginsPropertyData { 119 DisplayPortMarginsPropertyData(const DisplayPortMargins& aMargins, 120 uint32_t aPriority, bool aPainted) 121 : mMargins(aMargins), mPriority(aPriority), mPainted(aPainted) {} 122 DisplayPortMargins mMargins; 123 uint32_t mPriority; 124 bool mPainted; 125 }; 126 127 struct FrameAndASRKind { 128 nsIFrame* mFrame; 129 ActiveScrolledRoot::ASRKind mASRKind; 130 bool operator==(const FrameAndASRKind&) const = default; 131 static FrameAndASRKind default_value() { 132 return {nullptr, ActiveScrolledRoot::ASRKind::Scroll}; 133 } 134 }; 135 136 class DisplayPortUtils { 137 public: 138 /** 139 * Get display port for the given element, relative to the specified entity, 140 * defaulting to the scrollport. 141 */ 142 static bool GetDisplayPort( 143 nsIContent* aContent, nsRect* aResult, 144 const DisplayPortOptions& aOptions = DisplayPortOptions()); 145 146 /** 147 * Check whether the given element has a displayport. 148 */ 149 static bool HasDisplayPort(nsIContent* aContent); 150 151 /** 152 * Check whether the given element has a displayport that has already 153 * been sent to the compositor via a layers or WR transaction. 154 */ 155 static bool HasPaintedDisplayPort(nsIContent* aContent); 156 157 /** 158 * Mark the displayport of a given element as having been sent to 159 * the compositor via a layers or WR transaction. 160 */ 161 static void MarkDisplayPortAsPainted(nsIContent* aContent); 162 163 /** 164 * Check whether the given frame has a displayport. It returns false 165 * for scrolled frames and true for the corresponding scroll frame. 166 * Optionally pass the child, and it only returns true if the child is the 167 * scrolled frame for the displayport. 168 */ 169 static bool FrameHasDisplayPort(nsIFrame* aFrame, 170 const nsIFrame* aScrolledFrame = nullptr); 171 172 /** 173 * Check whether the given element has a non-minimal displayport. 174 */ 175 static bool HasNonMinimalDisplayPort(nsIContent* aContent); 176 177 /** 178 * Check whether the given element has a non-minimal displayport that also has 179 * non-zero margins. A display port rect is considered non-minimal non-zero. 180 */ 181 static bool HasNonMinimalNonZeroDisplayPort(nsIContent* aContent); 182 183 /** 184 * Check if the given element has a margins based displayport but is missing a 185 * displayport base rect that it needs to properly compute a displayport rect. 186 */ 187 static bool IsMissingDisplayPortBaseRect(nsIContent* aContent); 188 189 /** 190 * @return the display port for the given element which should be used for 191 * visibility testing purposes, relative to the scroll frame. 192 * 193 * This is the display port computed with a multipler of 1 which is the normal 194 * display port unless low-precision buffers are enabled. If low-precision 195 * buffers are enabled then GetDisplayPort() uses a multiplier to expand the 196 * displayport, so this will differ from GetDisplayPort. 197 */ 198 static bool GetDisplayPortForVisibilityTesting(nsIContent* aContent, 199 nsRect* aResult); 200 201 enum class RepaintMode : uint8_t { Repaint, DoNotRepaint }; 202 203 /** 204 * Invalidate for displayport change. 205 */ 206 static void InvalidateForDisplayPortChange( 207 nsIContent* aContent, bool aHadDisplayPort, const nsRect& aOldDisplayPort, 208 const nsRect& aNewDisplayPort, 209 RepaintMode aRepaintMode = RepaintMode::Repaint); 210 211 /** 212 * Set the display port margins for a content element to be used with a 213 * display port base (see SetDisplayPortBase()). 214 * See also nsIDOMWindowUtils.setDisplayPortMargins. 215 * @param aContent the content element for which to set the margins 216 * @param aPresShell the pres shell for the document containing the element 217 * @param aMargins the margins to set 218 * @param aAlignmentX, alignmentY the amount of pixels to which to align the 219 * displayport built by combining the base 220 * rect with the margins, in either direction 221 * @param aPriority a priority value to determine which margins take effect 222 * when multiple callers specify margins 223 * @param aRepaintMode whether to schedule a paint after setting the margins 224 * @return true if the new margins were applied. 225 */ 226 enum class ClearMinimalDisplayPortProperty { No, Yes }; 227 228 static bool SetDisplayPortMargins( 229 nsIContent* aContent, PresShell* aPresShell, 230 const DisplayPortMargins& aMargins, 231 ClearMinimalDisplayPortProperty aClearMinimalDisplayPortProperty, 232 uint32_t aPriority = 0, RepaintMode aRepaintMode = RepaintMode::Repaint); 233 234 /** 235 * Set the display port base rect for given element to be used with display 236 * port margins. 237 * SetDisplayPortBaseIfNotSet is like SetDisplayPortBase except it only sets 238 * the display port base to aBase if no display port base is currently set. 239 */ 240 static void SetDisplayPortBase(nsIContent* aContent, const nsRect& aBase); 241 static void SetDisplayPortBaseIfNotSet(nsIContent* aContent, 242 const nsRect& aBase); 243 244 /** 245 * Remove the displayport for the given element. 246 */ 247 static void RemoveDisplayPort(nsIContent* aContent); 248 249 /** 250 * Set minimal display port margins during painting. 251 */ 252 static void SetMinimalDisplayPortDuringPainting(nsIContent* aContent, 253 PresShell* aPresShell); 254 255 /** 256 * Return true if aPresContext's viewport has a displayport. 257 */ 258 static bool ViewportHasDisplayPort(nsPresContext* aPresContext); 259 260 /** 261 * Return true if aFrame is a fixed-pos frame and is a child of a viewport 262 * which has a displayport. These frames get special treatment from the 263 * compositor. aDisplayPort, if non-null, is set to the display port rectangle 264 * (relative to the viewport). 265 */ 266 static bool IsFixedPosFrameInDisplayPort(const nsIFrame* aFrame); 267 268 static bool MaybeCreateDisplayPortInFirstScrollFrameEncountered( 269 nsIFrame* aFrame, nsDisplayListBuilder* aBuilder); 270 271 /** 272 * Calculate a default set of displayport margins for the given scrollframe 273 * and set them on the scrollframe's content element. The margins are set with 274 * the default priority, which may clobber previously set margins. The repaint 275 * mode provided is passed through to the call to SetDisplayPortMargins. 276 * The |aScrollFrame| parameter must be non-null and queryable to an nsIFrame. 277 * @return true iff the call to SetDisplayPortMargins returned true. 278 */ 279 static bool CalculateAndSetDisplayPortMargins( 280 ScrollContainerFrame* aScrollContainerFrame, RepaintMode aRepaintMode); 281 282 /** 283 * If |aScrollContainerFrame| WantsAsyncScroll() and we don't have a 284 * scrollable displayport yet (as tracked by |aBuilder|), calculate and set a 285 * displayport. 286 * 287 * If this is called during display list building pass DoNotRepaint in 288 * aRepaintMode. 289 * 290 * Returns true if there is a displayport on an async scrollable scrollframe 291 * after this call, either because one was just added or it already existed. 292 */ 293 static bool MaybeCreateDisplayPort( 294 nsDisplayListBuilder* aBuilder, 295 ScrollContainerFrame* aScrollContainerFrame, RepaintMode aRepaintMode); 296 297 /** 298 * Sets a zero margin display port on all proper ancestors of aFrame that 299 * are async scrollable. 300 */ 301 static void SetZeroMarginDisplayPortOnAsyncScrollableAncestors( 302 nsIFrame* aFrame); 303 304 /** 305 * Finds the closest ancestor async scrollable frame from aFrame that has a 306 * displayport and attempts to trigger the displayport expiry on that 307 * ancestor. 308 */ 309 static void ExpireDisplayPortOnAsyncScrollableAncestor(nsIFrame* aFrame); 310 311 /** 312 * Returns root displayport base rect for |aPresShell|. In the case where 313 * |aPresShell| is in an out-of-process iframe, this function may return 314 * Nothing() if we haven't received the iframe's visible rect from the parent 315 * content. 316 * |aPresShell| should be top level content or in-process root or root in the 317 * browser process. 318 */ 319 static Maybe<nsRect> GetRootDisplayportBase(PresShell* aPresShell); 320 321 static nsRect GetDisplayportBase(nsIFrame* aFrame); 322 323 /** 324 * Whether to tell the given element will use empty displayport marings. 325 * NOTE: This function should be called only for the element having any type 326 * of displayports. 327 */ 328 static bool WillUseEmptyDisplayPortMargins(nsIContent* aContent); 329 330 /** 331 * Step up one frame in the async scrollable ancestor chain, to be used in 332 * conjunction with GetAsyncScrollableAncestorFrame to walk the async 333 * scrollable ancestor chain. Note this doesn't go from one async scrollable 334 * frame to the next. Rather this walks all frame types, taking only one 335 * ancestor step per call. 336 */ 337 static nsIFrame* OneStepInAsyncScrollableAncestorChain(nsIFrame* aFrame); 338 339 /** 340 * The next two functions (GetASRAncestorFrame and OneStepInASRChain) use 341 * FrameAndASRKind (a pair of a nsIFrame pointer an an ASRKind enum) as a 342 * cursor iterating up the frame tree. Each frame can potential generate two 343 * ASRs: an inner one corresponding to scrolling with the contents of the 344 * frame if it is a scroll frame, and an outer one correspnding to scrolling 345 * with the frame itself if it is a sticky position frame. Its meaning is 346 * different for each of the two functions but is natural when considering 347 * what each function does. When passed into GetASRAncestorFrame it specifies 348 * the first frame and type for the function to check. When returned from 349 * GetASRAncestorFrame it specifies the frame and type of the ASR (because 350 * GetASRAncestorFrame only returns ASRs). When passed into OneStepInASRChain 351 * it specifies the last spot that was checked, and OneStepInASRChain's job is 352 * to move one iteration from that, so it returns the next frame and ASR kind 353 * to be checked (which may not generate an ASR, just that it needs to be 354 * checked because it could generate an ASR). 355 */ 356 357 /** 358 * Follows the ASR (ActiveScrolledRoot) chain of frames, so that if 359 * f is the frame of an ASR A, then calling this function on 360 * OneStepInASRChain(f) will return the frame of parent ASR of A. Frames that 361 * generate an ASR are scroll frames for which IsMaybeAsynchronouslyScrolled() 362 * returns true (aka mWillBuildScrollableLayer == true) or they are sticky 363 * position frames for which their corresponding scroll frame will generate an 364 * ASR. This function is different from 365 * nsLayoutUtils::GetAsyncScrollableAncestorFrame because 366 * GetAsyncScrollableAncestorFrame looks only for scroll frames that 367 * WantAsyncScroll that that function walks from fixed pos to the root scroll 368 * frame. Because that status (ie mWillBuildScrollableLayer) can change this 369 * should only be called during a paint to the window after BuildDisplayList 370 * has been called on aTarget so that mWillBuildScrollableLayer will have been 371 * updated for this paint already for any frame we need to consult. Or for 372 * some other reason you know that mWillBuildScrollableLayer is up to date for 373 * this paint for any frame that might need to be consulted, ie you just 374 * updated them yourself. Note that a frame returned from this function could 375 * generate two ASRs: an inner one corresponding to an activated scroll frame, 376 * and an outer one corresponding to sticky pos. 377 */ 378 static FrameAndASRKind GetASRAncestorFrame(FrameAndASRKind aFrameAndASRKind, 379 nsDisplayListBuilder* aBuilder); 380 381 /** 382 * Step up one frame in the ASR chain, to be used in conjunction with 383 * GetASRAncestorFrame to walk the ASR chain. Note this doesn't go from one 384 * ASR frame to the next. Rather this walks all frame types, taking only one 385 * ancestor step per call. Note that a frame returned from this function could 386 * generate two ASRs: an inner one corresponding to an activated scroll frame, 387 * and an outer one corresponding to sticky pos. Returns null if we hit 388 * aLimitAncestor. 389 */ 390 static FrameAndASRKind OneStepInASRChain(FrameAndASRKind aFrameAndASRKind, 391 nsDisplayListBuilder* aBuilder, 392 nsIFrame* aLimitAncestor = nullptr); 393 394 /** 395 * Calls DecideScrollableLayerEnsureDisplayport on all proper ancestors of 396 * aAnchor that are async scrollable up to but not including aLimitAncestor 397 * (this creates a minimal display port on all async scrollable ancestors if 398 * they don't have a display port) and makes sure that there is an ASR struct 399 * created for all such async scrollable ancestors. 400 * Returns the ASR of aAnchor. 401 * This is a very specific function for anchor positioning and likely not 402 * what you want. In that context, aAnchor is the anchor of an abspos frame f 403 * (not passed to this function because it is not needed) and aLimitAncestor 404 * is the parent/containing block of f. 405 */ 406 static const ActiveScrolledRoot* ActivateDisplayportOnASRAncestors( 407 nsIFrame* aAnchor, nsIFrame* aLimitAncestor, 408 const ActiveScrolledRoot* aASRofLimitAncestor, 409 nsDisplayListBuilder* aBuilder); 410 411 /** 412 * aFrame is an absolutely positioned frame that is anchor positioned and 413 * compensates for scroll in at least one axis. 414 */ 415 static bool ShouldAsyncScrollWithAnchor(nsIFrame* aFrame, nsIFrame* aAnchor, 416 nsDisplayListBuilder* aBuilder, 417 PhysicalAxes aAxes); 418 }; 419 420 } // namespace mozilla 421 422 #endif // mozilla_DisplayPortUtils_h__