LCOV - code coverage report
Current view: top level - layout/generic - ScrollSnap.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 114 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 11 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "FrameMetrics.h"
       7             : #include "ScrollSnap.h"
       8             : #include "gfxPrefs.h"
       9             : #include "mozilla/Maybe.h"
      10             : #include "mozilla/Preferences.h"
      11             : #include "nsLineLayout.h"
      12             : 
      13             : namespace mozilla {
      14             : 
      15             : using layers::ScrollSnapInfo;
      16             : 
      17             : /**
      18             :  * Stores candidate snapping edges.
      19             :  */
      20           0 : class SnappingEdgeCallback {
      21             : public:
      22             :   virtual void AddHorizontalEdge(nscoord aEdge) = 0;
      23             :   virtual void AddVerticalEdge(nscoord aEdge) = 0;
      24             :   virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange,
      25             :                                          nscoord aInterval,
      26             :                                          nscoord aOffset) = 0;
      27             :   virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange,
      28             :                                        nscoord aInterval,
      29             :                                        nscoord aOffset) = 0;
      30             : };
      31             : 
      32             : /**
      33             :  * Keeps track of the current best edge to snap to. The criteria for
      34             :  * adding an edge depends on the scrolling unit.
      35             :  */
      36             : class CalcSnapPoints : public SnappingEdgeCallback {
      37             : public:
      38             :   CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit,
      39             :                  const nsPoint& aDestination,
      40             :                  const nsPoint& aStartPos);
      41             :   virtual void AddHorizontalEdge(nscoord aEdge) override;
      42             :   virtual void AddVerticalEdge(nscoord aEdge) override;
      43             :   virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange,
      44             :                                          nscoord aInterval, nscoord aOffset)
      45             :                                          override;
      46             :   virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange,
      47             :                                        nscoord aInterval, nscoord aOffset)
      48             :                                        override;
      49             :   void AddEdge(nscoord aEdge,
      50             :                nscoord aDestination,
      51             :                nscoord aStartPos,
      52             :                nscoord aScrollingDirection,
      53             :                nscoord* aBestEdge,
      54             :                bool* aEdgeFound);
      55             :   void AddEdgeInterval(nscoord aInterval,
      56             :                        nscoord aMinPos,
      57             :                        nscoord aMaxPos,
      58             :                        nscoord aOffset,
      59             :                        nscoord aDestination,
      60             :                        nscoord aStartPos,
      61             :                        nscoord aScrollingDirection,
      62             :                        nscoord* aBestEdge,
      63             :                        bool* aEdgeFound);
      64             :   nsPoint GetBestEdge() const;
      65             : protected:
      66             :   nsIScrollableFrame::ScrollUnit mUnit;
      67             :   nsPoint mDestination;            // gives the position after scrolling but before snapping
      68             :   nsPoint mStartPos;               // gives the position before scrolling
      69             :   nsIntPoint mScrollingDirection;  // always -1, 0, or 1
      70             :   nsPoint mBestEdge;               // keeps track of the position of the current best edge
      71             :   bool mHorizontalEdgeFound;       // true if mBestEdge.x is storing a valid horizontal edge
      72             :   bool mVerticalEdgeFound;         // true if mBestEdge.y is storing a valid vertical edge
      73             : };
      74             : 
      75           0 : CalcSnapPoints::CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit,
      76             :                                const nsPoint& aDestination,
      77           0 :                                const nsPoint& aStartPos)
      78             : {
      79           0 :   mUnit = aUnit;
      80           0 :   mDestination = aDestination;
      81           0 :   mStartPos = aStartPos;
      82             : 
      83           0 :   nsPoint direction = aDestination - aStartPos;
      84           0 :   mScrollingDirection = nsIntPoint(0,0);
      85           0 :   if (direction.x < 0) {
      86           0 :     mScrollingDirection.x = -1;
      87             :   }
      88           0 :   if (direction.x > 0) {
      89           0 :     mScrollingDirection.x = 1;
      90             :   }
      91           0 :   if (direction.y < 0) {
      92           0 :     mScrollingDirection.y = -1;
      93             :   }
      94           0 :   if (direction.y > 0) {
      95           0 :     mScrollingDirection.y = 1;
      96             :   }
      97           0 :   mBestEdge = aDestination;
      98           0 :   mHorizontalEdgeFound = false;
      99           0 :   mVerticalEdgeFound = false;
     100           0 : }
     101             : 
     102             : nsPoint
     103           0 : CalcSnapPoints::GetBestEdge() const
     104             : {
     105           0 :   return nsPoint(mVerticalEdgeFound ? mBestEdge.x : mStartPos.x,
     106           0 :                  mHorizontalEdgeFound ? mBestEdge.y : mStartPos.y);
     107             : }
     108             : 
     109             : void
     110           0 : CalcSnapPoints::AddHorizontalEdge(nscoord aEdge)
     111             : {
     112           0 :   AddEdge(aEdge, mDestination.y, mStartPos.y, mScrollingDirection.y, &mBestEdge.y,
     113           0 :           &mHorizontalEdgeFound);
     114           0 : }
     115             : 
     116             : void
     117           0 : CalcSnapPoints::AddVerticalEdge(nscoord aEdge)
     118             : {
     119           0 :   AddEdge(aEdge, mDestination.x, mStartPos.x, mScrollingDirection.x, &mBestEdge.x,
     120           0 :           &mVerticalEdgeFound);
     121           0 : }
     122             : 
     123             : void
     124           0 : CalcSnapPoints::AddHorizontalEdgeInterval(const nsRect &aScrollRange,
     125             :                                           nscoord aInterval, nscoord aOffset)
     126             : {
     127           0 :   AddEdgeInterval(aInterval, aScrollRange.y, aScrollRange.YMost(), aOffset,
     128             :                   mDestination.y, mStartPos.y, mScrollingDirection.y,
     129           0 :                   &mBestEdge.y, &mHorizontalEdgeFound);
     130           0 : }
     131             : 
     132             : void
     133           0 : CalcSnapPoints::AddVerticalEdgeInterval(const nsRect &aScrollRange,
     134             :                                         nscoord aInterval, nscoord aOffset)
     135             : {
     136           0 :   AddEdgeInterval(aInterval, aScrollRange.x, aScrollRange.XMost(), aOffset,
     137             :                   mDestination.x, mStartPos.x, mScrollingDirection.x,
     138           0 :                   &mBestEdge.x, &mVerticalEdgeFound);
     139           0 : }
     140             : 
     141             : void
     142           0 : CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination, nscoord aStartPos,
     143             :                         nscoord aScrollingDirection, nscoord* aBestEdge,
     144             :                         bool *aEdgeFound)
     145             : {
     146             :   // nsIScrollableFrame::DEVICE_PIXELS indicates that we are releasing a drag
     147             :   // gesture or any other user input event that sets an absolute scroll
     148             :   // position.  In this case, scroll snapping is expected to travel in any
     149             :   // direction.  Otherwise, we will restrict the direction of the scroll
     150             :   // snapping movement based on aScrollingDirection.
     151           0 :   if (mUnit != nsIScrollableFrame::DEVICE_PIXELS) {
     152             :     // Unless DEVICE_PIXELS, we only want to snap to points ahead of the
     153             :     // direction we are scrolling
     154           0 :     if (aScrollingDirection == 0) {
     155             :       // The scroll direction is neutral - will not hit a snap point.
     156           0 :       return;
     157             :     }
     158             :     // nsIScrollableFrame::WHOLE indicates that we are navigating to "home" or
     159             :     // "end".  In this case, we will always select the first or last snap point
     160             :     // regardless of the direction of the scroll.  Otherwise, we will select
     161             :     // scroll snapping points only in the direction specified by
     162             :     // aScrollingDirection.
     163           0 :     if (mUnit != nsIScrollableFrame::WHOLE) {
     164             :       // Direction of the edge from the current position (before scrolling) in
     165             :       // the direction of scrolling
     166           0 :       nscoord direction = (aEdge - aStartPos) * aScrollingDirection;
     167           0 :       if (direction <= 0) {
     168             :         // The edge is not in the direction we are scrolling, skip it.
     169           0 :         return;
     170             :       }
     171             :     }
     172             :   }
     173           0 :   if (!*aEdgeFound) {
     174           0 :     *aBestEdge = aEdge;
     175           0 :     *aEdgeFound = true;
     176           0 :     return;
     177             :   }
     178           0 :   if (mUnit == nsIScrollableFrame::DEVICE_PIXELS ||
     179           0 :       mUnit == nsIScrollableFrame::LINES) {
     180           0 :     if (std::abs(aEdge - aDestination) < std::abs(*aBestEdge - aDestination)) {
     181           0 :       *aBestEdge = aEdge;
     182             :     }
     183           0 :   } else if (mUnit == nsIScrollableFrame::PAGES) {
     184             :     // distance to the edge from the scrolling destination in the direction of scrolling
     185           0 :     nscoord overshoot = (aEdge - aDestination) * aScrollingDirection;
     186             :     // distance to the current best edge from the scrolling destination in the direction of scrolling
     187           0 :     nscoord curOvershoot = (*aBestEdge - aDestination) * aScrollingDirection;
     188             : 
     189             :     // edges between the current position and the scrolling destination are favoured
     190             :     // to preserve context
     191           0 :     if (overshoot < 0 && (overshoot > curOvershoot || curOvershoot >= 0)) {
     192           0 :       *aBestEdge = aEdge;
     193             :     }
     194             :     // if there are no edges between the current position and the scrolling destination
     195             :     // the closest edge beyond the destination is used
     196           0 :     if (overshoot > 0 && overshoot < curOvershoot) {
     197           0 :       *aBestEdge = aEdge;
     198             :     }
     199           0 :   } else if (mUnit == nsIScrollableFrame::WHOLE) {
     200             :     // the edge closest to the top/bottom/left/right is used, depending on scrolling direction
     201           0 :     if (aScrollingDirection > 0 && aEdge > *aBestEdge) {
     202           0 :       *aBestEdge = aEdge;
     203           0 :     } else if (aScrollingDirection < 0 && aEdge < *aBestEdge) {
     204           0 :       *aBestEdge = aEdge;
     205             :     }
     206             :   } else {
     207           0 :     NS_ERROR("Invalid scroll mode");
     208           0 :     return;
     209             :   }
     210             : }
     211             : 
     212             : void
     213           0 : CalcSnapPoints::AddEdgeInterval(nscoord aInterval, nscoord aMinPos,
     214             :                                 nscoord aMaxPos, nscoord aOffset,
     215             :                                 nscoord aDestination, nscoord aStartPos,
     216             :                                 nscoord aScrollingDirection,
     217             :                                 nscoord* aBestEdge, bool *aEdgeFound)
     218             : {
     219           0 :   if (aInterval == 0) {
     220             :     // When interval is 0, there are no scroll snap points.
     221             :     // Avoid division by zero and bail.
     222           0 :     return;
     223             :   }
     224             : 
     225             :   // The only possible candidate interval snap points are the edges immediately
     226             :   // surrounding aDestination.
     227             : 
     228             :   // aDestination must be clamped to the scroll
     229             :   // range in order to handle cases where the best matching snap point would
     230             :   // result in scrolling out of bounds.  This clamping must be prior to
     231             :   // selecting the two interval edges.
     232           0 :   nscoord clamped = std::max(std::min(aDestination, aMaxPos), aMinPos);
     233             : 
     234             :   // Add each edge in the interval immediately before aTarget and after aTarget
     235             :   // Do not add edges that are out of range.
     236           0 :   nscoord r = (clamped + aOffset) % aInterval;
     237           0 :   if (r < aMinPos) {
     238           0 :     r += aInterval;
     239             :   }
     240           0 :   nscoord edge = clamped - r;
     241           0 :   if (edge >= aMinPos && edge <= aMaxPos) {
     242             :     AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge,
     243           0 :             aEdgeFound);
     244             :   }
     245           0 :   edge += aInterval;
     246           0 :   if (edge >= aMinPos && edge <= aMaxPos) {
     247             :     AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge,
     248           0 :             aEdgeFound);
     249             :   }
     250             : }
     251             : 
     252             : static void
     253           0 : ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback,
     254             :                              const nsTArray<nsPoint>& aScrollSnapCoordinates,
     255             :                              const nsPoint& aScrollSnapDestination) {
     256           0 :   for (nsPoint snapCoords : aScrollSnapCoordinates) {
     257             :     // Make them relative to the scroll snap destination.
     258           0 :     snapCoords -= aScrollSnapDestination;
     259             : 
     260           0 :     aCallback.AddVerticalEdge(snapCoords.x);
     261           0 :     aCallback.AddHorizontalEdge(snapCoords.y);
     262             :   }
     263           0 : }
     264             : 
     265           0 : Maybe<nsPoint> ScrollSnapUtils::GetSnapPointForDestination(
     266             :     const ScrollSnapInfo& aSnapInfo,
     267             :     nsIScrollableFrame::ScrollUnit aUnit,
     268             :     const nsSize& aScrollPortSize,
     269             :     const nsRect& aScrollRange,
     270             :     const nsPoint& aStartPos,
     271             :     const nsPoint& aDestination)
     272             : {
     273           0 :   if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
     274           0 :       aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
     275           0 :     return Nothing();
     276             :   }
     277             : 
     278           0 :   nsPoint destPos = aSnapInfo.mScrollSnapDestination;
     279             : 
     280           0 :   CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos);
     281             : 
     282           0 :   if (aSnapInfo.mScrollSnapIntervalX.isSome()) {
     283           0 :     nscoord interval = aSnapInfo.mScrollSnapIntervalX.value();
     284           0 :     calcSnapPoints.AddVerticalEdgeInterval(aScrollRange, interval, destPos.x);
     285             :   }
     286           0 :   if (aSnapInfo.mScrollSnapIntervalY.isSome()) {
     287           0 :     nscoord interval = aSnapInfo.mScrollSnapIntervalY.value();
     288           0 :     calcSnapPoints.AddHorizontalEdgeInterval(aScrollRange, interval, destPos.y);
     289             :   }
     290             : 
     291           0 :   ProcessScrollSnapCoordinates(calcSnapPoints, aSnapInfo.mScrollSnapCoordinates, destPos);
     292           0 :   bool snapped = false;
     293           0 :   nsPoint finalPos = calcSnapPoints.GetBestEdge();
     294           0 :   nscoord proximityThreshold = gfxPrefs::ScrollSnapProximityThreshold();
     295           0 :   proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold);
     296           0 :   if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
     297           0 :       std::abs(aDestination.y - finalPos.y) > proximityThreshold) {
     298           0 :     finalPos.y = aDestination.y;
     299             :   } else {
     300           0 :     snapped = true;
     301             :   }
     302           0 :   if (aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
     303           0 :       std::abs(aDestination.x - finalPos.x) > proximityThreshold) {
     304           0 :     finalPos.x = aDestination.x;
     305             :   } else {
     306           0 :     snapped = true;
     307             :   }
     308           0 :   return snapped ? Some(finalPos) : Nothing();
     309             : }
     310             : 
     311             : }  // namespace mozilla

Generated by: LCOV version 1.13