tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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