FrameMetrics.cpp (13720B)
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 #include "FrameMetrics.h" 8 9 #include <ostream> 10 11 #include "gfxUtils.h" 12 #include "nsStyleConsts.h" 13 #include "mozilla/gfx/Types.h" 14 15 namespace mozilla { 16 namespace layers { 17 18 const ScrollableLayerGuid::ViewID ScrollableLayerGuid::NULL_SCROLL_ID = 0; 19 20 std::ostream& operator<<(std::ostream& aStream, const FrameMetrics& aMetrics) { 21 aStream << "{ [cb=" << aMetrics.GetCompositionBounds() 22 << "] [sr=" << aMetrics.GetScrollableRect() 23 << "] [s=" << aMetrics.GetVisualScrollOffset(); 24 if (aMetrics.GetVisualScrollUpdateType() != FrameMetrics::eNone) { 25 aStream << "] [vd=" << aMetrics.GetVisualDestination(); 26 } 27 if (aMetrics.IsScrollInfoLayer()) { 28 aStream << "] [scrollinfo"; 29 } 30 aStream << "] [dp=" << aMetrics.GetDisplayPort() 31 << "] [rcs=" << aMetrics.GetBoundingCompositionSize() 32 << "] [v=" << aMetrics.GetLayoutViewport() 33 << nsPrintfCString("] [z=(ld=%.3f r=%.3f", 34 aMetrics.GetDevPixelsPerCSSPixel().scale, 35 aMetrics.GetPresShellResolution()) 36 .get() 37 << " cr=" << aMetrics.GetCumulativeResolution() 38 << " z=" << aMetrics.GetZoom() 39 << " t=" << aMetrics.GetTransformToAncestorScale() << " )] [u=(" 40 << (int)aMetrics.GetVisualScrollUpdateType() << " " 41 << aMetrics.GetScrollGeneration() 42 << ")] scrollId=" << aMetrics.GetScrollId(); 43 if (aMetrics.IsRootContent()) { 44 aStream << " [rcd]"; 45 } 46 aStream << " }"; 47 return aStream; 48 } 49 50 CSSRect FrameMetrics::GetVisualViewportForLayoutViewportContainment( 51 ScreenCoord aFixedLayerBottomMargin) const { 52 const bool hasDynamicToolbar = GetCompositionSizeWithoutDynamicToolbar() != 53 GetCompositionBounds().Size(); 54 // In the case where the toolbar is dynamic if `aFixedLayerBottomMargin` 55 // is zero, it means the dynamic toolbar is fully visible. 56 const bool isDynamicToolbarFullyVisible = 57 hasDynamicToolbar && aFixedLayerBottomMargin == 0; 58 59 return CSSRect( 60 GetVisualScrollOffset(), 61 // Use `mCompositionSizeWithoutDynamicToolbar` in the case where the 62 // dynamic toolbar is fully visible. 63 // Theoretically we don't need to check `IsSoftwareKeyboardVisible()` or 64 // `GetInteractiveWidget()` either, but for now we'd like to restrict this 65 // behavior change in the scope of the visual scroll offset change 66 // initiated by zoom-to-focused-input on resizes-visual with the software 67 // keyboard. 68 // TODO Bug 2003420: This restriction will be dropped in one of the bugs 69 // blocking bug 2003420. As of now it's unclear what kind of test 70 // cases need to drop this restction as user visible issues. 71 isDynamicToolbarFullyVisible && IsSoftwareKeyboardVisible() && 72 GetInteractiveWidget() == dom::InteractiveWidget::ResizesVisual 73 ? CalculateCompositedSizeInCssPixels( 74 ParentLayerRect(ParentLayerPoint(), 75 mCompositionSizeWithoutDynamicToolbar), 76 mZoom) 77 : CalculateCompositedSizeInCssPixels()); 78 } 79 80 void FrameMetrics::RecalculateLayoutViewportOffset( 81 ScreenCoord aFixedLayerBottomMargin) { 82 // For subframes, the visual and layout viewports coincide, so just 83 // keep the layout viewport offset in sync with the visual one. 84 if (!mIsRootContent) { 85 mLayoutViewport.MoveTo(GetVisualScrollOffset()); 86 return; 87 } 88 89 // For the root, the two viewports can diverge, but the layout 90 // viewport needs to keep enclosing the visual viewport. 91 KeepLayoutViewportEnclosingVisualViewport( 92 GetVisualViewportForLayoutViewportContainment(aFixedLayerBottomMargin), 93 mScrollableRect, mLayoutViewport); 94 } 95 96 /* static */ 97 void FrameMetrics::KeepLayoutViewportEnclosingVisualViewport( 98 const CSSRect& aVisualViewport, const CSSRect& aScrollableRect, 99 CSSRect& aLayoutViewport) { 100 // If the visual viewport is contained within the layout viewport, we don't 101 // need to make any adjustments, so we can exit early. 102 // 103 // Additionally, if the composition bounds changes (due to an orientation 104 // change, window resize, etc.), it may take a few frames for aLayoutViewport 105 // to update and during that time, the visual viewport may be larger than the 106 // layout viewport. In such situations, we take an early exit if the visual 107 // viewport contains the layout viewport. 108 if (aLayoutViewport.Contains(aVisualViewport) || 109 aVisualViewport.Contains(aLayoutViewport)) { 110 return; 111 } 112 113 // If visual viewport size is greater than the layout viewport, move the 114 // layout viewport such that it remains inside the visual viewport. Otherwise, 115 // move the layout viewport such that the visual viewport is contained 116 // inside the layout viewport. 117 if ((aLayoutViewport.Width() < aVisualViewport.Width() && 118 !FuzzyEqualsMultiplicative(aLayoutViewport.Width(), 119 aVisualViewport.Width())) || 120 (aLayoutViewport.Height() < aVisualViewport.Height() && 121 !FuzzyEqualsMultiplicative(aLayoutViewport.Height(), 122 aVisualViewport.Height()))) { 123 if (aLayoutViewport.X() < aVisualViewport.X()) { 124 // layout viewport moves right 125 aLayoutViewport.MoveToX(aVisualViewport.X()); 126 } else if (aVisualViewport.XMost() < aLayoutViewport.XMost()) { 127 // layout viewport moves left 128 aLayoutViewport.MoveByX(aVisualViewport.XMost() - 129 aLayoutViewport.XMost()); 130 } 131 if (aLayoutViewport.Y() < aVisualViewport.Y()) { 132 // layout viewport moves down 133 aLayoutViewport.MoveToY(aVisualViewport.Y()); 134 } else if (aVisualViewport.YMost() < aLayoutViewport.YMost()) { 135 // layout viewport moves up 136 aLayoutViewport.MoveByY(aVisualViewport.YMost() - 137 aLayoutViewport.YMost()); 138 } 139 } else { 140 if (aVisualViewport.X() < aLayoutViewport.X()) { 141 aLayoutViewport.MoveToX(aVisualViewport.X()); 142 } else if (aLayoutViewport.XMost() < aVisualViewport.XMost()) { 143 aLayoutViewport.MoveByX(aVisualViewport.XMost() - 144 aLayoutViewport.XMost()); 145 } 146 if (aVisualViewport.Y() < aLayoutViewport.Y()) { 147 aLayoutViewport.MoveToY(aVisualViewport.Y()); 148 } else if (aLayoutViewport.YMost() < aVisualViewport.YMost()) { 149 aLayoutViewport.MoveByY(aVisualViewport.YMost() - 150 aLayoutViewport.YMost()); 151 } 152 } 153 154 // Regardless of any adjustment above, the layout viewport is not allowed 155 // to go outside the scrollable rect. 156 aLayoutViewport = aLayoutViewport.MoveInsideAndClamp(aScrollableRect); 157 } 158 159 /* static */ 160 CSSRect FrameMetrics::CalculateScrollRange( 161 const CSSRect& aScrollableRect, const ParentLayerRect& aCompositionBounds, 162 const CSSToParentLayerScale& aZoom) { 163 CSSSize scrollPortSize = 164 CalculateCompositedSizeInCssPixels(aCompositionBounds, aZoom); 165 CSSRect scrollRange = aScrollableRect; 166 scrollRange.SetWidth( 167 std::max(scrollRange.Width() - scrollPortSize.width, 0.0f)); 168 scrollRange.SetHeight( 169 std::max(scrollRange.Height() - scrollPortSize.height, 0.0f)); 170 return scrollRange; 171 } 172 173 /* static */ 174 CSSSize FrameMetrics::CalculateCompositedSizeInCssPixels( 175 const ParentLayerRect& aCompositionBounds, 176 const CSSToParentLayerScale& aZoom) { 177 if (aZoom == CSSToParentLayerScale(0)) { 178 return CSSSize(); // avoid division by zero 179 } 180 return aCompositionBounds.Size() / aZoom; 181 } 182 183 bool FrameMetrics::ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) { 184 MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Absolute); 185 MOZ_ASSERT(aUpdate.GetMode() != ScrollMode::Smooth && 186 aUpdate.GetMode() != ScrollMode::SmoothMsd); 187 return ScrollLayoutViewportTo(aUpdate.GetDestination()); 188 } 189 190 bool FrameMetrics::ScrollLayoutViewportTo(const CSSPoint& aDestination) { 191 // In applying a main-thread scroll update, try to preserve the relative 192 // offset between the visual and layout viewports. 193 CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset(); 194 MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint()); 195 // We need to set the two offsets together, otherwise a subsequent 196 // RecalculateLayoutViewportOffset() could see divergent layout and 197 // visual offsets. 198 bool offsetChanged = SetLayoutScrollOffset(aDestination); 199 offsetChanged |= ClampAndSetVisualScrollOffset(aDestination + relativeOffset); 200 return offsetChanged; 201 } 202 203 CSSPoint FrameMetrics::ApplyRelativeScrollUpdateFrom( 204 const ScrollPositionUpdate& aUpdate, IsDefaultApzc aIsDefaultApzc) { 205 MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Relative); 206 MOZ_ASSERT(aUpdate.GetMode() != ScrollMode::Smooth && 207 aUpdate.GetMode() != ScrollMode::SmoothMsd); 208 209 // If the APZC is default, i.e. newly created one, any relative instant 210 // scroll position update has been already reflected as the visual scroll 211 // offset, so we use the mSource in this ScrollPositionUpdate, which is the 212 // original scroll offset when this relative scroll update operation happened 213 // on the content. 214 CSSPoint origin = 215 bool(aIsDefaultApzc) ? aUpdate.GetSource() : GetVisualScrollOffset(); 216 CSSPoint delta = (aUpdate.GetDestination() - aUpdate.GetSource()); 217 SetVisualScrollOffset(origin + delta); 218 return GetVisualScrollOffset() - origin; 219 } 220 221 CSSPoint FrameMetrics::ApplyPureRelativeScrollUpdateFrom( 222 const ScrollPositionUpdate& aUpdate) { 223 MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::PureRelative); 224 MOZ_ASSERT(aUpdate.GetMode() != ScrollMode::Smooth && 225 aUpdate.GetMode() != ScrollMode::SmoothMsd); 226 CSSPoint origin = GetVisualScrollOffset(); 227 ClampAndSetVisualScrollOffset(origin + aUpdate.GetDelta()); 228 return GetVisualScrollOffset() - origin; 229 } 230 231 void FrameMetrics::UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo) { 232 // We only get this "pending scroll info" for paint-skip transactions, 233 // but PureRelative position updates always trigger a full paint, so 234 // we should never enter this code with a PureRelative update type. For 235 // the other types, the destination field on the ScrollPositionUpdate will 236 // tell us the final layout scroll position on the main thread. 237 MOZ_ASSERT(aInfo.GetType() != ScrollUpdateType::PureRelative); 238 239 // In applying a main-thread scroll update, try to preserve the relative 240 // offset between the visual and layout viewports. 241 CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset(); 242 MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint()); 243 244 SetLayoutScrollOffset(aInfo.GetDestination()); 245 ClampAndSetVisualScrollOffset(aInfo.GetDestination() + relativeOffset); 246 mScrollGeneration = aInfo.GetGeneration(); 247 } 248 249 std::ostream& operator<<(std::ostream& aStream, 250 const OverscrollBehavior& aBehavior) { 251 switch (aBehavior) { 252 case OverscrollBehavior::Auto: { 253 aStream << "auto"; 254 break; 255 } 256 case OverscrollBehavior::Contain: { 257 aStream << "contain"; 258 break; 259 } 260 case OverscrollBehavior::None: { 261 aStream << "none"; 262 break; 263 } 264 } 265 return aStream; 266 } 267 268 OverscrollBehaviorInfo::OverscrollBehaviorInfo() 269 : mBehaviorX(OverscrollBehavior::Auto), 270 mBehaviorY(OverscrollBehavior::Auto) {} 271 272 static OverscrollBehavior ToOverscrollBehavior( 273 StyleOverscrollBehavior aBehavior) { 274 switch (aBehavior) { 275 case StyleOverscrollBehavior::Auto: 276 return OverscrollBehavior::Auto; 277 case StyleOverscrollBehavior::Contain: 278 return OverscrollBehavior::Contain; 279 case StyleOverscrollBehavior::None: 280 return OverscrollBehavior::None; 281 } 282 MOZ_ASSERT_UNREACHABLE("Invalid overscroll behavior"); 283 return OverscrollBehavior::Auto; 284 } 285 286 OverscrollBehaviorInfo OverscrollBehaviorInfo::FromStyleConstants( 287 StyleOverscrollBehavior aBehaviorX, StyleOverscrollBehavior aBehaviorY) { 288 OverscrollBehaviorInfo result; 289 result.mBehaviorX = ToOverscrollBehavior(aBehaviorX); 290 result.mBehaviorY = ToOverscrollBehavior(aBehaviorY); 291 return result; 292 } 293 294 bool OverscrollBehaviorInfo::operator==( 295 const OverscrollBehaviorInfo& aOther) const { 296 return mBehaviorX == aOther.mBehaviorX && mBehaviorY == aOther.mBehaviorY; 297 } 298 299 std::ostream& operator<<(std::ostream& aStream, 300 const OverscrollBehaviorInfo& aInfo) { 301 if (aInfo.mBehaviorX == aInfo.mBehaviorY) { 302 aStream << aInfo.mBehaviorX; 303 } else { 304 aStream << "{ x=" << aInfo.mBehaviorX << ", y=" << aInfo.mBehaviorY << " }"; 305 } 306 return aStream; 307 } 308 309 bool OverflowInfo::operator==(const OverflowInfo& aOther) const { 310 return mOverflowX == aOther.mOverflowX && mOverflowY == aOther.mOverflowY; 311 } 312 313 std::ostream& operator<<(std::ostream& aStream, 314 const ScrollMetadata& aMetadata) { 315 aStream << "{ [description=" << aMetadata.GetContentDescription() 316 << "] [metrics=" << aMetadata.GetMetrics(); 317 if (aMetadata.GetScrollParentId() != ScrollableLayerGuid::NULL_SCROLL_ID) { 318 aStream << "] [scrollParent=" << aMetadata.GetScrollParentId(); 319 } 320 aStream << "] [overscroll=" << aMetadata.GetOverscrollBehavior() << "] [" 321 << aMetadata.GetScrollUpdates().Length() << " scrollupdates" 322 << "] }"; 323 return aStream; 324 } 325 326 StaticAutoPtr<const ScrollMetadata> ScrollMetadata::sNullMetadata; 327 328 } // namespace layers 329 } // namespace mozilla