LCOV - code coverage report
Current view: top level - layout/generic - nsGfxScrollFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1321 2902 45.5 %
Date: 2017-07-14 16:53:18 Functions: 113 235 48.1 %
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             : /* rendering object to wrap rendering objects that should be scrollable */
       7             : 
       8             : #include "nsGfxScrollFrame.h"
       9             : 
      10             : #include "ActiveLayerTracker.h"
      11             : #include "base/compiler_specific.h"
      12             : #include "DisplayItemClip.h"
      13             : #include "nsCOMPtr.h"
      14             : #include "nsIContentViewer.h"
      15             : #include "nsPresContext.h"
      16             : #include "nsView.h"
      17             : #include "nsIScrollable.h"
      18             : #include "nsContainerFrame.h"
      19             : #include "nsGkAtoms.h"
      20             : #include "nsNameSpaceManager.h"
      21             : #include "nsContentList.h"
      22             : #include "nsIDocumentInlines.h"
      23             : #include "nsFontMetrics.h"
      24             : #include "nsBoxLayoutState.h"
      25             : #include "mozilla/dom/NodeInfo.h"
      26             : #include "nsScrollbarFrame.h"
      27             : #include "nsIScrollbarMediator.h"
      28             : #include "nsITextControlFrame.h"
      29             : #include "nsIDOMHTMLTextAreaElement.h"
      30             : #include "nsNodeInfoManager.h"
      31             : #include "nsContentCreatorFunctions.h"
      32             : #include "nsPresState.h"
      33             : #include "nsIHTMLDocument.h"
      34             : #include "nsContentUtils.h"
      35             : #include "nsLayoutUtils.h"
      36             : #include "nsBidiPresUtils.h"
      37             : #include "nsBidiUtils.h"
      38             : #include "mozilla/ContentEvents.h"
      39             : #include "mozilla/EventDispatcher.h"
      40             : #include "mozilla/Preferences.h"
      41             : #include "mozilla/LookAndFeel.h"
      42             : #include "mozilla/dom/Element.h"
      43             : #include <stdint.h>
      44             : #include "mozilla/MathAlgorithms.h"
      45             : #include "mozilla/Telemetry.h"
      46             : #include "FrameLayerBuilder.h"
      47             : #include "nsSMILKeySpline.h"
      48             : #include "nsSubDocumentFrame.h"
      49             : #include "nsSVGOuterSVGFrame.h"
      50             : #include "nsIObjectLoadingContent.h"
      51             : #include "mozilla/Attributes.h"
      52             : #include "ScrollbarActivity.h"
      53             : #include "nsRefreshDriver.h"
      54             : #include "nsThemeConstants.h"
      55             : #include "nsSVGIntegrationUtils.h"
      56             : #include "nsIScrollPositionListener.h"
      57             : #include "StickyScrollContainer.h"
      58             : #include "nsIFrameInlines.h"
      59             : #include "gfxPlatform.h"
      60             : #include "gfxPrefs.h"
      61             : #include "AsyncScrollBase.h"
      62             : #include "ScrollSnap.h"
      63             : #include "UnitTransforms.h"
      64             : #include "nsPluginFrame.h"
      65             : #include "nsSliderFrame.h"
      66             : #include "mozilla/layers/APZCCallbackHelper.h"
      67             : #include <mozilla/layers/AxisPhysicsModel.h>
      68             : #include <mozilla/layers/AxisPhysicsMSDModel.h>
      69             : #include "mozilla/layers/LayerTransactionChild.h"
      70             : #include "mozilla/layers/ScrollLinkedEffectDetector.h"
      71             : #include "mozilla/Unused.h"
      72             : #include "LayersLogging.h"  // for Stringify
      73             : #include <algorithm>
      74             : #include <cstdlib> // for std::abs(int/long)
      75             : #include <cmath> // for std::abs(float/double)
      76             : 
      77             : #define PAINT_SKIP_LOG(...)
      78             : // #define PAINT_SKIP_LOG(...) printf_stderr("PSKIP: " __VA_ARGS__)
      79             : 
      80             : using namespace mozilla;
      81             : using namespace mozilla::dom;
      82             : using namespace mozilla::layers;
      83             : using namespace mozilla::layout;
      84             : 
      85             : static uint32_t
      86           0 : GetOverflowChange(const nsRect& aCurScrolledRect, const nsRect& aPrevScrolledRect)
      87             : {
      88           0 :   uint32_t result = 0;
      89           0 :   if (aPrevScrolledRect.x != aCurScrolledRect.x ||
      90           0 :       aPrevScrolledRect.width != aCurScrolledRect.width) {
      91           0 :     result |= nsIScrollableFrame::HORIZONTAL;
      92             :   }
      93           0 :   if (aPrevScrolledRect.y != aCurScrolledRect.y ||
      94           0 :       aPrevScrolledRect.height != aCurScrolledRect.height) {
      95           0 :     result |= nsIScrollableFrame::VERTICAL;
      96             :   }
      97           0 :   return result;
      98             : }
      99             : 
     100             : //----------------------------------------------------------------------
     101             : 
     102             : //----------nsHTMLScrollFrame-------------------------------------------
     103             : 
     104             : nsHTMLScrollFrame*
     105          35 : NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
     106             : {
     107          35 :   return new (aPresShell) nsHTMLScrollFrame(aContext, aIsRoot);
     108             : }
     109             : 
     110          35 : NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame)
     111             : 
     112          35 : nsHTMLScrollFrame::nsHTMLScrollFrame(nsStyleContext* aContext,
     113             :                                      nsIFrame::ClassID aID,
     114          35 :                                      bool aIsRoot)
     115             :   : nsContainerFrame(aContext, aID)
     116          35 :   , mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
     117             : {
     118          35 : }
     119             : 
     120             : void
     121           0 : nsHTMLScrollFrame::ScrollbarActivityStarted() const
     122             : {
     123           0 :   if (mHelper.mScrollbarActivity) {
     124           0 :     mHelper.mScrollbarActivity->ActivityStarted();
     125             :   }
     126           0 : }
     127             : 
     128             : void
     129           0 : nsHTMLScrollFrame::ScrollbarActivityStopped() const
     130             : {
     131           0 :   if (mHelper.mScrollbarActivity) {
     132           0 :     mHelper.mScrollbarActivity->ActivityStopped();
     133             :   }
     134           0 : }
     135             : 
     136             : nsresult
     137          35 : nsHTMLScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
     138             : {
     139          35 :   return mHelper.CreateAnonymousContent(aElements);
     140             : }
     141             : 
     142             : void
     143           0 : nsHTMLScrollFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
     144             :                                             uint32_t aFilter)
     145             : {
     146           0 :   mHelper.AppendAnonymousContentTo(aElements, aFilter);
     147           0 : }
     148             : 
     149             : void
     150           6 : nsHTMLScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
     151             : {
     152           6 :   DestroyAbsoluteFrames(aDestructRoot);
     153           6 :   mHelper.Destroy();
     154           6 :   nsContainerFrame::DestroyFrom(aDestructRoot);
     155           6 : }
     156             : 
     157             : void
     158          35 : nsHTMLScrollFrame::SetInitialChildList(ChildListID  aListID,
     159             :                                        nsFrameList& aChildList)
     160             : {
     161          35 :   nsContainerFrame::SetInitialChildList(aListID, aChildList);
     162          35 :   mHelper.ReloadChildFrames();
     163          35 : }
     164             : 
     165             : 
     166             : void
     167          35 : nsHTMLScrollFrame::AppendFrames(ChildListID  aListID,
     168             :                                 nsFrameList& aFrameList)
     169             : {
     170          35 :   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
     171          35 :   mFrames.AppendFrames(nullptr, aFrameList);
     172          35 :   mHelper.ReloadChildFrames();
     173          35 : }
     174             : 
     175             : void
     176           0 : nsHTMLScrollFrame::InsertFrames(ChildListID aListID,
     177             :                                 nsIFrame* aPrevFrame,
     178             :                                 nsFrameList& aFrameList)
     179             : {
     180           0 :   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
     181           0 :   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
     182             :                "inserting after sibling frame with different parent");
     183           0 :   mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
     184           0 :   mHelper.ReloadChildFrames();
     185           0 : }
     186             : 
     187             : void
     188           0 : nsHTMLScrollFrame::RemoveFrame(ChildListID aListID,
     189             :                                nsIFrame* aOldFrame)
     190             : {
     191           0 :   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
     192           0 :   mFrames.DestroyFrame(aOldFrame);
     193           0 :   mHelper.ReloadChildFrames();
     194           0 : }
     195             : 
     196             : nsSplittableType
     197           0 : nsHTMLScrollFrame::GetSplittableType() const
     198             : {
     199           0 :   return NS_FRAME_NOT_SPLITTABLE;
     200             : }
     201             : 
     202             : /**
     203             :  HTML scrolling implementation
     204             : 
     205             :  All other things being equal, we prefer layouts with fewer scrollbars showing.
     206             : */
     207             : 
     208             : namespace mozilla {
     209             : 
     210         125 : struct MOZ_STACK_CLASS ScrollReflowInput {
     211             :   const ReflowInput& mReflowInput;
     212             :   nsBoxLayoutState mBoxState;
     213             :   ScrollbarStyles mStyles;
     214             :   nsMargin mComputedBorder;
     215             : 
     216             :   // === Filled in by ReflowScrolledFrame ===
     217             :   nsOverflowAreas mContentsOverflowAreas;
     218             :   MOZ_INIT_OUTSIDE_CTOR
     219             :   bool mReflowedContentsWithHScrollbar;
     220             :   MOZ_INIT_OUTSIDE_CTOR
     221             :   bool mReflowedContentsWithVScrollbar;
     222             : 
     223             :   // === Filled in when TryLayout succeeds ===
     224             :   // The size of the inside-border area
     225             :   nsSize mInsideBorderSize;
     226             :   // Whether we decided to show the horizontal scrollbar
     227             :   MOZ_INIT_OUTSIDE_CTOR
     228             :   bool mShowHScrollbar;
     229             :   // Whether we decided to show the vertical scrollbar
     230             :   MOZ_INIT_OUTSIDE_CTOR
     231             :   bool mShowVScrollbar;
     232             : 
     233         125 :   ScrollReflowInput(nsIScrollableFrame* aFrame,
     234         125 :                     const ReflowInput& aState) :
     235             :     mReflowInput(aState),
     236             :     // mBoxState is just used for scrollbars so we don't need to
     237             :     // worry about the reflow depth here
     238         125 :     mBoxState(aState.mFrame->PresContext(), aState.mRenderingContext, 0),
     239         250 :     mStyles(aFrame->GetScrollbarStyles()) {
     240         125 :   }
     241             : };
     242             : 
     243             : } // namespace mozilla
     244             : 
     245             : // XXXldb Can this go away?
     246         127 : static nsSize ComputeInsideBorderSize(ScrollReflowInput* aState,
     247             :                                       const nsSize& aDesiredInsideBorderSize)
     248             : {
     249             :   // aDesiredInsideBorderSize is the frame size; i.e., it includes
     250             :   // borders and padding (but the scrolled child doesn't have
     251             :   // borders). The scrolled child has the same padding as us.
     252         127 :   nscoord contentWidth = aState->mReflowInput.ComputedWidth();
     253         127 :   if (contentWidth == NS_UNCONSTRAINEDSIZE) {
     254           0 :     contentWidth = aDesiredInsideBorderSize.width -
     255           0 :       aState->mReflowInput.ComputedPhysicalPadding().LeftRight();
     256             :   }
     257         127 :   nscoord contentHeight = aState->mReflowInput.ComputedHeight();
     258         127 :   if (contentHeight == NS_UNCONSTRAINEDSIZE) {
     259          92 :     contentHeight = aDesiredInsideBorderSize.height -
     260          46 :       aState->mReflowInput.ComputedPhysicalPadding().TopBottom();
     261             :   }
     262             : 
     263         127 :   contentWidth  = aState->mReflowInput.ApplyMinMaxWidth(contentWidth);
     264         127 :   contentHeight = aState->mReflowInput.ApplyMinMaxHeight(contentHeight);
     265         254 :   return nsSize(contentWidth + aState->mReflowInput.ComputedPhysicalPadding().LeftRight(),
     266         381 :                 contentHeight + aState->mReflowInput.ComputedPhysicalPadding().TopBottom());
     267             : }
     268             : 
     269             : static void
     270          10 : GetScrollbarMetrics(nsBoxLayoutState& aState, nsIFrame* aBox, nsSize* aMin,
     271             :                     nsSize* aPref, bool aVertical)
     272             : {
     273          10 :   NS_ASSERTION(aState.GetRenderingContext(),
     274             :                "Must have rendering context in layout state for size "
     275             :                "computations");
     276             : 
     277          10 :   if (aMin) {
     278           8 :     *aMin = aBox->GetXULMinSize(aState);
     279           8 :     nsBox::AddMargin(aBox, *aMin);
     280           8 :     if (aMin->width < 0) {
     281           0 :       aMin->width = 0;
     282             :     }
     283           8 :     if (aMin->height < 0) {
     284           0 :       aMin->height = 0;
     285             :     }
     286             :   }
     287             : 
     288          10 :   if (aPref) {
     289           2 :     *aPref = aBox->GetXULPrefSize(aState);
     290           2 :     nsBox::AddMargin(aBox, *aPref);
     291           2 :     if (aPref->width < 0) {
     292           0 :       aPref->width = 0;
     293             :     }
     294           2 :     if (aPref->height < 0) {
     295           0 :       aPref->height = 0;
     296             :     }
     297             :   }
     298          10 : }
     299             : 
     300             : /**
     301             :  * Assuming that we know the metrics for our wrapped frame and
     302             :  * whether the horizontal and/or vertical scrollbars are present,
     303             :  * compute the resulting layout and return true if the layout is
     304             :  * consistent. If the layout is consistent then we fill in the
     305             :  * computed fields of the ScrollReflowInput.
     306             :  *
     307             :  * The layout is consistent when both scrollbars are showing if and only
     308             :  * if they should be showing. A horizontal scrollbar should be showing if all
     309             :  * following conditions are met:
     310             :  * 1) the style is not HIDDEN
     311             :  * 2) our inside-border height is at least the scrollbar height (i.e., the
     312             :  * scrollbar fits vertically)
     313             :  * 3) our scrollport width (the inside-border width minus the width allocated for a
     314             :  * vertical scrollbar, if showing) is at least the scrollbar's min-width
     315             :  * (i.e., the scrollbar fits horizontally)
     316             :  * 4) the style is SCROLL, or the kid's overflow-area XMost is
     317             :  * greater than the scrollport width
     318             :  *
     319             :  * @param aForce if true, then we just assume the layout is consistent.
     320             :  */
     321             : bool
     322         125 : nsHTMLScrollFrame::TryLayout(ScrollReflowInput* aState,
     323             :                              ReflowOutput* aKidMetrics,
     324             :                              bool aAssumeHScroll, bool aAssumeVScroll,
     325             :                              bool aForce)
     326             : {
     327         250 :   if ((aState->mStyles.mVertical == NS_STYLE_OVERFLOW_HIDDEN && aAssumeVScroll) ||
     328         246 :       (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
     329           0 :     NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
     330           0 :     return false;
     331             :   }
     332             : 
     333         250 :   if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
     334         125 :       (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar &&
     335           0 :        ScrolledContentDependsOnHeight(aState))) {
     336           0 :     if (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar) {
     337           0 :       nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
     338           0 :           mHelper.mScrolledFrame);
     339             :     }
     340           0 :     aKidMetrics->mOverflowAreas.Clear();
     341           0 :     ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll, aKidMetrics,
     342           0 :                         false);
     343             :   }
     344             : 
     345         125 :   nsSize vScrollbarMinSize(0, 0);
     346         125 :   nsSize vScrollbarPrefSize(0, 0);
     347         125 :   if (mHelper.mVScrollbarBox) {
     348           4 :     GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
     349             :                         &vScrollbarMinSize,
     350           4 :                         aAssumeVScroll ? &vScrollbarPrefSize : nullptr, true);
     351           4 :     nsScrollbarFrame* scrollbar = do_QueryFrame(mHelper.mVScrollbarBox);
     352           4 :     scrollbar->SetScrollbarMediatorContent(mContent);
     353             :   }
     354         125 :   nscoord vScrollbarDesiredWidth = aAssumeVScroll ? vScrollbarPrefSize.width : 0;
     355         125 :   nscoord vScrollbarMinHeight = aAssumeVScroll ? vScrollbarMinSize.height : 0;
     356             : 
     357         125 :   nsSize hScrollbarMinSize(0, 0);
     358         125 :   nsSize hScrollbarPrefSize(0, 0);
     359         125 :   if (mHelper.mHScrollbarBox) {
     360           4 :     GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
     361             :                         &hScrollbarMinSize,
     362           4 :                         aAssumeHScroll ? &hScrollbarPrefSize : nullptr, false);
     363           4 :     nsScrollbarFrame* scrollbar = do_QueryFrame(mHelper.mHScrollbarBox);
     364           4 :     scrollbar->SetScrollbarMediatorContent(mContent);
     365             :   }
     366         125 :   nscoord hScrollbarDesiredHeight = aAssumeHScroll ? hScrollbarPrefSize.height : 0;
     367         125 :   nscoord hScrollbarMinWidth = aAssumeHScroll ? hScrollbarMinSize.width : 0;
     368             : 
     369             :   // First, compute our inside-border size and scrollport size
     370             :   // XXXldb Can we depend more on ComputeSize here?
     371         125 :   nsSize desiredInsideBorderSize;
     372         125 :   desiredInsideBorderSize.width = vScrollbarDesiredWidth +
     373         125 :     std::max(aKidMetrics->Width(), hScrollbarMinWidth);
     374         125 :   desiredInsideBorderSize.height = hScrollbarDesiredHeight +
     375         125 :     std::max(aKidMetrics->Height(), vScrollbarMinHeight);
     376             :   aState->mInsideBorderSize =
     377         125 :     ComputeInsideBorderSize(aState, desiredInsideBorderSize);
     378         250 :   nsSize scrollPortSize = nsSize(std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
     379         375 :                                  std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
     380             : 
     381         125 :   nsSize visualScrollPortSize = scrollPortSize;
     382         125 :   nsIPresShell* presShell = PresContext()->PresShell();
     383         125 :   if (mHelper.mIsRoot && presShell->IsScrollPositionClampingScrollPortSizeSet()) {
     384           0 :     nsSize compositionSize = nsLayoutUtils::CalculateCompositionSizeForFrame(this, false);
     385           0 :     float resolution = presShell->GetResolution();
     386           0 :     compositionSize.width /= resolution;
     387           0 :     compositionSize.height /= resolution;
     388           0 :     visualScrollPortSize = nsSize(std::max(0, compositionSize.width - vScrollbarDesiredWidth),
     389           0 :                                   std::max(0, compositionSize.height - hScrollbarDesiredHeight));
     390             :   }
     391             : 
     392         125 :   if (!aForce) {
     393             :     nsRect scrolledRect =
     394         125 :       mHelper.GetUnsnappedScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
     395         250 :                                                scrollPortSize);
     396         125 :     nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
     397             : 
     398             :     // If the style is HIDDEN then we already know that aAssumeHScroll is false
     399         125 :     if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
     400             :       bool wantHScrollbar =
     401           8 :         aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
     402           8 :         scrolledRect.XMost() >= visualScrollPortSize.width + oneDevPixel ||
     403           8 :         scrolledRect.x <= -oneDevPixel;
     404           4 :       if (scrollPortSize.width < hScrollbarMinSize.width)
     405           0 :         wantHScrollbar = false;
     406           4 :       if (wantHScrollbar != aAssumeHScroll)
     407           0 :         return false;
     408             :     }
     409             : 
     410             :     // If the style is HIDDEN then we already know that aAssumeVScroll is false
     411         125 :     if (aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
     412             :       bool wantVScrollbar =
     413           8 :         aState->mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL ||
     414           8 :         scrolledRect.YMost() >= visualScrollPortSize.height + oneDevPixel ||
     415           8 :         scrolledRect.y <= -oneDevPixel;
     416           4 :       if (scrollPortSize.height < vScrollbarMinSize.height)
     417           0 :         wantVScrollbar = false;
     418           4 :       if (wantVScrollbar != aAssumeVScroll)
     419           0 :         return false;
     420             :     }
     421             :   }
     422             : 
     423         125 :   nscoord vScrollbarActualWidth = aState->mInsideBorderSize.width - scrollPortSize.width;
     424             : 
     425         125 :   aState->mShowHScrollbar = aAssumeHScroll;
     426         125 :   aState->mShowVScrollbar = aAssumeVScroll;
     427             :   nsPoint scrollPortOrigin(aState->mComputedBorder.left,
     428         125 :                            aState->mComputedBorder.top);
     429         125 :   if (!IsScrollbarOnRight()) {
     430           0 :     scrollPortOrigin.x += vScrollbarActualWidth;
     431             :   }
     432         125 :   mHelper.mScrollPort = nsRect(scrollPortOrigin, scrollPortSize);
     433         125 :   return true;
     434             : }
     435             : 
     436             : // XXX Height/BSize mismatch needs to be addressed here; check the caller!
     437             : // Currently this will only behave as expected for horizontal writing modes.
     438             : // (See bug 1175509.)
     439             : bool
     440           0 : nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowInput* aState)
     441             : {
     442             :   // Return true if ReflowScrolledFrame is going to do something different
     443             :   // based on the presence of a horizontal scrollbar.
     444           0 :   return mHelper.mScrolledFrame->HasAnyStateBits(
     445           0 :       NS_FRAME_CONTAINS_RELATIVE_BSIZE | NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE) ||
     446           0 :     aState->mReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE ||
     447           0 :     aState->mReflowInput.ComputedMinBSize() > 0 ||
     448           0 :     aState->mReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE;
     449             : }
     450             : 
     451             : void
     452         127 : nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowInput* aState,
     453             :                                        bool aAssumeHScroll,
     454             :                                        bool aAssumeVScroll,
     455             :                                        ReflowOutput* aMetrics,
     456             :                                        bool aFirstPass)
     457             : {
     458         127 :   WritingMode wm = mHelper.mScrolledFrame->GetWritingMode();
     459             : 
     460             :   // these could be NS_UNCONSTRAINEDSIZE ... std::min arithmetic should
     461             :   // be OK
     462         127 :   LogicalMargin padding = aState->mReflowInput.ComputedLogicalPadding();
     463             :   nscoord availISize =
     464         127 :     aState->mReflowInput.ComputedISize() + padding.IStartEnd(wm);
     465             : 
     466         127 :   nscoord computedBSize = aState->mReflowInput.ComputedBSize();
     467         127 :   nscoord computedMinBSize = aState->mReflowInput.ComputedMinBSize();
     468         127 :   nscoord computedMaxBSize = aState->mReflowInput.ComputedMaxBSize();
     469         127 :   if (!ShouldPropagateComputedBSizeToScrolledContent()) {
     470           0 :     computedBSize = NS_UNCONSTRAINEDSIZE;
     471           0 :     computedMinBSize = 0;
     472           0 :     computedMaxBSize = NS_UNCONSTRAINEDSIZE;
     473             :   }
     474             : 
     475         127 :   if (wm.IsVertical()) {
     476           0 :     if (aAssumeVScroll) {
     477           0 :       nsSize vScrollbarPrefSize;
     478           0 :       GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
     479           0 :                           nullptr, &vScrollbarPrefSize, false);
     480           0 :       if (computedBSize != NS_UNCONSTRAINEDSIZE) {
     481           0 :         computedBSize = std::max(0, computedBSize - vScrollbarPrefSize.width);
     482             :       }
     483           0 :       computedMinBSize = std::max(0, computedMinBSize - vScrollbarPrefSize.width);
     484           0 :       if (computedMaxBSize != NS_UNCONSTRAINEDSIZE) {
     485           0 :         computedMaxBSize = std::max(0, computedMaxBSize - vScrollbarPrefSize.width);
     486             :       }
     487             :     }
     488             : 
     489           0 :     if (aAssumeHScroll) {
     490           0 :       nsSize hScrollbarPrefSize;
     491           0 :       GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
     492           0 :                           nullptr, &hScrollbarPrefSize, true);
     493           0 :       availISize = std::max(0, availISize - hScrollbarPrefSize.height);
     494             :     }
     495             :   } else {
     496         127 :     if (aAssumeHScroll) {
     497           0 :       nsSize hScrollbarPrefSize;
     498           0 :       GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
     499           0 :                           nullptr, &hScrollbarPrefSize, false);
     500           0 :       if (computedBSize != NS_UNCONSTRAINEDSIZE) {
     501           0 :         computedBSize = std::max(0, computedBSize - hScrollbarPrefSize.height);
     502             :       }
     503           0 :       computedMinBSize = std::max(0, computedMinBSize - hScrollbarPrefSize.height);
     504           0 :       if (computedMaxBSize != NS_UNCONSTRAINEDSIZE) {
     505           0 :         computedMaxBSize = std::max(0, computedMaxBSize - hScrollbarPrefSize.height);
     506             :       }
     507             :     }
     508             : 
     509         127 :     if (aAssumeVScroll) {
     510           2 :       nsSize vScrollbarPrefSize;
     511           2 :       GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
     512           2 :                           nullptr, &vScrollbarPrefSize, true);
     513           2 :       availISize = std::max(0, availISize - vScrollbarPrefSize.width);
     514             :     }
     515             :   }
     516             : 
     517         127 :   nsPresContext* presContext = PresContext();
     518             : 
     519             :   // Pass false for aInit so we can pass in the correct padding.
     520             :   ReflowInput
     521             :     kidReflowInput(presContext, aState->mReflowInput,
     522             :                    mHelper.mScrolledFrame,
     523         254 :                    LogicalSize(wm, availISize, NS_UNCONSTRAINEDSIZE),
     524         127 :                    nullptr, ReflowInput::CALLER_WILL_INIT);
     525         127 :   const nsMargin physicalPadding = padding.GetPhysicalMargin(wm);
     526             :   kidReflowInput.Init(presContext, nullptr, nullptr,
     527         127 :                       &physicalPadding);
     528         127 :   kidReflowInput.mFlags.mAssumingHScrollbar = aAssumeHScroll;
     529         127 :   kidReflowInput.mFlags.mAssumingVScrollbar = aAssumeVScroll;
     530         127 :   kidReflowInput.SetComputedBSize(computedBSize);
     531         127 :   kidReflowInput.ComputedMinBSize() = computedMinBSize;
     532         127 :   kidReflowInput.ComputedMaxBSize() = computedMaxBSize;
     533         127 :   if (aState->mReflowInput.IsBResizeForWM(kidReflowInput.GetWritingMode())) {
     534          68 :     kidReflowInput.SetBResize(true);
     535             :   }
     536             : 
     537             :   // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to
     538             :   // reflect our assumptions while we reflow the child.
     539         127 :   bool didHaveHorizontalScrollbar = mHelper.mHasHorizontalScrollbar;
     540         127 :   bool didHaveVerticalScrollbar = mHelper.mHasVerticalScrollbar;
     541         127 :   mHelper.mHasHorizontalScrollbar = aAssumeHScroll;
     542         127 :   mHelper.mHasVerticalScrollbar = aAssumeVScroll;
     543             : 
     544         127 :   nsReflowStatus status;
     545             :   // No need to pass a true container-size to ReflowChild or
     546             :   // FinishReflowChild, because it's only used there when positioning
     547             :   // the frame (i.e. if NS_FRAME_NO_MOVE_FRAME isn't set)
     548         127 :   const nsSize dummyContainerSize;
     549         127 :   ReflowChild(mHelper.mScrolledFrame, presContext, *aMetrics,
     550         254 :               kidReflowInput, wm, LogicalPoint(wm), dummyContainerSize,
     551         127 :               NS_FRAME_NO_MOVE_FRAME, status);
     552             : 
     553         127 :   mHelper.mHasHorizontalScrollbar = didHaveHorizontalScrollbar;
     554         127 :   mHelper.mHasVerticalScrollbar = didHaveVerticalScrollbar;
     555             : 
     556             :   // Don't resize or position the view (if any) because we're going to resize
     557             :   // it to the correct size anyway in PlaceScrollArea. Allowing it to
     558             :   // resize here would size it to the natural height of the frame,
     559             :   // which will usually be different from the scrollport height;
     560             :   // invalidating the difference will cause unnecessary repainting.
     561         127 :   FinishReflowChild(mHelper.mScrolledFrame, presContext,
     562         254 :                     *aMetrics, &kidReflowInput, wm, LogicalPoint(wm),
     563             :                     dummyContainerSize,
     564         127 :                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
     565             : 
     566             :   // XXX Some frames (e.g., nsPluginFrame, nsFrameFrame, nsTextFrame) don't bother
     567             :   // setting their mOverflowArea. This is wrong because every frame should
     568             :   // always set mOverflowArea. In fact nsPluginFrame and nsFrameFrame don't
     569             :   // support the 'outline' property because of this. Rather than fix the world
     570             :   // right now, just fix up the overflow area if necessary. Note that we don't
     571             :   // check HasOverflowRect() because it could be set even though the
     572             :   // overflow area doesn't include the frame bounds.
     573         127 :   aMetrics->UnionOverflowAreasWithDesiredBounds();
     574             : 
     575         127 :   if (MOZ_UNLIKELY(StyleDisplay()->mOverflowClipBox ==
     576             :                      NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
     577          72 :     nsOverflowAreas childOverflow;
     578          36 :     nsLayoutUtils::UnionChildOverflow(mHelper.mScrolledFrame, childOverflow);
     579          72 :     nsRect childScrollableOverflow = childOverflow.ScrollableOverflow();
     580          36 :     childScrollableOverflow.Inflate(padding.GetPhysicalMargin(wm));
     581             :     nsRect contentArea =
     582          36 :       wm.IsVertical() ? nsRect(0, 0, computedBSize, availISize)
     583          72 :                       : nsRect(0, 0, availISize, computedBSize);
     584          36 :     if (!contentArea.Contains(childScrollableOverflow)) {
     585           2 :       aMetrics->mOverflowAreas.ScrollableOverflow() = childScrollableOverflow;
     586             :     }
     587             :   }
     588             : 
     589         127 :   aState->mContentsOverflowAreas = aMetrics->mOverflowAreas;
     590         127 :   aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
     591         127 :   aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
     592         127 : }
     593             : 
     594             : bool
     595         125 : nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowInput& aState)
     596             : {
     597         125 :   if (aState.mStyles.mHorizontal != NS_STYLE_OVERFLOW_AUTO)
     598             :     // no guessing required
     599         121 :     return aState.mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL;
     600             : 
     601           4 :   return mHelper.mHasHorizontalScrollbar;
     602             : }
     603             : 
     604             : bool
     605         125 : nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowInput& aState)
     606             : {
     607         125 :   if (aState.mStyles.mVertical != NS_STYLE_OVERFLOW_AUTO)
     608             :     // no guessing required
     609         121 :     return aState.mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL;
     610             : 
     611             :   // If we've had at least one non-initial reflow, then just assume
     612             :   // the state of the vertical scrollbar will be what we determined
     613             :   // last time.
     614           4 :   if (mHelper.mHadNonInitialReflow) {
     615           2 :     return mHelper.mHasVerticalScrollbar;
     616             :   }
     617             : 
     618             :   // If this is the initial reflow, guess false because usually
     619             :   // we have very little content by then.
     620           2 :   if (InInitialReflow())
     621           0 :     return false;
     622             : 
     623           2 :   if (mHelper.mIsRoot) {
     624           2 :     nsIFrame *f = mHelper.mScrolledFrame->PrincipalChildList().FirstChild();
     625           2 :     if (f && f->IsSVGOuterSVGFrame() &&
     626           0 :         static_cast<nsSVGOuterSVGFrame*>(f)->VerticalScrollbarNotNeeded()) {
     627             :       // Common SVG case - avoid a bad guess.
     628           0 :       return false;
     629             :     }
     630             :     // Assume that there will be a scrollbar; it seems to me
     631             :     // that 'most pages' do have a scrollbar, and anyway, it's cheaper
     632             :     // to do an extra reflow for the pages that *don't* need a
     633             :     // scrollbar (because on average they will have less content).
     634           2 :     return true;
     635             :   }
     636             : 
     637             :   // For non-viewports, just guess that we don't need a scrollbar.
     638             :   // XXX I wonder if statistically this is the right idea; I'm
     639             :   // basically guessing that there are a lot of overflow:auto DIVs
     640             :   // that get their intrinsic size and don't overflow
     641           0 :   return false;
     642             : }
     643             : 
     644             : bool
     645         127 : nsHTMLScrollFrame::InInitialReflow() const
     646             : {
     647             :   // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
     648             :   // root scrollframe.  In that case we want to skip this clause altogether.
     649             :   // The guess here is that there are lots of overflow:auto divs out there that
     650             :   // end up auto-sizing so they don't overflow, and that the root basically
     651             :   // always needs a scrollbar if it did last time we loaded this page (good
     652             :   // assumption, because our initial reflow is no longer synchronous).
     653         127 :   return !mHelper.mIsRoot && (GetStateBits() & NS_FRAME_FIRST_REFLOW);
     654             : }
     655             : 
     656             : void
     657         125 : nsHTMLScrollFrame::ReflowContents(ScrollReflowInput* aState,
     658             :                                   const ReflowOutput& aDesiredSize)
     659             : {
     660         125 :   ReflowOutput kidDesiredSize(aDesiredSize.GetWritingMode(), aDesiredSize.mFlags);
     661         125 :   ReflowScrolledFrame(aState, GuessHScrollbarNeeded(*aState),
     662         250 :                       GuessVScrollbarNeeded(*aState), &kidDesiredSize, true);
     663             : 
     664             :   // There's an important special case ... if the child appears to fit
     665             :   // in the inside-border rect (but overflows the scrollport), we
     666             :   // should try laying it out without a vertical scrollbar. It will
     667             :   // usually fit because making the available-width wider will not
     668             :   // normally make the child taller. (The only situation I can think
     669             :   // of is when you have a line containing %-width inline replaced
     670             :   // elements whose percentages sum to more than 100%, so increasing
     671             :   // the available width makes the line break where it was fitting
     672             :   // before.) If we don't treat this case specially, then we will
     673             :   // decide that showing scrollbars is OK because the content
     674             :   // overflows when we're showing scrollbars and we won't try to
     675             :   // remove the vertical scrollbar.
     676             : 
     677             :   // Detecting when we enter this special case is important for when
     678             :   // people design layouts that exactly fit the container "most of the
     679             :   // time".
     680             : 
     681             :   // XXX Is this check really sufficient to catch all the incremental cases
     682             :   // where the ideal case doesn't have a scrollbar?
     683         127 :   if ((aState->mReflowedContentsWithHScrollbar || aState->mReflowedContentsWithVScrollbar) &&
     684           4 :       aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
     685           2 :       aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
     686             :     nsSize insideBorderSize =
     687             :       ComputeInsideBorderSize(aState,
     688           2 :                               nsSize(kidDesiredSize.Width(), kidDesiredSize.Height()));
     689             :     nsRect scrolledRect =
     690           2 :       mHelper.GetUnsnappedScrolledRectInternal(kidDesiredSize.ScrollableOverflow(),
     691           4 :                                                insideBorderSize);
     692           2 :     if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
     693             :       // Let's pretend we had no scrollbars coming in here
     694           2 :       kidDesiredSize.mOverflowAreas.Clear();
     695           2 :       ReflowScrolledFrame(aState, false, false, &kidDesiredSize, false);
     696             :     }
     697             :   }
     698             : 
     699             :   // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
     700             :   // Do this first because changing the vertical scrollbar setting is expensive,
     701             :   // forcing a reflow always.
     702             : 
     703             :   // Try leaving the horizontal scrollbar unchanged first. This will be more
     704             :   // efficient.
     705         125 :   if (TryLayout(aState, &kidDesiredSize, aState->mReflowedContentsWithHScrollbar,
     706         125 :                 aState->mReflowedContentsWithVScrollbar, false))
     707         125 :     return;
     708           0 :   if (TryLayout(aState, &kidDesiredSize, !aState->mReflowedContentsWithHScrollbar,
     709           0 :                 aState->mReflowedContentsWithVScrollbar, false))
     710           0 :     return;
     711             : 
     712             :   // OK, now try toggling the vertical scrollbar. The performance advantage
     713             :   // of trying the status-quo horizontal scrollbar state
     714             :   // does not exist here (we'll have to reflow due to the vertical scrollbar
     715             :   // change), so always try no horizontal scrollbar first.
     716           0 :   bool newVScrollbarState = !aState->mReflowedContentsWithVScrollbar;
     717           0 :   if (TryLayout(aState, &kidDesiredSize, false, newVScrollbarState, false))
     718           0 :     return;
     719           0 :   if (TryLayout(aState, &kidDesiredSize, true, newVScrollbarState, false))
     720           0 :     return;
     721             : 
     722             :   // OK, we're out of ideas. Try again enabling whatever scrollbars we can
     723             :   // enable and force the layout to stick even if it's inconsistent.
     724             :   // This just happens sometimes.
     725           0 :   TryLayout(aState, &kidDesiredSize,
     726           0 :             aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN,
     727           0 :             aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN,
     728           0 :             true);
     729             : }
     730             : 
     731             : void
     732         125 : nsHTMLScrollFrame::PlaceScrollArea(ScrollReflowInput& aState,
     733             :                                    const nsPoint& aScrollPosition)
     734             : {
     735         125 :   nsIFrame *scrolledFrame = mHelper.mScrolledFrame;
     736             :   // Set the x,y of the scrolled frame to the correct value
     737         125 :   scrolledFrame->SetPosition(mHelper.mScrollPort.TopLeft() - aScrollPosition);
     738             : 
     739             :   // Recompute our scrollable overflow, taking perspective children into
     740             :   // account. Note that this only recomputes the overflow areas stored on the
     741             :   // helper (which are used to compute scrollable length and scrollbar thumb
     742             :   // sizes) but not the overflow areas stored on the frame. This seems to work
     743             :   // for now, but it's possible that we may need to update both in the future.
     744         125 :   AdjustForPerspective(aState.mContentsOverflowAreas.ScrollableOverflow());
     745             : 
     746         250 :   nsRect scrolledArea;
     747             :   // Preserve the width or height of empty rects
     748         125 :   nsSize portSize = mHelper.mScrollPort.Size();
     749             :   nsRect scrolledRect =
     750         125 :     mHelper.GetUnsnappedScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
     751         250 :                                              portSize);
     752             :   scrolledArea.UnionRectEdges(scrolledRect,
     753         125 :                               nsRect(nsPoint(0,0), portSize));
     754             : 
     755             :   // Store the new overflow area. Note that this changes where an outline
     756             :   // of the scrolled frame would be painted, but scrolled frames can't have
     757             :   // outlines (the outline would go on this scrollframe instead).
     758             :   // Using FinishAndStoreOverflow is needed so the overflow rect
     759             :   // gets set correctly.  It also messes with the overflow rect in the
     760             :   // -moz-hidden-unscrollable case, but scrolled frames can't have
     761             :   // 'overflow' either.
     762             :   // This needs to happen before SyncFrameViewAfterReflow so
     763             :   // HasOverflowRect() will return the correct value.
     764         250 :   nsOverflowAreas overflow(scrolledArea, scrolledArea);
     765         125 :   scrolledFrame->FinishAndStoreOverflow(overflow,
     766         125 :                                         scrolledFrame->GetSize());
     767             : 
     768             :   // Note that making the view *exactly* the size of the scrolled area
     769             :   // is critical, since the view scrolling code uses the size of the
     770             :   // scrolled view to clamp scroll requests.
     771             :   // Normally the scrolledFrame won't have a view but in some cases it
     772             :   // might create its own.
     773         125 :   nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame->PresContext(),
     774             :                                              scrolledFrame,
     775             :                                              scrolledFrame->GetView(),
     776             :                                              scrolledArea,
     777         125 :                                              0);
     778         125 : }
     779             : 
     780             : nscoord
     781         152 : nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(gfxContext *aRenderingContext)
     782             : {
     783         304 :   ScrollbarStyles ss = GetScrollbarStyles();
     784         152 :   if (ss.mVertical != NS_STYLE_OVERFLOW_SCROLL || !mHelper.mVScrollbarBox)
     785         152 :     return 0;
     786             : 
     787             :   // Don't need to worry about reflow depth here since it's
     788             :   // just for scrollbars
     789           0 :   nsBoxLayoutState bls(PresContext(), aRenderingContext, 0);
     790           0 :   nsSize vScrollbarPrefSize(0, 0);
     791           0 :   GetScrollbarMetrics(bls, mHelper.mVScrollbarBox,
     792           0 :                       nullptr, &vScrollbarPrefSize, true);
     793           0 :   return vScrollbarPrefSize.width;
     794             : }
     795             : 
     796             : /* virtual */ nscoord
     797          82 : nsHTMLScrollFrame::GetMinISize(gfxContext *aRenderingContext)
     798             : {
     799          82 :   nscoord result = mHelper.mScrolledFrame->GetMinISize(aRenderingContext);
     800         164 :   DISPLAY_MIN_WIDTH(this, result);
     801         164 :   return result + GetIntrinsicVScrollbarWidth(aRenderingContext);
     802             : }
     803             : 
     804             : /* virtual */ nscoord
     805          70 : nsHTMLScrollFrame::GetPrefISize(gfxContext *aRenderingContext)
     806             : {
     807          70 :   nscoord result = mHelper.mScrolledFrame->GetPrefISize(aRenderingContext);
     808         140 :   DISPLAY_PREF_WIDTH(this, result);
     809         140 :   return NSCoordSaturatingAdd(result, GetIntrinsicVScrollbarWidth(aRenderingContext));
     810             : }
     811             : 
     812             : nsresult
     813           0 : nsHTMLScrollFrame::GetXULPadding(nsMargin& aMargin)
     814             : {
     815             :   // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
     816             :   // reaize that.  If we're stuck inside a XUL box, we need to claim no
     817             :   // padding.
     818             :   // @see also nsXULScrollFrame::GetXULPadding.
     819           0 :   aMargin.SizeTo(0,0,0,0);
     820           0 :   return NS_OK;
     821             : }
     822             : 
     823             : bool
     824           0 : nsHTMLScrollFrame::IsXULCollapsed()
     825             : {
     826             :   // We're never collapsed in the box sense.
     827           0 :   return false;
     828             : }
     829             : 
     830             : // Return the <browser> if the scrollframe is for the root frame directly
     831             : // inside a <browser>.
     832             : static nsIContent*
     833          43 : GetBrowserRoot(nsIContent* aContent)
     834             : {
     835          43 :   if (aContent) {
     836          43 :     nsIDocument* doc = aContent->GetUncomposedDoc();
     837          43 :     if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
     838           8 :       nsCOMPtr<Element> frameElement = win->GetFrameElementInternal();
     839           4 :       if (frameElement &&
     840           4 :           frameElement->NodeInfo()->Equals(nsGkAtoms::browser, kNameSpaceID_XUL))
     841           0 :         return frameElement;
     842             :     }
     843             :   }
     844             : 
     845          43 :   return nullptr;
     846             : }
     847             : 
     848             : // When we have perspective set on the outer scroll frame, and transformed
     849             : // children (possibly with preserve-3d) then the effective transform on the
     850             : // child depends on the offset to the scroll frame, which changes as we scroll.
     851             : // This perspective transform can cause the element to move relative to the
     852             : // scrolled inner frame, which would cause the scrollable length changes during
     853             : // scrolling if we didn't account for it. Since we don't want scrollHeight/Width
     854             : // and the size of scrollbar thumbs to change during scrolling, we compute the
     855             : // scrollable overflow by determining the scroll position at which the child
     856             : // becomes completely visible within the scrollport rather than using the union
     857             : // of the overflow areas at their current position.
     858             : void
     859           0 : GetScrollableOverflowForPerspective(nsIFrame* aScrolledFrame,
     860             :                                     nsIFrame* aCurrentFrame,
     861             :                                     const nsRect aScrollPort,
     862             :                                     nsPoint aOffset,
     863             :                                     nsRect& aScrolledFrameOverflowArea)
     864             : {
     865             :   // Iterate over all children except pop-ups.
     866           0 :   FrameChildListIDs skip = nsIFrame::kSelectPopupList | nsIFrame::kPopupList;
     867           0 :   for (nsIFrame::ChildListIterator childLists(aCurrentFrame);
     868           0 :        !childLists.IsDone(); childLists.Next()) {
     869           0 :     if (skip.Contains(childLists.CurrentID())) {
     870           0 :       continue;
     871             :     }
     872             : 
     873           0 :     for (nsIFrame* child : childLists.CurrentList()) {
     874           0 :       nsPoint offset = aOffset;
     875             : 
     876             :       // When we reach a direct child of the scroll, then we record the offset
     877             :       // to convert from that frame's coordinate into the scroll frame's
     878             :       // coordinates. Preserve-3d descendant frames use the same offset as their
     879             :       // ancestors, since TransformRect already converts us into the coordinate
     880             :       // space of the preserve-3d root.
     881           0 :       if (aScrolledFrame == aCurrentFrame) {
     882           0 :         offset = child->GetPosition();
     883             :       }
     884             : 
     885           0 :       if (child->Extend3DContext()) {
     886             :         // If we're a preserve-3d frame, then recurse and include our
     887             :         // descendants since overflow of preserve-3d frames is only included
     888             :         // in the post-transform overflow area of the preserve-3d root frame.
     889           0 :         GetScrollableOverflowForPerspective(aScrolledFrame, child, aScrollPort,
     890           0 :                                             offset, aScrolledFrameOverflowArea);
     891             :       }
     892             : 
     893             :       // If we're transformed, then we want to consider the possibility that
     894             :       // this frame might move relative to the scrolled frame when scrolling.
     895             :       // For preserve-3d, leaf frames have correct overflow rects relative to
     896             :       // themselves. preserve-3d 'nodes' (intermediate frames and the root) have
     897             :       // only their untransformed children included in their overflow relative
     898             :       // to self, which is what we want to include here.
     899           0 :       if (child->IsTransformed()) {
     900             :         // Compute the overflow rect for this leaf transform frame in the
     901             :         // coordinate space of the scrolled frame.
     902           0 :         nsPoint scrollPos = aScrolledFrame->GetPosition();
     903             :         nsRect preScroll = nsDisplayTransform::TransformRect(
     904           0 :           child->GetScrollableOverflowRectRelativeToSelf(), child);
     905             : 
     906             :         // Temporarily override the scroll position of the scrolled frame by
     907             :         // 10 CSS pixels, and then recompute what the overflow rect would be.
     908             :         // This scroll position may not be valid, but that shouldn't matter
     909             :         // for our calculations.
     910           0 :         aScrolledFrame->SetPosition(scrollPos + nsPoint(600, 600));
     911             :         nsRect postScroll = nsDisplayTransform::TransformRect(
     912           0 :           child->GetScrollableOverflowRectRelativeToSelf(), child);
     913           0 :         aScrolledFrame->SetPosition(scrollPos);
     914             : 
     915             :         // Compute how many app units the overflow rects moves by when we adjust
     916             :         // the scroll position by 1 app unit.
     917             :         double rightDelta =
     918           0 :           (postScroll.XMost() - preScroll.XMost() + 600.0) / 600.0;
     919             :         double bottomDelta =
     920           0 :           (postScroll.YMost() - preScroll.YMost() + 600.0) / 600.0;
     921             : 
     922             :         // We can't ever have negative scrolling.
     923           0 :         NS_ASSERTION(rightDelta > 0.0f && bottomDelta > 0.0f,
     924             :                      "Scrolling can't be reversed!");
     925             : 
     926             :         // Move preScroll into the coordinate space of the scrollport.
     927           0 :         preScroll += offset + scrollPos;
     928             : 
     929             :         // For each of the four edges of preScroll, figure out how far they
     930             :         // extend beyond the scrollport. Ignore negative values since that means
     931             :         // that side is already scrolled in to view and we don't need to add
     932             :         // overflow to account for it.
     933           0 :         nsMargin overhang(std::max(0, aScrollPort.Y() - preScroll.Y()),
     934           0 :                           std::max(0, preScroll.XMost() - aScrollPort.XMost()),
     935           0 :                           std::max(0, preScroll.YMost() - aScrollPort.YMost()),
     936           0 :                           std::max(0, aScrollPort.X() - preScroll.X()));
     937             : 
     938             :         // Scale according to rightDelta/bottomDelta to adjust for the different
     939             :         // scroll rates.
     940           0 :         overhang.top /= bottomDelta;
     941           0 :         overhang.right /= rightDelta;
     942           0 :         overhang.bottom /= bottomDelta;
     943           0 :         overhang.left /= rightDelta;
     944             : 
     945             :         // Take the minimum overflow rect that would allow the current scroll
     946             :         // position, using the size of the scroll port and offset by the
     947             :         // inverse of the scroll position.
     948           0 :         nsRect overflow = aScrollPort - scrollPos;
     949             : 
     950             :         // Expand it by our margins to get an overflow rect that would allow all
     951             :         // edges of our transformed content to be scrolled into view.
     952           0 :         overflow.Inflate(overhang);
     953             : 
     954             :         // Merge it with the combined overflow
     955             :         aScrolledFrameOverflowArea.UnionRect(aScrolledFrameOverflowArea,
     956           0 :                                              overflow);
     957           0 :       } else if (aCurrentFrame == aScrolledFrame) {
     958             :         aScrolledFrameOverflowArea.UnionRect(
     959             :           aScrolledFrameOverflowArea,
     960           0 :           child->GetScrollableOverflowRectRelativeToParent());
     961             :       }
     962             :     }
     963             :   }
     964           0 : }
     965             : 
     966             : void
     967         125 : nsHTMLScrollFrame::AdjustForPerspective(nsRect& aScrollableOverflow)
     968             : {
     969             :   // If we have perspective that is being applied to our children, then
     970             :   // the effective transform on the child depends on the relative position
     971             :   // of the child to us and changes during scrolling.
     972         125 :   if (!ChildrenHavePerspective()) {
     973         125 :     return;
     974             :   }
     975           0 :   aScrollableOverflow.SetEmpty();
     976           0 :   GetScrollableOverflowForPerspective(mHelper.mScrolledFrame,
     977             :                                       mHelper.mScrolledFrame,
     978             :                                       mHelper.mScrollPort,
     979           0 :                                       nsPoint(), aScrollableOverflow);
     980             : }
     981             : 
     982             : void
     983         125 : nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
     984             :                           ReflowOutput&     aDesiredSize,
     985             :                           const ReflowInput& aReflowInput,
     986             :                           nsReflowStatus&          aStatus)
     987             : {
     988         125 :   MarkInReflow();
     989         125 :   DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
     990         250 :   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
     991             : 
     992         125 :   mHelper.HandleScrollbarStyleSwitching();
     993             : 
     994         250 :   ScrollReflowInput state(this, aReflowInput);
     995             :   // sanity check: ensure that if we have no scrollbar, we treat it
     996             :   // as hidden.
     997         125 :   if (!mHelper.mVScrollbarBox || mHelper.mNeverHasVerticalScrollbar)
     998         121 :     state.mStyles.mVertical = NS_STYLE_OVERFLOW_HIDDEN;
     999         125 :   if (!mHelper.mHScrollbarBox || mHelper.mNeverHasHorizontalScrollbar)
    1000         121 :     state.mStyles.mHorizontal = NS_STYLE_OVERFLOW_HIDDEN;
    1001             : 
    1002             :   //------------ Handle Incremental Reflow -----------------
    1003         125 :   bool reflowHScrollbar = true;
    1004         125 :   bool reflowVScrollbar = true;
    1005         125 :   bool reflowScrollCorner = true;
    1006         125 :   if (!aReflowInput.ShouldReflowAllKids()) {
    1007             :     #define NEEDS_REFLOW(frame_) \
    1008             :       ((frame_) && NS_SUBTREE_DIRTY(frame_))
    1009             : 
    1010          39 :     reflowHScrollbar = NEEDS_REFLOW(mHelper.mHScrollbarBox);
    1011          39 :     reflowVScrollbar = NEEDS_REFLOW(mHelper.mVScrollbarBox);
    1012          78 :     reflowScrollCorner = NEEDS_REFLOW(mHelper.mScrollCornerBox) ||
    1013          39 :                          NEEDS_REFLOW(mHelper.mResizerBox);
    1014             : 
    1015             :     #undef NEEDS_REFLOW
    1016             :   }
    1017             : 
    1018         125 :   if (mHelper.mIsRoot) {
    1019          43 :     mHelper.mCollapsedResizer = true;
    1020             : 
    1021          43 :     nsIContent* browserRoot = GetBrowserRoot(mContent);
    1022          43 :     if (browserRoot) {
    1023           0 :       bool showResizer = browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer);
    1024           0 :       reflowScrollCorner = showResizer == mHelper.mCollapsedResizer;
    1025           0 :       mHelper.mCollapsedResizer = !showResizer;
    1026             :     }
    1027             :   }
    1028             : 
    1029         250 :   nsRect oldScrollAreaBounds = mHelper.mScrollPort;
    1030             :   nsRect oldScrolledAreaBounds =
    1031         250 :     mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
    1032         125 :   nsPoint oldScrollPosition = mHelper.GetScrollPosition();
    1033             : 
    1034         125 :   state.mComputedBorder = aReflowInput.ComputedPhysicalBorderPadding() -
    1035             :     aReflowInput.ComputedPhysicalPadding();
    1036             : 
    1037         125 :   ReflowContents(&state, aDesiredSize);
    1038             : 
    1039         250 :   aDesiredSize.Width() = state.mInsideBorderSize.width +
    1040         125 :     state.mComputedBorder.LeftRight();
    1041         250 :   aDesiredSize.Height() = state.mInsideBorderSize.height +
    1042         125 :     state.mComputedBorder.TopBottom();
    1043             : 
    1044             :   // Set the size of the frame now since computing the perspective-correct
    1045             :   // overflow (within PlaceScrollArea) can rely on it.
    1046         125 :   SetSize(aDesiredSize.GetWritingMode(),
    1047         250 :           aDesiredSize.Size(aDesiredSize.GetWritingMode()));
    1048             : 
    1049             :   // Restore the old scroll position, for now, even if that's not valid anymore
    1050             :   // because we changed size. We'll fix it up in a post-reflow callback, because
    1051             :   // our current size may only be temporary (e.g. we're compute XUL desired sizes).
    1052         125 :   PlaceScrollArea(state, oldScrollPosition);
    1053         125 :   if (!mHelper.mPostedReflowCallback) {
    1054             :     // Make sure we'll try scrolling to restored position
    1055         117 :     PresContext()->PresShell()->PostReflowCallback(&mHelper);
    1056         117 :     mHelper.mPostedReflowCallback = true;
    1057             :   }
    1058             : 
    1059         125 :   bool didHaveHScrollbar = mHelper.mHasHorizontalScrollbar;
    1060         125 :   bool didHaveVScrollbar = mHelper.mHasVerticalScrollbar;
    1061         125 :   mHelper.mHasHorizontalScrollbar = state.mShowHScrollbar;
    1062         125 :   mHelper.mHasVerticalScrollbar = state.mShowVScrollbar;
    1063         250 :   nsRect newScrollAreaBounds = mHelper.mScrollPort;
    1064             :   nsRect newScrolledAreaBounds =
    1065         250 :     mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
    1066         375 :   if (mHelper.mSkippedScrollbarLayout ||
    1067          78 :       reflowHScrollbar || reflowVScrollbar || reflowScrollCorner ||
    1068          78 :       (GetStateBits() & NS_FRAME_IS_DIRTY) ||
    1069          78 :       didHaveHScrollbar != state.mShowHScrollbar ||
    1070          78 :       didHaveVScrollbar != state.mShowVScrollbar ||
    1071         203 :       !oldScrollAreaBounds.IsEqualEdges(newScrollAreaBounds) ||
    1072          39 :       !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
    1073          86 :     if (!mHelper.mSuppressScrollbarUpdate) {
    1074          86 :       mHelper.mSkippedScrollbarLayout = false;
    1075          86 :       mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, state.mShowHScrollbar);
    1076          86 :       mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, state.mShowVScrollbar);
    1077             :       // place and reflow scrollbars
    1078             :       nsRect insideBorderArea =
    1079         172 :         nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
    1080         172 :                state.mInsideBorderSize);
    1081          86 :       mHelper.LayoutScrollbars(state.mBoxState, insideBorderArea,
    1082          86 :                               oldScrollAreaBounds);
    1083             :     } else {
    1084           0 :       mHelper.mSkippedScrollbarLayout = true;
    1085             :     }
    1086             :   }
    1087             : 
    1088         125 :   aDesiredSize.SetOverflowAreasToDesiredBounds();
    1089         125 :   if (mHelper.IsIgnoringViewportClipping()) {
    1090           0 :     aDesiredSize.mOverflowAreas.UnionWith(
    1091           0 :       state.mContentsOverflowAreas + mHelper.mScrolledFrame->GetPosition());
    1092             :   }
    1093             : 
    1094         125 :   mHelper.UpdateSticky();
    1095         125 :   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
    1096             : 
    1097         125 :   if (!InInitialReflow() && !mHelper.mHadNonInitialReflow) {
    1098          35 :     mHelper.mHadNonInitialReflow = true;
    1099             :   }
    1100             : 
    1101         125 :   if (mHelper.mIsRoot && !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
    1102          20 :     mHelper.PostScrolledAreaEvent();
    1103             :   }
    1104             : 
    1105         125 :   mHelper.UpdatePrevScrolledRect();
    1106             : 
    1107         125 :   aStatus.Reset();
    1108         125 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
    1109         125 :   mHelper.PostOverflowEvent();
    1110         125 : }
    1111             : 
    1112             : 
    1113             : ////////////////////////////////////////////////////////////////////////////////
    1114             : 
    1115             : #ifdef DEBUG_FRAME_DUMP
    1116             : nsresult
    1117           0 : nsHTMLScrollFrame::GetFrameName(nsAString& aResult) const
    1118             : {
    1119           0 :   return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult);
    1120             : }
    1121             : #endif
    1122             : 
    1123             : #ifdef ACCESSIBILITY
    1124             : a11y::AccType
    1125           0 : nsHTMLScrollFrame::AccessibleType()
    1126             : {
    1127           0 :   if (IsTableCaption()) {
    1128           0 :     return GetRect().IsEmpty() ? a11y::eNoType : a11y::eHTMLCaptionType;
    1129             :   }
    1130             : 
    1131             :   // Create an accessible regardless of focusable state because the state can be
    1132             :   // changed during frame life cycle without any notifications to accessibility.
    1133           0 :   if (mContent->IsRootOfNativeAnonymousSubtree() ||
    1134           0 :       GetScrollbarStyles().IsHiddenInBothDirections()) {
    1135           0 :     return a11y::eNoType;
    1136             :   }
    1137             : 
    1138           0 :   return a11y::eHyperTextType;
    1139             : }
    1140             : #endif
    1141             : 
    1142        1366 : NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
    1143          35 :   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
    1144         751 :   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
    1145          16 :   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
    1146           8 :   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
    1147         556 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
    1148             : 
    1149             : //----------nsXULScrollFrame-------------------------------------------
    1150             : 
    1151             : nsXULScrollFrame*
    1152           8 : NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
    1153             :                      bool aIsRoot, bool aClipAllDescendants)
    1154             : {
    1155             :   return new (aPresShell) nsXULScrollFrame(aContext, aIsRoot,
    1156           8 :                                            aClipAllDescendants);
    1157             : }
    1158             : 
    1159           8 : NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
    1160             : 
    1161           8 : nsXULScrollFrame::nsXULScrollFrame(nsStyleContext* aContext,
    1162             :                                    bool aIsRoot,
    1163           8 :                                    bool aClipAllDescendants)
    1164             :   : nsBoxFrame(aContext, kClassID, aIsRoot)
    1165           8 :   , mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
    1166             : {
    1167           8 :   SetXULLayoutManager(nullptr);
    1168           8 :   mHelper.mClipAllDescendants = aClipAllDescendants;
    1169           8 : }
    1170             : 
    1171             : void
    1172           0 : nsXULScrollFrame::ScrollbarActivityStarted() const
    1173             : {
    1174           0 :   if (mHelper.mScrollbarActivity) {
    1175           0 :     mHelper.mScrollbarActivity->ActivityStarted();
    1176             :   }
    1177           0 : }
    1178             : 
    1179             : void
    1180           0 : nsXULScrollFrame::ScrollbarActivityStopped() const
    1181             : {
    1182           0 :   if (mHelper.mScrollbarActivity) {
    1183           0 :     mHelper.mScrollbarActivity->ActivityStopped();
    1184             :   }
    1185           0 : }
    1186             : 
    1187             : nsMargin
    1188           0 : ScrollFrameHelper::GetDesiredScrollbarSizes(nsBoxLayoutState* aState)
    1189             : {
    1190           0 :   NS_ASSERTION(aState && aState->GetRenderingContext(),
    1191             :                "Must have rendering context in layout state for size "
    1192             :                "computations");
    1193             : 
    1194           0 :   nsMargin result(0, 0, 0, 0);
    1195             : 
    1196           0 :   if (mVScrollbarBox) {
    1197           0 :     nsSize size = mVScrollbarBox->GetXULPrefSize(*aState);
    1198           0 :     nsBox::AddMargin(mVScrollbarBox, size);
    1199           0 :     if (IsScrollbarOnRight())
    1200           0 :       result.left = size.width;
    1201             :     else
    1202           0 :       result.right = size.width;
    1203             :   }
    1204             : 
    1205           0 :   if (mHScrollbarBox) {
    1206           0 :     nsSize size = mHScrollbarBox->GetXULPrefSize(*aState);
    1207           0 :     nsBox::AddMargin(mHScrollbarBox, size);
    1208             :     // We don't currently support any scripts that would require a scrollbar
    1209             :     // at the top. (Are there any?)
    1210           0 :     result.bottom = size.height;
    1211             :   }
    1212             : 
    1213           0 :   return result;
    1214             : }
    1215             : 
    1216             : nscoord
    1217           0 : ScrollFrameHelper::GetNondisappearingScrollbarWidth(nsBoxLayoutState* aState,
    1218             :                                                     WritingMode aWM)
    1219             : {
    1220           0 :   NS_ASSERTION(aState && aState->GetRenderingContext(),
    1221             :                "Must have rendering context in layout state for size "
    1222             :                "computations");
    1223             : 
    1224           0 :   bool verticalWM = aWM.IsVertical();
    1225           0 :   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
    1226             :     // We're using overlay scrollbars, so we need to get the width that
    1227             :     // non-disappearing scrollbars would have.
    1228           0 :     nsITheme* theme = aState->PresContext()->GetTheme();
    1229           0 :     if (theme &&
    1230           0 :         theme->ThemeSupportsWidget(aState->PresContext(),
    1231             :                                    verticalWM ? mHScrollbarBox
    1232             :                                               : mVScrollbarBox,
    1233           0 :                                    NS_THEME_SCROLLBAR_NON_DISAPPEARING)) {
    1234           0 :       LayoutDeviceIntSize size;
    1235           0 :       bool canOverride = true;
    1236           0 :       theme->GetMinimumWidgetSize(aState->PresContext(),
    1237             :                                   verticalWM ? mHScrollbarBox
    1238             :                                              : mVScrollbarBox,
    1239             :                                   NS_THEME_SCROLLBAR_NON_DISAPPEARING,
    1240             :                                   &size,
    1241           0 :                                   &canOverride);
    1242             :       return aState->PresContext()->
    1243           0 :              DevPixelsToAppUnits(verticalWM ? size.height : size.width);
    1244             :     }
    1245             :   }
    1246             : 
    1247           0 :   nsMargin sizes(GetDesiredScrollbarSizes(aState));
    1248           0 :   return verticalWM ? sizes.TopBottom() : sizes.LeftRight();
    1249             : }
    1250             : 
    1251             : void
    1252         125 : ScrollFrameHelper::HandleScrollbarStyleSwitching()
    1253             : {
    1254             :   // Check if we switched between scrollbar styles.
    1255         125 :   if (mScrollbarActivity &&
    1256         125 :       LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) == 0) {
    1257           0 :     mScrollbarActivity->Destroy();
    1258           0 :     mScrollbarActivity = nullptr;
    1259           0 :     mOuter->PresContext()->ThemeChanged();
    1260             :   }
    1261         250 :   else if (!mScrollbarActivity &&
    1262         125 :            LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
    1263           0 :     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(mOuter));
    1264           0 :     mOuter->PresContext()->ThemeChanged();
    1265             :   }
    1266         125 : }
    1267             : 
    1268             : #if defined(MOZ_WIDGET_ANDROID)
    1269             : static bool IsFocused(nsIContent* aContent)
    1270             : {
    1271             :   // Some content elements, like the GetContent() of a scroll frame
    1272             :   // for a text input field, are inside anonymous subtrees, but the focus
    1273             :   // manager always reports a non-anonymous element as the focused one, so
    1274             :   // walk up the tree until we reach a non-anonymous element.
    1275             :   while (aContent && aContent->IsInAnonymousSubtree()) {
    1276             :     aContent = aContent->GetParent();
    1277             :   }
    1278             : 
    1279             :   return aContent ? nsContentUtils::IsFocusedContent(aContent) : false;
    1280             : }
    1281             : #endif
    1282             : 
    1283             : void
    1284           0 : ScrollFrameHelper::SetScrollableByAPZ(bool aScrollable)
    1285             : {
    1286           0 :   mScrollableByAPZ = aScrollable;
    1287           0 : }
    1288             : 
    1289             : void
    1290           1 : ScrollFrameHelper::SetZoomableByAPZ(bool aZoomable)
    1291             : {
    1292           1 :   if (mZoomableByAPZ != aZoomable) {
    1293             :     // We might be changing the result of WantAsyncScroll() so schedule a
    1294             :     // paint to make sure we pick up the result of that change.
    1295           0 :     mZoomableByAPZ = aZoomable;
    1296           0 :     mOuter->SchedulePaint();
    1297             :   }
    1298           1 : }
    1299             : 
    1300             : bool
    1301         462 : ScrollFrameHelper::WantAsyncScroll() const
    1302             : {
    1303             :   // If zooming is allowed, and this is a frame that's allowed to zoom, then
    1304             :   // we want it to be async-scrollable or zooming will not be permitted.
    1305         462 :   if (mZoomableByAPZ) {
    1306           0 :     return true;
    1307             :   }
    1308             : 
    1309         924 :   ScrollbarStyles styles = GetScrollbarStylesFromFrame();
    1310         462 :   nscoord oneDevPixel = GetScrolledFrame()->PresContext()->AppUnitsPerDevPixel();
    1311         924 :   nsRect scrollRange = GetScrollRange();
    1312         558 :   bool isVScrollable = (scrollRange.height >= oneDevPixel) &&
    1313         558 :                        (styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
    1314         520 :   bool isHScrollable = (scrollRange.width >= oneDevPixel) &&
    1315         520 :                        (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN);
    1316             : 
    1317             : #if defined(MOZ_WIDGET_ANDROID)
    1318             :   // Mobile platforms need focus to scroll.
    1319             :   bool canScrollWithoutScrollbars = IsFocused(mOuter->GetContent());
    1320             : #else
    1321         462 :   bool canScrollWithoutScrollbars = true;
    1322             : #endif
    1323             : 
    1324             :   // The check for scroll bars was added in bug 825692 to prevent layerization
    1325             :   // of text inputs for performance reasons.
    1326         462 :   bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || canScrollWithoutScrollbars);
    1327         462 :   bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || canScrollWithoutScrollbars);
    1328         462 :   return isVAsyncScrollable || isHAsyncScrollable;
    1329             : }
    1330             : 
    1331             : static nsRect
    1332           0 : GetOnePixelRangeAroundPoint(nsPoint aPoint, bool aIsHorizontal)
    1333             : {
    1334           0 :   nsRect allowedRange(aPoint, nsSize());
    1335           0 :   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
    1336           0 :   if (aIsHorizontal) {
    1337           0 :     allowedRange.x = aPoint.x - halfPixel;
    1338           0 :     allowedRange.width = halfPixel*2 - 1;
    1339             :   } else {
    1340           0 :     allowedRange.y = aPoint.y - halfPixel;
    1341           0 :     allowedRange.height = halfPixel*2 - 1;
    1342             :   }
    1343           0 :   return allowedRange;
    1344             : }
    1345             : 
    1346             : void
    1347           0 : ScrollFrameHelper::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
    1348             :                                 nsIScrollbarMediator::ScrollSnapMode aSnap)
    1349             : {
    1350             :   ScrollByUnit(aScrollbar, nsIScrollableFrame::SMOOTH, aDirection,
    1351           0 :                nsIScrollableFrame::PAGES, aSnap);
    1352           0 : }
    1353             : 
    1354             : void
    1355           0 : ScrollFrameHelper::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
    1356             :                                  nsIScrollbarMediator::ScrollSnapMode aSnap)
    1357             : {
    1358             :   ScrollByUnit(aScrollbar, nsIScrollableFrame::INSTANT, aDirection,
    1359           0 :                nsIScrollableFrame::WHOLE, aSnap);
    1360           0 : }
    1361             : 
    1362             : void
    1363           0 : ScrollFrameHelper::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
    1364             :                                 nsIScrollbarMediator::ScrollSnapMode aSnap)
    1365             : {
    1366           0 :   bool isHorizontal = aScrollbar->IsXULHorizontal();
    1367           0 :   nsIntPoint delta;
    1368           0 :   if (isHorizontal) {
    1369             :     const double kScrollMultiplier =
    1370           0 :       Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
    1371           0 :                           NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
    1372           0 :     delta.x = aDirection * kScrollMultiplier;
    1373           0 :     if (GetLineScrollAmount().width * delta.x > GetPageScrollAmount().width) {
    1374             :       // The scroll frame is so small that the delta would be more
    1375             :       // than an entire page.  Scroll by one page instead to maintain
    1376             :       // context.
    1377           0 :       ScrollByPage(aScrollbar, aDirection);
    1378           0 :       return;
    1379             :     }
    1380             :   } else {
    1381             :     const double kScrollMultiplier =
    1382           0 :       Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
    1383           0 :                           NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
    1384           0 :     delta.y = aDirection * kScrollMultiplier;
    1385           0 :     if (GetLineScrollAmount().height * delta.y > GetPageScrollAmount().height) {
    1386             :       // The scroll frame is so small that the delta would be more
    1387             :       // than an entire page.  Scroll by one page instead to maintain
    1388             :       // context.
    1389           0 :       ScrollByPage(aScrollbar, aDirection);
    1390           0 :       return;
    1391             :     }
    1392             :   }
    1393             : 
    1394           0 :   nsIntPoint overflow;
    1395             :   ScrollBy(delta, nsIScrollableFrame::LINES, nsIScrollableFrame::SMOOTH,
    1396             :            &overflow, nsGkAtoms::other, nsIScrollableFrame::NOT_MOMENTUM,
    1397           0 :            aSnap);
    1398             : }
    1399             : 
    1400             : void
    1401           0 : ScrollFrameHelper::RepeatButtonScroll(nsScrollbarFrame* aScrollbar)
    1402             : {
    1403           0 :   aScrollbar->MoveToNewPosition();
    1404           0 : }
    1405             : 
    1406             : void
    1407           0 : ScrollFrameHelper::ThumbMoved(nsScrollbarFrame* aScrollbar,
    1408             :                               nscoord aOldPos,
    1409             :                               nscoord aNewPos)
    1410             : {
    1411           0 :   MOZ_ASSERT(aScrollbar != nullptr);
    1412           0 :   bool isHorizontal = aScrollbar->IsXULHorizontal();
    1413           0 :   nsPoint current = GetScrollPosition();
    1414           0 :   nsPoint dest = current;
    1415           0 :   if (isHorizontal) {
    1416           0 :     dest.x = IsPhysicalLTR() ? aNewPos : aNewPos - GetScrollRange().width;
    1417             :   } else {
    1418           0 :     dest.y = aNewPos;
    1419             :   }
    1420           0 :   nsRect allowedRange = GetOnePixelRangeAroundPoint(dest, isHorizontal);
    1421             : 
    1422             :   // Don't try to scroll if we're already at an acceptable place.
    1423             :   // Don't call Contains here since Contains returns false when the point is
    1424             :   // on the bottom or right edge of the rectangle.
    1425           0 :   if (allowedRange.ClampPoint(current) == current) {
    1426           0 :     return;
    1427             :   }
    1428             : 
    1429           0 :   ScrollTo(dest, nsIScrollableFrame::INSTANT, &allowedRange);
    1430             : }
    1431             : 
    1432             : void
    1433           0 : ScrollFrameHelper::ScrollbarReleased(nsScrollbarFrame* aScrollbar)
    1434             : {
    1435             :   // Scrollbar scrolling does not result in fling gestures, clear any
    1436             :   // accumulated velocity
    1437           0 :   mVelocityQueue.Reset();
    1438             : 
    1439             :   // Perform scroll snapping, if needed.  Scrollbar movement uses the same
    1440             :   // smooth scrolling animation as keyboard scrolling.
    1441           0 :   ScrollSnap(mDestination, nsIScrollableFrame::SMOOTH);
    1442           0 : }
    1443             : 
    1444             : void
    1445           0 : ScrollFrameHelper::ScrollByUnit(nsScrollbarFrame* aScrollbar,
    1446             :                                 nsIScrollableFrame::ScrollMode aMode,
    1447             :                                 int32_t aDirection,
    1448             :                                 nsIScrollableFrame::ScrollUnit aUnit,
    1449             :                                 nsIScrollbarMediator::ScrollSnapMode aSnap)
    1450             : {
    1451           0 :   MOZ_ASSERT(aScrollbar != nullptr);
    1452           0 :   bool isHorizontal = aScrollbar->IsXULHorizontal();
    1453           0 :   nsIntPoint delta;
    1454           0 :   if (isHorizontal) {
    1455           0 :     delta.x = aDirection;
    1456             :   } else {
    1457           0 :     delta.y = aDirection;
    1458             :   }
    1459           0 :   nsIntPoint overflow;
    1460             :   ScrollBy(delta, aUnit, aMode, &overflow, nsGkAtoms::other,
    1461           0 :            nsIScrollableFrame::NOT_MOMENTUM, aSnap);
    1462           0 : }
    1463             : 
    1464             : nsresult
    1465           8 : nsXULScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
    1466             : {
    1467           8 :   return mHelper.CreateAnonymousContent(aElements);
    1468             : }
    1469             : 
    1470             : void
    1471           0 : nsXULScrollFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
    1472             :                                            uint32_t aFilter)
    1473             : {
    1474           0 :   mHelper.AppendAnonymousContentTo(aElements, aFilter);
    1475           0 : }
    1476             : 
    1477             : void
    1478           1 : nsXULScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
    1479             : {
    1480           1 :   mHelper.Destroy();
    1481           1 :   nsBoxFrame::DestroyFrom(aDestructRoot);
    1482           1 : }
    1483             : 
    1484             : void
    1485           8 : nsXULScrollFrame::SetInitialChildList(ChildListID     aListID,
    1486             :                                       nsFrameList&    aChildList)
    1487             : {
    1488           8 :   nsBoxFrame::SetInitialChildList(aListID, aChildList);
    1489           8 :   if (aListID == kPrincipalList) {
    1490           8 :     mHelper.ReloadChildFrames();
    1491             :   }
    1492           8 : }
    1493             : 
    1494             : 
    1495             : void
    1496           8 : nsXULScrollFrame::AppendFrames(ChildListID     aListID,
    1497             :                                nsFrameList&    aFrameList)
    1498             : {
    1499           8 :   nsBoxFrame::AppendFrames(aListID, aFrameList);
    1500           8 :   mHelper.ReloadChildFrames();
    1501           8 : }
    1502             : 
    1503             : void
    1504           0 : nsXULScrollFrame::InsertFrames(ChildListID     aListID,
    1505             :                                nsIFrame*       aPrevFrame,
    1506             :                                nsFrameList&    aFrameList)
    1507             : {
    1508           0 :   nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
    1509           0 :   mHelper.ReloadChildFrames();
    1510           0 : }
    1511             : 
    1512             : void
    1513           0 : nsXULScrollFrame::RemoveFrame(ChildListID     aListID,
    1514             :                               nsIFrame*       aOldFrame)
    1515             : {
    1516           0 :   nsBoxFrame::RemoveFrame(aListID, aOldFrame);
    1517           0 :   mHelper.ReloadChildFrames();
    1518           0 : }
    1519             : 
    1520             : nsSplittableType
    1521           0 : nsXULScrollFrame::GetSplittableType() const
    1522             : {
    1523           0 :   return NS_FRAME_NOT_SPLITTABLE;
    1524             : }
    1525             : 
    1526             : nsresult
    1527         824 : nsXULScrollFrame::GetXULPadding(nsMargin& aMargin)
    1528             : {
    1529         824 :   aMargin.SizeTo(0,0,0,0);
    1530         824 :   return NS_OK;
    1531             : }
    1532             : 
    1533             : nscoord
    1534         174 : nsXULScrollFrame::GetXULBoxAscent(nsBoxLayoutState& aState)
    1535             : {
    1536         174 :   if (!mHelper.mScrolledFrame)
    1537           0 :     return 0;
    1538             : 
    1539         174 :   nscoord ascent = mHelper.mScrolledFrame->GetXULBoxAscent(aState);
    1540         174 :   nsMargin m(0,0,0,0);
    1541         174 :   GetXULBorderAndPadding(m);
    1542         174 :   ascent += m.top;
    1543         174 :   GetXULMargin(m);
    1544         174 :   ascent += m.top;
    1545             : 
    1546         174 :   return ascent;
    1547             : }
    1548             : 
    1549             : nsSize
    1550         203 : nsXULScrollFrame::GetXULPrefSize(nsBoxLayoutState& aState)
    1551             : {
    1552             : #ifdef DEBUG_LAYOUT
    1553             :   PropagateDebug(aState);
    1554             : #endif
    1555             : 
    1556         203 :   nsSize pref = mHelper.mScrolledFrame->GetXULPrefSize(aState);
    1557             : 
    1558         406 :   ScrollbarStyles styles = GetScrollbarStyles();
    1559             : 
    1560             :   // scrolled frames don't have their own margins
    1561             : 
    1562         203 :   if (mHelper.mVScrollbarBox &&
    1563           0 :       styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
    1564           0 :     nsSize vSize = mHelper.mVScrollbarBox->GetXULPrefSize(aState);
    1565           0 :     nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
    1566           0 :     pref.width += vSize.width;
    1567             :   }
    1568             : 
    1569         203 :   if (mHelper.mHScrollbarBox &&
    1570           0 :       styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
    1571           0 :     nsSize hSize = mHelper.mHScrollbarBox->GetXULPrefSize(aState);
    1572           0 :     nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
    1573           0 :     pref.height += hSize.height;
    1574             :   }
    1575             : 
    1576         203 :   AddBorderAndPadding(pref);
    1577             :   bool widthSet, heightSet;
    1578         203 :   nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet);
    1579         406 :   return pref;
    1580             : }
    1581             : 
    1582             : nsSize
    1583         236 : nsXULScrollFrame::GetXULMinSize(nsBoxLayoutState& aState)
    1584             : {
    1585             : #ifdef DEBUG_LAYOUT
    1586             :   PropagateDebug(aState);
    1587             : #endif
    1588             : 
    1589         236 :   nsSize min = mHelper.mScrolledFrame->GetXULMinSizeForScrollArea(aState);
    1590             : 
    1591         472 :   ScrollbarStyles styles = GetScrollbarStyles();
    1592             : 
    1593         236 :   if (mHelper.mVScrollbarBox &&
    1594           0 :       styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
    1595           0 :      nsSize vSize = mHelper.mVScrollbarBox->GetXULMinSize(aState);
    1596           0 :      AddMargin(mHelper.mVScrollbarBox, vSize);
    1597           0 :      min.width += vSize.width;
    1598           0 :      if (min.height < vSize.height)
    1599           0 :         min.height = vSize.height;
    1600             :   }
    1601             : 
    1602         236 :   if (mHelper.mHScrollbarBox &&
    1603           0 :       styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
    1604           0 :      nsSize hSize = mHelper.mHScrollbarBox->GetXULMinSize(aState);
    1605           0 :      AddMargin(mHelper.mHScrollbarBox, hSize);
    1606           0 :      min.height += hSize.height;
    1607           0 :      if (min.width < hSize.width)
    1608           0 :         min.width = hSize.width;
    1609             :   }
    1610             : 
    1611         236 :   AddBorderAndPadding(min);
    1612             :   bool widthSet, heightSet;
    1613         236 :   nsIFrame::AddXULMinSize(aState, this, min, widthSet, heightSet);
    1614         472 :   return min;
    1615             : }
    1616             : 
    1617             : nsSize
    1618         173 : nsXULScrollFrame::GetXULMaxSize(nsBoxLayoutState& aState)
    1619             : {
    1620             : #ifdef DEBUG_LAYOUT
    1621             :   PropagateDebug(aState);
    1622             : #endif
    1623             : 
    1624         173 :   nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
    1625             : 
    1626         173 :   AddBorderAndPadding(maxSize);
    1627             :   bool widthSet, heightSet;
    1628         173 :   nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet);
    1629         173 :   return maxSize;
    1630             : }
    1631             : 
    1632             : #ifdef DEBUG_FRAME_DUMP
    1633             : nsresult
    1634           0 : nsXULScrollFrame::GetFrameName(nsAString& aResult) const
    1635             : {
    1636           0 :   return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult);
    1637             : }
    1638             : #endif
    1639             : 
    1640             : NS_IMETHODIMP
    1641          38 : nsXULScrollFrame::DoXULLayout(nsBoxLayoutState& aState)
    1642             : {
    1643          38 :   uint32_t flags = aState.LayoutFlags();
    1644          38 :   nsresult rv = XULLayout(aState);
    1645          38 :   aState.SetLayoutFlags(flags);
    1646             : 
    1647          38 :   nsBox::DoXULLayout(aState);
    1648          38 :   return rv;
    1649             : }
    1650             : 
    1651         548 : NS_QUERYFRAME_HEAD(nsXULScrollFrame)
    1652           8 :   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
    1653         414 :   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
    1654           3 :   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
    1655           0 :   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
    1656         123 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
    1657             : 
    1658             : //-------------------- Helper ----------------------
    1659             : 
    1660             : #define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
    1661             : 
    1662             : // AsyncSmoothMSDScroll has ref counting.
    1663             : class ScrollFrameHelper::AsyncSmoothMSDScroll final : public nsARefreshObserver {
    1664             : public:
    1665           0 :   AsyncSmoothMSDScroll(const nsPoint &aInitialPosition,
    1666             :                        const nsPoint &aInitialDestination,
    1667             :                        const nsSize &aInitialVelocity,
    1668             :                        const nsRect &aRange,
    1669             :                        const mozilla::TimeStamp &aStartTime,
    1670             :                        nsPresContext* aPresContext)
    1671           0 :     : mXAxisModel(aInitialPosition.x, aInitialDestination.x,
    1672           0 :                   aInitialVelocity.width,
    1673           0 :                   gfxPrefs::ScrollBehaviorSpringConstant(),
    1674           0 :                   gfxPrefs::ScrollBehaviorDampingRatio())
    1675           0 :     , mYAxisModel(aInitialPosition.y, aInitialDestination.y,
    1676           0 :                   aInitialVelocity.height,
    1677           0 :                   gfxPrefs::ScrollBehaviorSpringConstant(),
    1678           0 :                   gfxPrefs::ScrollBehaviorDampingRatio())
    1679             :     , mRange(aRange)
    1680             :     , mLastRefreshTime(aStartTime)
    1681             :     , mCallee(nullptr)
    1682           0 :     , mOneDevicePixelInAppUnits(aPresContext->DevPixelsToAppUnits(1))
    1683             :   {
    1684             :     Telemetry::SetHistogramRecordingEnabled(
    1685           0 :       Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, true);
    1686           0 :   }
    1687             : 
    1688           0 :   NS_INLINE_DECL_REFCOUNTING(AsyncSmoothMSDScroll, override)
    1689             : 
    1690           0 :   nsSize GetVelocity() {
    1691             :     // In nscoords per second
    1692           0 :     return nsSize(mXAxisModel.GetVelocity(), mYAxisModel.GetVelocity());
    1693             :   }
    1694             : 
    1695           0 :   nsPoint GetPosition() {
    1696             :     // In nscoords
    1697           0 :     return nsPoint(NSToCoordRound(mXAxisModel.GetPosition()), NSToCoordRound(mYAxisModel.GetPosition()));
    1698             :   }
    1699             : 
    1700           0 :   void SetDestination(const nsPoint &aDestination) {
    1701           0 :     mXAxisModel.SetDestination(static_cast<int32_t>(aDestination.x));
    1702           0 :     mYAxisModel.SetDestination(static_cast<int32_t>(aDestination.y));
    1703           0 :   }
    1704             : 
    1705             :   void SetRange(const nsRect &aRange)
    1706             :   {
    1707             :     mRange = aRange;
    1708             :   }
    1709             : 
    1710           0 :   nsRect GetRange()
    1711             :   {
    1712           0 :     return mRange;
    1713             :   }
    1714             : 
    1715           0 :   void Simulate(const TimeDuration& aDeltaTime)
    1716             :   {
    1717           0 :     mXAxisModel.Simulate(aDeltaTime);
    1718           0 :     mYAxisModel.Simulate(aDeltaTime);
    1719             : 
    1720           0 :     nsPoint desired = GetPosition();
    1721           0 :     nsPoint clamped = mRange.ClampPoint(desired);
    1722           0 :     if(desired.x != clamped.x) {
    1723             :       // The scroll has hit the "wall" at the left or right edge of the allowed
    1724             :       // scroll range.
    1725             :       // Absorb the impact to avoid bounceback effect.
    1726           0 :       mXAxisModel.SetVelocity(0.0);
    1727           0 :       mXAxisModel.SetPosition(clamped.x);
    1728             :     }
    1729             : 
    1730           0 :     if(desired.y != clamped.y) {
    1731             :       // The scroll has hit the "wall" at the left or right edge of the allowed
    1732             :       // scroll range.
    1733             :       // Absorb the impact to avoid bounceback effect.
    1734           0 :       mYAxisModel.SetVelocity(0.0);
    1735           0 :       mYAxisModel.SetPosition(clamped.y);
    1736             :     }
    1737           0 :   }
    1738             : 
    1739           0 :   bool IsFinished()
    1740             :   {
    1741           0 :     return mXAxisModel.IsFinished(mOneDevicePixelInAppUnits) &&
    1742           0 :            mYAxisModel.IsFinished(mOneDevicePixelInAppUnits);
    1743             :   }
    1744             : 
    1745           0 :   virtual void WillRefresh(mozilla::TimeStamp aTime) override {
    1746           0 :     mozilla::TimeDuration deltaTime = aTime - mLastRefreshTime;
    1747           0 :     mLastRefreshTime = aTime;
    1748             : 
    1749             :     // The callback may release "this".
    1750             :     // We don't access members after returning, so no need for KungFuDeathGrip.
    1751           0 :     ScrollFrameHelper::AsyncSmoothMSDScrollCallback(mCallee, deltaTime);
    1752           0 :   }
    1753             : 
    1754             :   /*
    1755             :    * Set a refresh observer for smooth scroll iterations (and start observing).
    1756             :    * Should be used at most once during the lifetime of this object.
    1757             :    * Return value: true on success, false otherwise.
    1758             :    */
    1759           0 :   bool SetRefreshObserver(ScrollFrameHelper *aCallee) {
    1760           0 :     NS_ASSERTION(aCallee && !mCallee, "AsyncSmoothMSDScroll::SetRefreshObserver - Invalid usage.");
    1761             : 
    1762           0 :     if (!RefreshDriver(aCallee)->AddRefreshObserver(this, FlushType::Style)) {
    1763           0 :       return false;
    1764             :     }
    1765             : 
    1766           0 :     mCallee = aCallee;
    1767           0 :     return true;
    1768             :   }
    1769             : 
    1770             : private:
    1771             :   // Private destructor, to discourage deletion outside of Release():
    1772           0 :   ~AsyncSmoothMSDScroll() {
    1773           0 :     RemoveObserver();
    1774             :     Telemetry::SetHistogramRecordingEnabled(
    1775           0 :       Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, false);
    1776           0 :   }
    1777             : 
    1778           0 :   nsRefreshDriver* RefreshDriver(ScrollFrameHelper* aCallee) {
    1779           0 :     return aCallee->mOuter->PresContext()->RefreshDriver();
    1780             :   }
    1781             : 
    1782             :   /*
    1783             :    * The refresh driver doesn't hold a reference to its observers,
    1784             :    *   so releasing this object can (and is) used to remove the observer on DTOR.
    1785             :    * Currently, this object is released once the scrolling ends.
    1786             :    */
    1787           0 :   void RemoveObserver() {
    1788           0 :     if (mCallee) {
    1789           0 :       RefreshDriver(mCallee)->RemoveRefreshObserver(this, FlushType::Style);
    1790             :     }
    1791           0 :   }
    1792             : 
    1793             :   mozilla::layers::AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
    1794             :   nsRect mRange;
    1795             :   mozilla::TimeStamp mLastRefreshTime;
    1796             :   ScrollFrameHelper *mCallee;
    1797             :   nscoord mOneDevicePixelInAppUnits;
    1798             : };
    1799             : 
    1800             : // AsyncScroll has ref counting.
    1801             : class ScrollFrameHelper::AsyncScroll final
    1802             :   : public nsARefreshObserver,
    1803             :     public AsyncScrollBase
    1804             : {
    1805             : public:
    1806             :   typedef mozilla::TimeStamp TimeStamp;
    1807             :   typedef mozilla::TimeDuration TimeDuration;
    1808             : 
    1809           0 :   explicit AsyncScroll(nsPoint aStartPos)
    1810           0 :     : AsyncScrollBase(aStartPos)
    1811           0 :     , mCallee(nullptr)
    1812             :   {
    1813             :     Telemetry::SetHistogramRecordingEnabled(
    1814           0 :       Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, true);
    1815           0 :   }
    1816             : 
    1817             : private:
    1818             :   // Private destructor, to discourage deletion outside of Release():
    1819           0 :   ~AsyncScroll() {
    1820           0 :     RemoveObserver();
    1821             :     Telemetry::SetHistogramRecordingEnabled(
    1822           0 :       Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, false);
    1823           0 :   }
    1824             : 
    1825             : public:
    1826             :   void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
    1827             :                         nsIAtom *aOrigin, const nsRect& aRange,
    1828             :                         const nsSize& aCurrentVelocity);
    1829           0 :   void Init(const nsRect& aRange) {
    1830           0 :     mRange = aRange;
    1831           0 :   }
    1832             : 
    1833             :   // Most recent scroll origin.
    1834             :   nsCOMPtr<nsIAtom> mOrigin;
    1835             : 
    1836             :   // Allowed destination positions around mDestination
    1837             :   nsRect mRange;
    1838             :   bool mIsSmoothScroll;
    1839             : 
    1840             : private:
    1841             :   void InitPreferences(TimeStamp aTime, nsIAtom *aOrigin);
    1842             : 
    1843             : // The next section is observer/callback management
    1844             : // Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
    1845             : public:
    1846           0 :   NS_INLINE_DECL_REFCOUNTING(AsyncScroll, override)
    1847             : 
    1848             :   /*
    1849             :    * Set a refresh observer for smooth scroll iterations (and start observing).
    1850             :    * Should be used at most once during the lifetime of this object.
    1851             :    * Return value: true on success, false otherwise.
    1852             :    */
    1853           0 :   bool SetRefreshObserver(ScrollFrameHelper *aCallee) {
    1854           0 :     NS_ASSERTION(aCallee && !mCallee, "AsyncScroll::SetRefreshObserver - Invalid usage.");
    1855             : 
    1856           0 :     if (!RefreshDriver(aCallee)->AddRefreshObserver(this, FlushType::Style)) {
    1857           0 :       return false;
    1858             :     }
    1859             : 
    1860           0 :     mCallee = aCallee;
    1861           0 :     APZCCallbackHelper::SuppressDisplayport(true, mCallee->mOuter->PresContext()->PresShell());
    1862           0 :     return true;
    1863             :   }
    1864             : 
    1865           0 :   virtual void WillRefresh(mozilla::TimeStamp aTime) override {
    1866             :     // The callback may release "this".
    1867             :     // We don't access members after returning, so no need for KungFuDeathGrip.
    1868           0 :     ScrollFrameHelper::AsyncScrollCallback(mCallee, aTime);
    1869           0 :   }
    1870             : 
    1871             : private:
    1872             :   ScrollFrameHelper *mCallee;
    1873             : 
    1874           0 :   nsRefreshDriver* RefreshDriver(ScrollFrameHelper* aCallee) {
    1875           0 :     return aCallee->mOuter->PresContext()->RefreshDriver();
    1876             :   }
    1877             : 
    1878             :   /*
    1879             :    * The refresh driver doesn't hold a reference to its observers,
    1880             :    *   so releasing this object can (and is) used to remove the observer on DTOR.
    1881             :    * Currently, this object is released once the scrolling ends.
    1882             :    */
    1883           0 :   void RemoveObserver() {
    1884           0 :     if (mCallee) {
    1885           0 :       RefreshDriver(mCallee)->RemoveRefreshObserver(this, FlushType::Style);
    1886           0 :       APZCCallbackHelper::SuppressDisplayport(false, mCallee->mOuter->PresContext()->PresShell());
    1887             :     }
    1888           0 :   }
    1889             : };
    1890             : 
    1891             : /*
    1892             :  * Calculate duration, possibly dynamically according to events rate and event origin.
    1893             :  * (also maintain previous timestamps - which are only used here).
    1894             :  */
    1895             : void
    1896           0 : ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin)
    1897             : {
    1898           0 :   if (!aOrigin || aOrigin == nsGkAtoms::restore) {
    1899             :     // We don't have special prefs for "restore", just treat it as "other".
    1900             :     // "restore" scrolls are (for now) always instant anyway so unless something
    1901             :     // changes we should never have aOrigin == nsGkAtoms::restore here.
    1902           0 :     aOrigin = nsGkAtoms::other;
    1903             :   }
    1904             :   // Likewise we should never get APZ-triggered scrolls here, and if that changes
    1905             :   // something is likely broken somewhere.
    1906           0 :   MOZ_ASSERT(aOrigin != nsGkAtoms::apz);
    1907             : 
    1908             :   // Read preferences only on first iteration or for a different event origin.
    1909           0 :   if (!mIsFirstIteration && aOrigin == mOrigin) {
    1910           0 :     return;
    1911             :   }
    1912             : 
    1913           0 :   mOrigin = aOrigin;
    1914           0 :   mOriginMinMS = mOriginMaxMS = 0;
    1915           0 :   bool isOriginSmoothnessEnabled = false;
    1916           0 :   mIntervalRatio = 1;
    1917             : 
    1918             :   // Default values for all preferences are defined in all.js
    1919             :   static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
    1920             :   static const bool kDefaultIsSmoothEnabled = true;
    1921             : 
    1922           0 :   nsAutoCString originName;
    1923           0 :   aOrigin->ToUTF8String(originName);
    1924           0 :   nsAutoCString prefBase = NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
    1925             : 
    1926           0 :   isOriginSmoothnessEnabled = Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
    1927           0 :   if (isOriginSmoothnessEnabled) {
    1928           0 :     nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
    1929           0 :     nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
    1930           0 :     mOriginMinMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
    1931           0 :     mOriginMaxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
    1932             : 
    1933             :     static const int32_t kSmoothScrollMaxAllowedAnimationDurationMS = 10000;
    1934           0 :     mOriginMaxMS = clamped(mOriginMaxMS, 0, kSmoothScrollMaxAllowedAnimationDurationMS);
    1935           0 :     mOriginMinMS = clamped(mOriginMinMS, 0, mOriginMaxMS);
    1936             :   }
    1937             : 
    1938             :   // Keep the animation duration longer than the average event intervals
    1939             :   //   (to "connect" consecutive scroll animations before the scroll comes to a stop).
    1940             :   static const double kDefaultDurationToIntervalRatio = 2; // Duplicated at all.js
    1941           0 :   mIntervalRatio = Preferences::GetInt("general.smoothScroll.durationToIntervalRatio",
    1942           0 :                                        kDefaultDurationToIntervalRatio * 100) / 100.0;
    1943             : 
    1944             :   // Duration should be at least as long as the intervals -> ratio is at least 1
    1945           0 :   mIntervalRatio = std::max(1.0, mIntervalRatio);
    1946             : 
    1947           0 :   if (mIsFirstIteration) {
    1948           0 :     InitializeHistory(aTime);
    1949             :   }
    1950             : }
    1951             : 
    1952             : void
    1953           0 : ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
    1954             :                                                  nsPoint aDestination,
    1955             :                                                  nsIAtom *aOrigin,
    1956             :                                                  const nsRect& aRange,
    1957             :                                                  const nsSize& aCurrentVelocity)
    1958             : {
    1959           0 :   InitPreferences(aTime, aOrigin);
    1960           0 :   mRange = aRange;
    1961             : 
    1962           0 :   Update(aTime, aDestination, aCurrentVelocity);
    1963           0 : }
    1964             : 
    1965             : bool
    1966           0 : ScrollFrameHelper::IsSmoothScrollingEnabled()
    1967             : {
    1968           0 :   return Preferences::GetBool(SMOOTH_SCROLL_PREF_NAME, false);
    1969             : }
    1970             : 
    1971             : class ScrollFrameActivityTracker final : public nsExpirationTracker<ScrollFrameHelper,4> {
    1972             : public:
    1973             :   // Wait for 3-4s between scrolls before we remove our layers.
    1974             :   // That's 4 generations of 1s each.
    1975             :   enum { TIMEOUT_MS = 1000 };
    1976           0 :   explicit ScrollFrameActivityTracker(nsIEventTarget* aEventTarget)
    1977           0 :     : nsExpirationTracker<ScrollFrameHelper,4>(TIMEOUT_MS,
    1978             :                                                "ScrollFrameActivityTracker",
    1979           0 :                                                aEventTarget)
    1980           0 :   {}
    1981           0 :   ~ScrollFrameActivityTracker() {
    1982           0 :     AgeAllGenerations();
    1983           0 :   }
    1984             : 
    1985           0 :   virtual void NotifyExpired(ScrollFrameHelper *aObject) {
    1986           0 :     RemoveObject(aObject);
    1987           0 :     aObject->MarkNotRecentlyScrolled();
    1988           0 :   }
    1989             : };
    1990             : 
    1991             : static ScrollFrameActivityTracker *gScrollFrameActivityTracker = nullptr;
    1992             : 
    1993             : // There are situations when a scroll frame is destroyed and then re-created
    1994             : // for the same content element. In this case we want to increment the scroll
    1995             : // generation between the old and new scrollframes. If the new one knew about
    1996             : // the old one then it could steal the old generation counter and increment it
    1997             : // but it doesn't have that reference so instead we use a static global to
    1998             : // ensure the new one gets a fresh value.
    1999             : static uint32_t sScrollGenerationCounter = 0;
    2000             : 
    2001          43 : ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
    2002          43 :                                              bool aIsRoot)
    2003             :   : mHScrollbarBox(nullptr)
    2004             :   , mVScrollbarBox(nullptr)
    2005             :   , mScrolledFrame(nullptr)
    2006             :   , mScrollCornerBox(nullptr)
    2007             :   , mResizerBox(nullptr)
    2008             :   , mOuter(aOuter)
    2009             :   , mAsyncScroll(nullptr)
    2010             :   , mAsyncSmoothMSDScroll(nullptr)
    2011             :   , mLastScrollOrigin(nsGkAtoms::other)
    2012             :   , mAllowScrollOriginDowngrade(false)
    2013             :   , mLastSmoothScrollOrigin(nullptr)
    2014          86 :   , mScrollGeneration(++sScrollGenerationCounter)
    2015             :   , mDestination(0, 0)
    2016             :   , mScrollPosAtLastPaint(0, 0)
    2017             :   , mRestorePos(-1, -1)
    2018             :   , mLastPos(-1, -1)
    2019             :   , mScrollPosForLayerPixelAlignment(-1, -1)
    2020             :   , mLastUpdateFramesPos(-1, -1)
    2021             :   , mHadDisplayPortAtLastFrameUpdate(false)
    2022             :   , mDisplayPortAtLastFrameUpdate()
    2023             :   , mNeverHasVerticalScrollbar(false)
    2024             :   , mNeverHasHorizontalScrollbar(false)
    2025             :   , mHasVerticalScrollbar(false)
    2026             :   , mHasHorizontalScrollbar(false)
    2027             :   , mFrameIsUpdatingScrollbar(false)
    2028             :   , mDidHistoryRestore(false)
    2029             :   , mIsRoot(aIsRoot)
    2030             :   , mClipAllDescendants(aIsRoot)
    2031             :   , mSuppressScrollbarUpdate(false)
    2032             :   , mSkippedScrollbarLayout(false)
    2033             :   , mHadNonInitialReflow(false)
    2034             :   , mHorizontalOverflow(false)
    2035             :   , mVerticalOverflow(false)
    2036             :   , mPostedReflowCallback(false)
    2037             :   , mMayHaveDirtyFixedChildren(false)
    2038             :   , mUpdateScrollbarAttributes(false)
    2039             :   , mHasBeenScrolledRecently(false)
    2040             :   , mCollapsedResizer(false)
    2041             :   , mWillBuildScrollableLayer(false)
    2042             :   , mIsScrollParent(false)
    2043             :   , mIsScrollableLayerInRootContainer(false)
    2044             :   , mHasBeenScrolled(false)
    2045             :   , mIgnoreMomentumScroll(false)
    2046             :   , mTransformingByAPZ(false)
    2047             :   , mScrollableByAPZ(false)
    2048             :   , mZoomableByAPZ(false)
    2049             :   , mSuppressScrollbarRepaints(false)
    2050         129 :   , mVelocityQueue(aOuter->PresContext())
    2051             : {
    2052          43 :   if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
    2053           0 :     mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
    2054             :   }
    2055             : 
    2056          43 :   EnsureFrameVisPrefsCached();
    2057             : 
    2058          87 :   if (IsAlwaysActive() &&
    2059           1 :       gfxPrefs::LayersTilesEnabled() &&
    2060          43 :       !nsLayoutUtils::UsesAsyncScrolling(mOuter) &&
    2061           0 :       mOuter->GetContent()) {
    2062             :     // If we have tiling but no APZ, then set a 0-margin display port on
    2063             :     // active scroll containers so that we paint by whole tile increments
    2064             :     // when scrolling.
    2065           0 :     nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
    2066           0 :                                          mOuter->PresContext()->PresShell(),
    2067           0 :                                          ScreenMargin(),
    2068             :                                          0,
    2069           0 :                                          nsLayoutUtils::RepaintMode::DoNotRepaint);
    2070             :     nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
    2071           0 :         mOuter, nsLayoutUtils::RepaintMode::DoNotRepaint);
    2072             :   }
    2073             : 
    2074          43 : }
    2075             : 
    2076           7 : ScrollFrameHelper::~ScrollFrameHelper()
    2077             : {
    2078           7 : }
    2079             : 
    2080             : /*
    2081             :  * Callback function from AsyncSmoothMSDScroll, used in ScrollFrameHelper::ScrollTo
    2082             :  */
    2083             : void
    2084           0 : ScrollFrameHelper::AsyncSmoothMSDScrollCallback(ScrollFrameHelper* aInstance,
    2085             :                                                 mozilla::TimeDuration aDeltaTime)
    2086             : {
    2087           0 :   NS_ASSERTION(aInstance != nullptr, "aInstance must not be null");
    2088           0 :   NS_ASSERTION(aInstance->mAsyncSmoothMSDScroll,
    2089             :     "Did not expect AsyncSmoothMSDScrollCallback without an active MSD scroll.");
    2090             : 
    2091           0 :   nsRect range = aInstance->mAsyncSmoothMSDScroll->GetRange();
    2092           0 :   aInstance->mAsyncSmoothMSDScroll->Simulate(aDeltaTime);
    2093             : 
    2094           0 :   if (!aInstance->mAsyncSmoothMSDScroll->IsFinished()) {
    2095           0 :     nsPoint destination = aInstance->mAsyncSmoothMSDScroll->GetPosition();
    2096             :     // Allow this scroll operation to land on any pixel boundary within the
    2097             :     // allowed scroll range for this frame.
    2098             :     // If the MSD is under-dampened or the destination is changed rapidly,
    2099             :     // it is expected (and desired) that the scrolling may overshoot.
    2100             :     nsRect intermediateRange =
    2101           0 :       nsRect(destination, nsSize()).UnionEdges(range);
    2102           0 :     aInstance->ScrollToImpl(destination, intermediateRange);
    2103             :     // 'aInstance' might be destroyed here
    2104           0 :     return;
    2105             :   }
    2106             : 
    2107           0 :   aInstance->CompleteAsyncScroll(range);
    2108             : }
    2109             : 
    2110             : /*
    2111             :  * Callback function from AsyncScroll, used in ScrollFrameHelper::ScrollTo
    2112             :  */
    2113             : void
    2114           0 : ScrollFrameHelper::AsyncScrollCallback(ScrollFrameHelper* aInstance,
    2115             :                                        mozilla::TimeStamp aTime)
    2116             : {
    2117           0 :   MOZ_ASSERT(aInstance != nullptr, "aInstance must not be null");
    2118           0 :   MOZ_ASSERT(aInstance->mAsyncScroll,
    2119             :     "Did not expect AsyncScrollCallback without an active async scroll.");
    2120             : 
    2121           0 :   if (!aInstance || !aInstance->mAsyncScroll) {
    2122           0 :     return;  // XXX wallpaper bug 1107353 for now.
    2123             :   }
    2124             : 
    2125           0 :   nsRect range = aInstance->mAsyncScroll->mRange;
    2126           0 :   if (aInstance->mAsyncScroll->mIsSmoothScroll) {
    2127           0 :     if (!aInstance->mAsyncScroll->IsFinished(aTime)) {
    2128           0 :       nsPoint destination = aInstance->mAsyncScroll->PositionAt(aTime);
    2129             :       // Allow this scroll operation to land on any pixel boundary between the
    2130             :       // current position and the final allowed range.  (We don't want
    2131             :       // intermediate steps to be more constrained than the final step!)
    2132             :       nsRect intermediateRange =
    2133           0 :         nsRect(aInstance->GetScrollPosition(), nsSize()).UnionEdges(range);
    2134           0 :       aInstance->ScrollToImpl(destination, intermediateRange);
    2135             :       // 'aInstance' might be destroyed here
    2136           0 :       return;
    2137             :     }
    2138             :   }
    2139             : 
    2140           0 :   aInstance->CompleteAsyncScroll(range);
    2141             : }
    2142             : 
    2143             : void
    2144           0 : ScrollFrameHelper::CompleteAsyncScroll(const nsRect &aRange, nsIAtom* aOrigin)
    2145             : {
    2146             :   // Apply desired destination range since this is the last step of scrolling.
    2147           0 :   mAsyncSmoothMSDScroll = nullptr;
    2148           0 :   mAsyncScroll = nullptr;
    2149           0 :   AutoWeakFrame weakFrame(mOuter);
    2150           0 :   ScrollToImpl(mDestination, aRange, aOrigin);
    2151           0 :   if (!weakFrame.IsAlive()) {
    2152           0 :     return;
    2153             :   }
    2154             :   // We are done scrolling, set our destination to wherever we actually ended
    2155             :   // up scrolling to.
    2156           0 :   mDestination = GetScrollPosition();
    2157             : }
    2158             : 
    2159             : bool
    2160           0 : ScrollFrameHelper::HasPluginFrames()
    2161             : {
    2162             : #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
    2163           0 :   if (XRE_IsContentProcess()) {
    2164           0 :     nsPresContext* presContext = mOuter->PresContext();
    2165           0 :     nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
    2166           0 :     if (!rootPresContext || rootPresContext->NeedToComputePluginGeometryUpdates()) {
    2167           0 :       return true;
    2168             :     }
    2169             :   }
    2170             : #endif
    2171           0 :   return false;
    2172             : }
    2173             : 
    2174             : bool
    2175           0 : ScrollFrameHelper::HasPerspective() const
    2176             : {
    2177           0 :   const nsStyleDisplay* disp = mOuter->StyleDisplay();
    2178           0 :   return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
    2179             : }
    2180             : 
    2181             : bool
    2182           0 : ScrollFrameHelper::HasBgAttachmentLocal() const
    2183             : {
    2184           0 :   const nsStyleBackground* bg = mOuter->StyleBackground();
    2185           0 :   return bg->HasLocalBackground();
    2186             : }
    2187             : 
    2188             : void
    2189           0 : ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
    2190             :                                      nsIScrollableFrame::ScrollMode aMode)
    2191             : {
    2192           0 :   nsPoint current = GetScrollPosition();
    2193           0 :   CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
    2194           0 :   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
    2195           0 :   nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
    2196           0 :   nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
    2197             :   // XXX I don't think the following blocks are needed anymore, now that
    2198             :   // ScrollToImpl simply tries to scroll an integer number of layer
    2199             :   // pixels from the current position
    2200           0 :   if (currentCSSPixels.x == aScrollPosition.x) {
    2201           0 :     pt.x = current.x;
    2202           0 :     range.x = pt.x;
    2203           0 :     range.width = 0;
    2204             :   }
    2205           0 :   if (currentCSSPixels.y == aScrollPosition.y) {
    2206           0 :     pt.y = current.y;
    2207           0 :     range.y = pt.y;
    2208           0 :     range.height = 0;
    2209             :   }
    2210           0 :   ScrollTo(pt, aMode, &range);
    2211             :   // 'this' might be destroyed here
    2212           0 : }
    2213             : 
    2214             : void
    2215           0 : ScrollFrameHelper::ScrollToCSSPixelsApproximate(const CSSPoint& aScrollPosition,
    2216             :                                                 nsIAtom *aOrigin)
    2217             : {
    2218           0 :   nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
    2219           0 :   nscoord halfRange = nsPresContext::CSSPixelsToAppUnits(1000);
    2220           0 :   nsRect range(pt.x - halfRange, pt.y - halfRange, 2*halfRange - 1, 2*halfRange - 1);
    2221           0 :   ScrollToWithOrigin(pt, nsIScrollableFrame::INSTANT, aOrigin, &range);
    2222             :   // 'this' might be destroyed here
    2223           0 : }
    2224             : 
    2225             : CSSIntPoint
    2226           0 : ScrollFrameHelper::GetScrollPositionCSSPixels()
    2227             : {
    2228           0 :   return CSSIntPoint::FromAppUnitsRounded(GetScrollPosition());
    2229             : }
    2230             : 
    2231             : /*
    2232             :  * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
    2233             :  *  based on the setting of the smoothness scroll pref
    2234             :  */
    2235             : void
    2236           0 : ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
    2237             :                                           nsIScrollableFrame::ScrollMode aMode,
    2238             :                                           nsIAtom *aOrigin,
    2239             :                                           const nsRect* aRange,
    2240             :                                           nsIScrollbarMediator::ScrollSnapMode aSnap)
    2241             : {
    2242             : 
    2243           0 :   if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
    2244           0 :     GetSnapPointForDestination(nsIScrollableFrame::DEVICE_PIXELS,
    2245             :                                mDestination,
    2246           0 :                                aScrollPosition);
    2247             :   }
    2248             : 
    2249           0 :   nsRect scrollRange = GetScrollRangeForClamping();
    2250           0 :   mDestination = scrollRange.ClampPoint(aScrollPosition);
    2251           0 :   if (mDestination != aScrollPosition && aOrigin == nsGkAtoms::restore &&
    2252           0 :       GetPageLoadingState() != LoadingState::Loading) {
    2253             :     // If we're doing a restore but the scroll position is clamped, promote
    2254             :     // the origin from one that APZ can clobber to one that it can't clobber.
    2255           0 :     aOrigin = nsGkAtoms::other;
    2256             :   }
    2257             : 
    2258           0 :   nsRect range = aRange ? *aRange : nsRect(aScrollPosition, nsSize(0, 0));
    2259             : 
    2260           0 :   if (aMode != nsIScrollableFrame::SMOOTH_MSD) {
    2261             :     // If we get a non-smooth-scroll, reset the cached APZ scroll destination,
    2262             :     // so that we know to process the next smooth-scroll destined for APZ.
    2263           0 :     mApzSmoothScrollDestination = Nothing();
    2264             :   }
    2265             : 
    2266           0 :   if (aMode == nsIScrollableFrame::INSTANT) {
    2267             :     // Asynchronous scrolling is not allowed, so we'll kill any existing
    2268             :     // async-scrolling process and do an instant scroll.
    2269           0 :     CompleteAsyncScroll(range, aOrigin);
    2270           0 :     return;
    2271             :   }
    2272             : 
    2273           0 :   nsPresContext* presContext = mOuter->PresContext();
    2274           0 :   TimeStamp now = presContext->RefreshDriver()->IsTestControllingRefreshesEnabled()
    2275           0 :                 ? presContext->RefreshDriver()->MostRecentRefresh()
    2276           0 :                 : TimeStamp::Now();
    2277           0 :   bool isSmoothScroll = (aMode == nsIScrollableFrame::SMOOTH) &&
    2278           0 :                           IsSmoothScrollingEnabled();
    2279             : 
    2280           0 :   nsSize currentVelocity(0, 0);
    2281             : 
    2282           0 :   if (gfxPrefs::ScrollBehaviorEnabled()) {
    2283           0 :     if (aMode == nsIScrollableFrame::SMOOTH_MSD) {
    2284           0 :       mIgnoreMomentumScroll = true;
    2285           0 :       if (!mAsyncSmoothMSDScroll) {
    2286           0 :         nsPoint sv = mVelocityQueue.GetVelocity();
    2287           0 :         currentVelocity.width = sv.x;
    2288           0 :         currentVelocity.height = sv.y;
    2289           0 :         if (mAsyncScroll) {
    2290           0 :           if (mAsyncScroll->mIsSmoothScroll) {
    2291           0 :             currentVelocity = mAsyncScroll->VelocityAt(now);
    2292             :           }
    2293           0 :           mAsyncScroll = nullptr;
    2294             :         }
    2295             : 
    2296           0 :         if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && WantAsyncScroll()) {
    2297           0 :           if (mApzSmoothScrollDestination == Some(mDestination) &&
    2298           0 :               mScrollGeneration == sScrollGenerationCounter) {
    2299             :             // If we already sent APZ a smooth-scroll request to this
    2300             :             // destination with this generation (i.e. it was the last request
    2301             :             // we sent), then don't send another one because it is redundant.
    2302             :             // This is to avoid a scenario where pages do repeated scrollBy
    2303             :             // calls, incrementing the generation counter, and blocking APZ from
    2304             :             // syncing the scroll offset back to the main thread.
    2305             :             // Note that if we get two smooth-scroll requests to the same
    2306             :             // destination with some other scroll in between,
    2307             :             // mApzSmoothScrollDestination will get reset to Nothing() and so
    2308             :             // we shouldn't have the problem where this check discards a
    2309             :             // legitimate smooth-scroll.
    2310             :             // Note: if there are two separate scrollframes both getting smooth
    2311             :             // scrolled at the same time, sScrollGenerationCounter can get
    2312             :             // incremented and this early-exit won't get taken. Bug 1231177 is
    2313             :             // on file for this.
    2314           0 :             return;
    2315             :           }
    2316             : 
    2317             :           // The animation will be handled in the compositor, pass the
    2318             :           // information needed to start the animation and skip the main-thread
    2319             :           // animation for this scroll.
    2320           0 :           mLastSmoothScrollOrigin = aOrigin;
    2321           0 :           mApzSmoothScrollDestination = Some(mDestination);
    2322           0 :           mScrollGeneration = ++sScrollGenerationCounter;
    2323             : 
    2324           0 :           if (!nsLayoutUtils::HasDisplayPort(mOuter->GetContent())) {
    2325             :             // If this frame doesn't have a displayport then there won't be an
    2326             :             // APZC instance for it and so there won't be anything to process
    2327             :             // this smooth scroll request. We should set a displayport on this
    2328             :             // frame to force an APZC which can handle the request.
    2329           0 :             nsLayoutUtils::CalculateAndSetDisplayPortMargins(
    2330           0 :               mOuter->GetScrollTargetFrame(),
    2331           0 :               nsLayoutUtils::RepaintMode::DoNotRepaint);
    2332           0 :             nsIFrame* frame = do_QueryFrame(mOuter->GetScrollTargetFrame());
    2333             :             nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
    2334             :               frame,
    2335           0 :               nsLayoutUtils::RepaintMode::DoNotRepaint);
    2336             :           }
    2337             : 
    2338             :           // Schedule a paint to ensure that the frame metrics get updated on
    2339             :           // the compositor thread.
    2340           0 :           mOuter->SchedulePaint();
    2341           0 :           return;
    2342             :         }
    2343             : 
    2344             :         mAsyncSmoothMSDScroll =
    2345           0 :           new AsyncSmoothMSDScroll(GetScrollPosition(), mDestination,
    2346           0 :                                    currentVelocity, GetScrollRangeForClamping(),
    2347           0 :                                    now, presContext);
    2348             : 
    2349           0 :         if (!mAsyncSmoothMSDScroll->SetRefreshObserver(this)) {
    2350             :           // Observer setup failed. Scroll the normal way.
    2351           0 :           CompleteAsyncScroll(range, aOrigin);
    2352           0 :           return;
    2353             :         }
    2354             :       } else {
    2355             :         // A previous smooth MSD scroll is still in progress, so we just need to
    2356             :         // update its destination.
    2357           0 :         mAsyncSmoothMSDScroll->SetDestination(mDestination);
    2358             :       }
    2359             : 
    2360           0 :       return;
    2361             :     } else {
    2362           0 :       if (mAsyncSmoothMSDScroll) {
    2363           0 :         currentVelocity = mAsyncSmoothMSDScroll->GetVelocity();
    2364           0 :         mAsyncSmoothMSDScroll = nullptr;
    2365             :       }
    2366             :     }
    2367             :   }
    2368             : 
    2369           0 :   if (!mAsyncScroll) {
    2370           0 :     mAsyncScroll = new AsyncScroll(GetScrollPosition());
    2371           0 :     if (!mAsyncScroll->SetRefreshObserver(this)) {
    2372             :       // Observer setup failed. Scroll the normal way.
    2373           0 :       CompleteAsyncScroll(range, aOrigin);
    2374           0 :       return;
    2375             :     }
    2376             :   }
    2377             : 
    2378           0 :   mAsyncScroll->mIsSmoothScroll = isSmoothScroll;
    2379             : 
    2380           0 :   if (isSmoothScroll) {
    2381           0 :     mAsyncScroll->InitSmoothScroll(now, mDestination, aOrigin, range, currentVelocity);
    2382             :   } else {
    2383           0 :     mAsyncScroll->Init(range);
    2384             :   }
    2385             : }
    2386             : 
    2387             : // We can't use nsContainerFrame::PositionChildViews here because
    2388             : // we don't want to invalidate views that have moved.
    2389           0 : static void AdjustViews(nsIFrame* aFrame)
    2390             : {
    2391           0 :   nsView* view = aFrame->GetView();
    2392           0 :   if (view) {
    2393           0 :     nsPoint pt;
    2394           0 :     aFrame->GetParent()->GetClosestView(&pt);
    2395           0 :     pt += aFrame->GetPosition();
    2396           0 :     view->SetPosition(pt.x, pt.y);
    2397             : 
    2398           0 :     return;
    2399             :   }
    2400             : 
    2401           0 :   if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
    2402           0 :     return;
    2403             :   }
    2404             : 
    2405             :   // Call AdjustViews recursively for all child frames except the popup list as
    2406             :   // the views for popups are not scrolled.
    2407           0 :   nsIFrame::ChildListIterator lists(aFrame);
    2408           0 :   for (; !lists.IsDone(); lists.Next()) {
    2409           0 :     if (lists.CurrentID() == nsIFrame::kPopupList) {
    2410           0 :       continue;
    2411             :     }
    2412           0 :     nsFrameList::Enumerator childFrames(lists.CurrentList());
    2413           0 :     for (; !childFrames.AtEnd(); childFrames.Next()) {
    2414           0 :       AdjustViews(childFrames.get());
    2415             :     }
    2416             :   }
    2417             : }
    2418             : 
    2419             : static bool
    2420           2 : NeedToInvalidateOnScroll(nsIFrame* aFrame)
    2421             : {
    2422           2 :   return (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL) != 0;
    2423             : }
    2424             : 
    2425         356 : bool ScrollFrameHelper::IsIgnoringViewportClipping() const
    2426             : {
    2427         356 :   if (!mIsRoot)
    2428         311 :     return false;
    2429             :   nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
    2430          45 :     (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
    2431          45 :   return subdocFrame && !subdocFrame->ShouldClipSubdocument();
    2432             : }
    2433             : 
    2434           0 : void ScrollFrameHelper::MarkScrollbarsDirtyForReflow() const
    2435             : {
    2436           0 :   nsIPresShell* presShell = mOuter->PresContext()->PresShell();
    2437           0 :   if (mVScrollbarBox) {
    2438           0 :     presShell->FrameNeedsReflow(mVScrollbarBox, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
    2439             :   }
    2440           0 :   if (mHScrollbarBox) {
    2441           0 :     presShell->FrameNeedsReflow(mHScrollbarBox, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
    2442             :   }
    2443           0 : }
    2444             : 
    2445         155 : bool ScrollFrameHelper::ShouldClampScrollPosition() const
    2446             : {
    2447         155 :   if (!mIsRoot)
    2448         111 :     return true;
    2449             :   nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
    2450          44 :     (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
    2451          44 :   return !subdocFrame || subdocFrame->ShouldClampScrollPosition();
    2452             : }
    2453             : 
    2454         741 : bool ScrollFrameHelper::IsAlwaysActive() const
    2455             : {
    2456         741 :   if (nsDisplayItem::ForceActiveLayers()) {
    2457           0 :     return true;
    2458             :   }
    2459             : 
    2460             :   // Unless this is the root scrollframe for a non-chrome document
    2461             :   // which is the direct child of a chrome document, we default to not
    2462             :   // being "active".
    2463         741 :   if (!(mIsRoot && mOuter->PresContext()->IsRootContentDocument())) {
    2464         729 :      return false;
    2465             :   }
    2466             : 
    2467             :   // If we have scrolled before, then we should stay active.
    2468          12 :   if (mHasBeenScrolled) {
    2469           0 :     return true;
    2470             :   }
    2471             : 
    2472             :   // If we're overflow:hidden, then start as inactive until
    2473             :   // we get scrolled manually.
    2474          24 :   ScrollbarStyles styles = GetScrollbarStylesFromFrame();
    2475          24 :   return (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
    2476          24 :           styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
    2477             : }
    2478             : 
    2479             : /*static*/ void
    2480           0 : RemoveDisplayPortCallback(nsITimer* aTimer, void* aClosure)
    2481             : {
    2482           0 :   ScrollFrameHelper* helper = static_cast<ScrollFrameHelper*>(aClosure);
    2483             : 
    2484             :   // This function only ever gets called from the expiry timer, so it must
    2485             :   // be non-null here. Set it to null here so that we don't keep resetting
    2486             :   // it unnecessarily in MarkRecentlyScrolled().
    2487           0 :   MOZ_ASSERT(helper->mDisplayPortExpiryTimer);
    2488           0 :   helper->mDisplayPortExpiryTimer = nullptr;
    2489             : 
    2490           0 :   if (!helper->AllowDisplayPortExpiration() || helper->mIsScrollParent) {
    2491             :     // If this is a scroll parent for some other scrollable frame, don't
    2492             :     // expire the displayport because it would break scroll handoff. Once the
    2493             :     // descendant scrollframes have their displayports expired, they will
    2494             :     // trigger the displayport expiration on this scrollframe as well, and
    2495             :     // mIsScrollParent will presumably be false when that kicks in.
    2496           0 :     return;
    2497             :   }
    2498             : 
    2499             :   // Remove the displayport from this scrollframe if it's been a while
    2500             :   // since it's scrolled, except if it needs to be always active. Note that
    2501             :   // there is one scrollframe that doesn't fall under this general rule, and
    2502             :   // that is the one that nsLayoutUtils::MaybeCreateDisplayPort decides to put
    2503             :   // a displayport on (i.e. the first scrollframe that WantAsyncScroll()s).
    2504             :   // If that scrollframe is this one, we remove the displayport anyway, and
    2505             :   // as part of the next paint MaybeCreateDisplayPort will put another
    2506             :   // displayport back on it. Although the displayport will "flicker" off and
    2507             :   // back on, the layer itself should never disappear, because this all
    2508             :   // happens between actual painting. If the displayport is reset to a
    2509             :   // different position that's ok; this scrollframe hasn't been scrolled
    2510             :   // recently and so the reset should be correct.
    2511           0 :   nsLayoutUtils::RemoveDisplayPort(helper->mOuter->GetContent());
    2512           0 :   nsLayoutUtils::ExpireDisplayPortOnAsyncScrollableAncestor(helper->mOuter);
    2513           0 :   helper->mOuter->SchedulePaint();
    2514             :   // Be conservative and unflag this this scrollframe as being scrollable by
    2515             :   // APZ. If it is still scrollable this will get flipped back soon enough.
    2516           0 :   helper->mScrollableByAPZ = false;
    2517             : }
    2518             : 
    2519           0 : void ScrollFrameHelper::MarkNotRecentlyScrolled()
    2520             : {
    2521           0 :   if (!mHasBeenScrolledRecently)
    2522           0 :     return;
    2523             : 
    2524           0 :   mHasBeenScrolledRecently = false;
    2525           0 :   mOuter->SchedulePaint();
    2526             : }
    2527             : 
    2528           0 : void ScrollFrameHelper::MarkRecentlyScrolled()
    2529             : {
    2530           0 :   mHasBeenScrolledRecently = true;
    2531           0 :   if (IsAlwaysActive()) {
    2532           0 :     return;
    2533             :   }
    2534             : 
    2535           0 :   if (mActivityExpirationState.IsTracked()) {
    2536           0 :     gScrollFrameActivityTracker->MarkUsed(this);
    2537             :   } else {
    2538           0 :     if (!gScrollFrameActivityTracker) {
    2539           0 :       gScrollFrameActivityTracker = new ScrollFrameActivityTracker(
    2540           0 :         SystemGroup::EventTargetFor(TaskCategory::Other));
    2541             :     }
    2542           0 :     gScrollFrameActivityTracker->AddObject(this);
    2543             :   }
    2544             : 
    2545             :   // If we just scrolled and there's a displayport expiry timer in place,
    2546             :   // reset the timer.
    2547           0 :   ResetDisplayPortExpiryTimer();
    2548             : }
    2549             : 
    2550           0 : void ScrollFrameHelper::ResetDisplayPortExpiryTimer()
    2551             : {
    2552           0 :   if (mDisplayPortExpiryTimer) {
    2553           0 :     mDisplayPortExpiryTimer->InitWithNamedFuncCallback(
    2554             :       RemoveDisplayPortCallback,
    2555             :       this,
    2556             :       gfxPrefs::APZDisplayPortExpiryTime(),
    2557             :       nsITimer::TYPE_ONE_SHOT,
    2558           0 :       "ScrollFrameHelper::ResetDisplayPortExpiryTimer");
    2559             :   }
    2560           0 : }
    2561             : 
    2562           1 : bool ScrollFrameHelper::AllowDisplayPortExpiration()
    2563             : {
    2564           1 :   if (IsAlwaysActive()) {
    2565           1 :     return false;
    2566             :   }
    2567           0 :   if (mIsRoot && mOuter->PresContext()->IsRoot()) {
    2568           0 :     return false;
    2569             :   }
    2570           0 :   return true;
    2571             : }
    2572             : 
    2573           1 : void ScrollFrameHelper::TriggerDisplayPortExpiration()
    2574             : {
    2575           1 :   if (!AllowDisplayPortExpiration()) {
    2576           1 :     return;
    2577             :   }
    2578             : 
    2579           0 :   if (!gfxPrefs::APZDisplayPortExpiryTime()) {
    2580             :     // a zero time disables the expiry
    2581           0 :     return;
    2582             :   }
    2583             : 
    2584           0 :   if (!mDisplayPortExpiryTimer) {
    2585           0 :     mDisplayPortExpiryTimer = do_CreateInstance("@mozilla.org/timer;1");
    2586             :   }
    2587           0 :   ResetDisplayPortExpiryTimer();
    2588             : }
    2589             : 
    2590           0 : void ScrollFrameHelper::ScrollVisual()
    2591             : {
    2592             :   // Mark this frame as having been scrolled. If this is the root
    2593             :   // scroll frame of a content document, then IsAlwaysActive()
    2594             :   // will return true from now on and MarkNotRecentlyScrolled() won't
    2595             :   // have any effect.
    2596           0 :   mHasBeenScrolled = true;
    2597             : 
    2598           0 :   AdjustViews(mScrolledFrame);
    2599             :   // We need to call this after fixing up the view positions
    2600             :   // to be consistent with the frame hierarchy.
    2601           0 :   bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
    2602           0 :   mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
    2603           0 :   if (needToInvalidateOnScroll) {
    2604           0 :     MarkNotRecentlyScrolled();
    2605             :   } else {
    2606           0 :     MarkRecentlyScrolled();
    2607             :   }
    2608             : 
    2609           0 : }
    2610             : 
    2611             : /**
    2612             :  * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
    2613             :  * to [aBoundLower, aBoundUpper] and then select the appunit value from among
    2614             :  * aBoundLower, aBoundUpper and those such that (aDesired - aCurrent) *
    2615             :  * aRes/aAppUnitsPerPixel is an integer (or as close as we can get
    2616             :  * modulo rounding to appunits) that is in [aDestLower, aDestUpper] and
    2617             :  * closest to aDesired.  If no such value exists, return the nearest in
    2618             :  * [aDestLower, aDestUpper].
    2619             :  */
    2620             : static nscoord
    2621         308 : ClampAndAlignWithPixels(nscoord aDesired,
    2622             :                         nscoord aBoundLower, nscoord aBoundUpper,
    2623             :                         nscoord aDestLower, nscoord aDestUpper,
    2624             :                         nscoord aAppUnitsPerPixel, double aRes,
    2625             :                         nscoord aCurrent)
    2626             : {
    2627             :   // Intersect scroll range with allowed range, by clamping the ends
    2628             :   // of aRange to be within bounds
    2629         308 :   nscoord destLower = clamped(aDestLower, aBoundLower, aBoundUpper);
    2630         308 :   nscoord destUpper = clamped(aDestUpper, aBoundLower, aBoundUpper);
    2631             : 
    2632         308 :   nscoord desired = clamped(aDesired, destLower, destUpper);
    2633             : 
    2634         308 :   double currentLayerVal = (aRes*aCurrent)/aAppUnitsPerPixel;
    2635         308 :   double desiredLayerVal = (aRes*desired)/aAppUnitsPerPixel;
    2636         308 :   double delta = desiredLayerVal - currentLayerVal;
    2637         308 :   double nearestLayerVal = NS_round(delta) + currentLayerVal;
    2638             : 
    2639             :   // Convert back from PaintedLayer space to appunits relative to the top-left
    2640             :   // of the scrolled frame.
    2641             :   nscoord aligned =
    2642         308 :     NSToCoordRoundWithClamp(nearestLayerVal*aAppUnitsPerPixel/aRes);
    2643             : 
    2644             :   // Use a bound if it is within the allowed range and closer to desired than
    2645             :   // the nearest pixel-aligned value.
    2646         555 :   if (aBoundUpper == destUpper &&
    2647         247 :       static_cast<decltype(Abs(desired))>(aBoundUpper - desired) <
    2648         247 :       Abs(desired - aligned))
    2649           0 :     return aBoundUpper;
    2650             : 
    2651         616 :   if (aBoundLower == destLower &&
    2652         308 :       static_cast<decltype(Abs(desired))>(desired - aBoundLower) <
    2653         308 :       Abs(aligned - desired))
    2654           0 :     return aBoundLower;
    2655             : 
    2656             :   // Accept the nearest pixel-aligned value if it is within the allowed range.
    2657         308 :   if (aligned >= destLower && aligned <= destUpper)
    2658         308 :     return aligned;
    2659             : 
    2660             :   // Check if opposite pixel boundary fits into allowed range.
    2661             :   double oppositeLayerVal =
    2662           0 :     nearestLayerVal + ((nearestLayerVal < desiredLayerVal) ? 1.0 : -1.0);
    2663             :   nscoord opposite =
    2664           0 :     NSToCoordRoundWithClamp(oppositeLayerVal*aAppUnitsPerPixel/aRes);
    2665           0 :   if (opposite >= destLower && opposite <= destUpper) {
    2666           0 :     return opposite;
    2667             :   }
    2668             : 
    2669             :   // No alignment available.
    2670           0 :   return desired;
    2671             : }
    2672             : 
    2673             : /**
    2674             :  * Clamp desired scroll position aPt to aBounds and then snap
    2675             :  * it to the same layer pixel edges as aCurrent, keeping it within aRange
    2676             :  * during snapping. aCurrent is the current scroll position.
    2677             :  */
    2678             : static nsPoint
    2679         154 : ClampAndAlignWithLayerPixels(const nsPoint& aPt,
    2680             :                              const nsRect& aBounds,
    2681             :                              const nsRect& aRange,
    2682             :                              const nsPoint& aCurrent,
    2683             :                              nscoord aAppUnitsPerPixel,
    2684             :                              const gfxSize& aScale)
    2685             : {
    2686         154 :   return nsPoint(ClampAndAlignWithPixels(aPt.x, aBounds.x, aBounds.XMost(),
    2687         154 :                                          aRange.x, aRange.XMost(),
    2688         154 :                                          aAppUnitsPerPixel, aScale.width,
    2689         154 :                                          aCurrent.x),
    2690         154 :                  ClampAndAlignWithPixels(aPt.y, aBounds.y, aBounds.YMost(),
    2691         154 :                                          aRange.y, aRange.YMost(),
    2692         154 :                                          aAppUnitsPerPixel, aScale.height,
    2693        1078 :                                          aCurrent.y));
    2694             : }
    2695             : 
    2696             : /* static */ void
    2697           0 : ScrollFrameHelper::ScrollActivityCallback(nsITimer *aTimer, void* anInstance)
    2698             : {
    2699           0 :   ScrollFrameHelper* self = static_cast<ScrollFrameHelper*>(anInstance);
    2700             : 
    2701             :   // Fire the synth mouse move.
    2702           0 :   self->mScrollActivityTimer->Cancel();
    2703           0 :   self->mScrollActivityTimer = nullptr;
    2704           0 :   self->mOuter->PresContext()->PresShell()->SynthesizeMouseMove(true);
    2705           0 : }
    2706             : 
    2707             : 
    2708             : void
    2709           0 : ScrollFrameHelper::ScheduleSyntheticMouseMove()
    2710             : {
    2711           0 :   if (!mScrollActivityTimer) {
    2712           0 :     mScrollActivityTimer = do_CreateInstance("@mozilla.org/timer;1");
    2713           0 :     if (!mScrollActivityTimer) {
    2714           0 :       return;
    2715             :     }
    2716             :   }
    2717             : 
    2718           0 :   mScrollActivityTimer->InitWithNamedFuncCallback(
    2719             :     ScrollActivityCallback,
    2720             :     this,
    2721             :     100,
    2722             :     nsITimer::TYPE_ONE_SHOT,
    2723           0 :     "ScrollFrameHelper::ScheduleSyntheticMouseMove");
    2724             : }
    2725             : 
    2726             : void
    2727           1 : ScrollFrameHelper::NotifyApproximateFrameVisibilityUpdate(bool aIgnoreDisplayPort)
    2728             : {
    2729           1 :   mLastUpdateFramesPos = GetScrollPosition();
    2730           1 :   if (aIgnoreDisplayPort) {
    2731           0 :     mHadDisplayPortAtLastFrameUpdate = false;
    2732           0 :     mDisplayPortAtLastFrameUpdate = nsRect();
    2733             :   } else {
    2734           1 :     mHadDisplayPortAtLastFrameUpdate =
    2735           1 :       nsLayoutUtils::GetDisplayPort(mOuter->GetContent(),
    2736             :                                     &mDisplayPortAtLastFrameUpdate);
    2737             :   }
    2738           1 : }
    2739             : 
    2740             : bool
    2741           1 : ScrollFrameHelper::GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort)
    2742             : {
    2743           1 :   if (mHadDisplayPortAtLastFrameUpdate) {
    2744           0 :     *aDisplayPort = mDisplayPortAtLastFrameUpdate;
    2745             :   }
    2746           1 :   return mHadDisplayPortAtLastFrameUpdate;
    2747             : }
    2748             : 
    2749             : void
    2750         154 : ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOrigin)
    2751             : {
    2752         154 :   if (aOrigin == nullptr) {
    2753             :     // If no origin was specified, we still want to set it to something that's
    2754             :     // non-null, so that we can use nullness to distinguish if the frame was scrolled
    2755             :     // at all. Default it to some generic placeholder.
    2756         154 :     aOrigin = nsGkAtoms::other;
    2757             :   }
    2758             : 
    2759         154 :   nsPresContext* presContext = mOuter->PresContext();
    2760         154 :   nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
    2761             :   // 'scale' is our estimate of the scale factor that will be applied
    2762             :   // when rendering the scrolled content to its own PaintedLayer.
    2763         154 :   gfxSize scale = FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame);
    2764         154 :   nsPoint curPos = GetScrollPosition();
    2765         308 :   nsPoint alignWithPos = mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)
    2766         154 :                          ? curPos : mScrollPosForLayerPixelAlignment;
    2767             :   // Try to align aPt with curPos so they have an integer number of layer
    2768             :   // pixels between them. This gives us the best chance of scrolling without
    2769             :   // having to invalidate due to changes in subpixel rendering.
    2770             :   // Note that when we actually draw into a PaintedLayer, the coordinates
    2771             :   // that get mapped onto the layer buffer pixels are from the display list,
    2772             :   // which are relative to the display root frame's top-left increasing down,
    2773             :   // whereas here our coordinates are scroll positions which increase upward
    2774             :   // and are relative to the scrollport top-left. This difference doesn't actually
    2775             :   // matter since all we are about is that there be an integer number of
    2776             :   // layer pixels between pt and curPos.
    2777             :   nsPoint pt =
    2778             :     ClampAndAlignWithLayerPixels(aPt,
    2779         308 :                                  GetScrollRangeForClamping(),
    2780             :                                  aRange,
    2781             :                                  alignWithPos,
    2782             :                                  appUnitsPerDevPixel,
    2783         154 :                                  scale);
    2784         154 :   if (pt == curPos) {
    2785         308 :     return;
    2786             :   }
    2787             : 
    2788           0 :   bool needFrameVisibilityUpdate = mLastUpdateFramesPos == nsPoint(-1,-1);
    2789             : 
    2790           0 :   nsPoint dist(std::abs(pt.x - mLastUpdateFramesPos.x),
    2791           0 :                std::abs(pt.y - mLastUpdateFramesPos.y));
    2792           0 :   nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
    2793           0 :   nscoord horzAllowance = std::max(scrollPortSize.width / std::max(sHorzScrollFraction, 1),
    2794           0 :                                    nsPresContext::AppUnitsPerCSSPixel());
    2795           0 :   nscoord vertAllowance = std::max(scrollPortSize.height / std::max(sVertScrollFraction, 1),
    2796           0 :                                    nsPresContext::AppUnitsPerCSSPixel());
    2797           0 :   if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
    2798           0 :     needFrameVisibilityUpdate = true;
    2799             :   }
    2800             : 
    2801             :   // notify the listeners.
    2802           0 :   for (uint32_t i = 0; i < mListeners.Length(); i++) {
    2803           0 :     mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
    2804             :   }
    2805             : 
    2806           0 :   nsRect oldDisplayPort;
    2807           0 :   nsIContent* content = mOuter->GetContent();
    2808           0 :   nsLayoutUtils::GetHighResolutionDisplayPort(content, &oldDisplayPort);
    2809           0 :   oldDisplayPort.MoveBy(-mScrolledFrame->GetPosition());
    2810             : 
    2811             :   // Update frame position for scrolling
    2812           0 :   mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
    2813             : 
    2814             :   // If |mLastScrollOrigin| is already set to something that can clobber APZ's
    2815             :   // scroll offset, then we don't want to change it to something that can't.
    2816             :   // If we allowed this, then we could end up in a state where APZ ignores
    2817             :   // legitimate scroll offset updates because the origin has been masked by
    2818             :   // a later change within the same refresh driver tick.
    2819             :   bool isScrollOriginDowngrade =
    2820           0 :     nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin) &&
    2821           0 :     !nsLayoutUtils::CanScrollOriginClobberApz(aOrigin);
    2822           0 :   bool allowScrollOriginChange = mAllowScrollOriginDowngrade ||
    2823           0 :     !isScrollOriginDowngrade;
    2824           0 :   if (allowScrollOriginChange) {
    2825           0 :     mLastScrollOrigin = aOrigin;
    2826           0 :     mAllowScrollOriginDowngrade = false;
    2827             :   }
    2828           0 :   mLastSmoothScrollOrigin = nullptr;
    2829           0 :   mScrollGeneration = ++sScrollGenerationCounter;
    2830             : 
    2831           0 :   ScrollVisual();
    2832             : 
    2833           0 :   bool schedulePaint = true;
    2834           0 :   if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
    2835           0 :       !nsLayoutUtils::ShouldDisableApzForElement(content) &&
    2836           0 :       gfxPrefs::APZPaintSkipping()) {
    2837             :     // If APZ is enabled with paint-skipping, there are certain conditions in
    2838             :     // which we can skip paints:
    2839             :     // 1) If APZ triggered this scroll, and the tile-aligned displayport is
    2840             :     //    unchanged.
    2841             :     // 2) If non-APZ triggered this scroll, but we can handle it by just asking
    2842             :     //    APZ to update the scroll position. Again we make this conditional on
    2843             :     //    the tile-aligned displayport being unchanged.
    2844             :     // We do the displayport check first since it's common to all scenarios,
    2845             :     // and then if the displayport is unchanged, we check if APZ triggered,
    2846             :     // or can handle, this scroll. If so, we set schedulePaint to false and
    2847             :     // skip the paint.
    2848             :     // Because of bug 1264297, we also don't do paint-skipping for elements with
    2849             :     // perspective, because the displayport may not have captured everything
    2850             :     // that needs to be painted. So even if the final tile-aligned displayport
    2851             :     // is the same, we force a repaint for these elements. Bug 1254260 tracks
    2852             :     // fixing this properly.
    2853           0 :     nsRect displayPort;
    2854             :     bool usingDisplayPort =
    2855           0 :       nsLayoutUtils::GetHighResolutionDisplayPort(content, &displayPort);
    2856           0 :     displayPort.MoveBy(-mScrolledFrame->GetPosition());
    2857             : 
    2858             :     PAINT_SKIP_LOG("New scrollpos %s usingDP %d dpEqual %d scrollableByApz %d plugins %d perspective %d bglocal %d\n",
    2859             :         Stringify(CSSPoint::FromAppUnits(GetScrollPosition())).c_str(),
    2860             :         usingDisplayPort, displayPort.IsEqualEdges(oldDisplayPort),
    2861             :         mScrollableByAPZ, HasPluginFrames(), HasPerspective(),
    2862             :         HasBgAttachmentLocal());
    2863           0 :     if (usingDisplayPort && displayPort.IsEqualEdges(oldDisplayPort) &&
    2864           0 :         !HasPerspective() && !HasBgAttachmentLocal()) {
    2865           0 :       bool haveScrollLinkedEffects = content->GetComposedDoc()->HasScrollLinkedEffect();
    2866           0 :       bool apzDisabled = haveScrollLinkedEffects && gfxPrefs::APZDisableForScrollLinkedEffects();
    2867           0 :       if (!apzDisabled && !HasPluginFrames()) {
    2868           0 :         if (LastScrollOrigin() == nsGkAtoms::apz) {
    2869           0 :           schedulePaint = false;
    2870             :           PAINT_SKIP_LOG("Skipping due to APZ scroll\n");
    2871           0 :         } else if (mScrollableByAPZ) {
    2872           0 :           nsIWidget* widget = presContext->GetNearestWidget();
    2873           0 :           LayerManager* manager = widget ? widget->GetLayerManager() : nullptr;
    2874           0 :           if (manager) {
    2875             :             mozilla::layers::FrameMetrics::ViewID id;
    2876           0 :             DebugOnly<bool> success = nsLayoutUtils::FindIDFor(content, &id);
    2877           0 :             MOZ_ASSERT(success); // we have a displayport, we better have an ID
    2878             : 
    2879             :             // Schedule an empty transaction to carry over the scroll offset update,
    2880             :             // instead of a full transaction. This empty transaction might still get
    2881             :             // squashed into a full transaction if something happens to trigger one.
    2882           0 :             schedulePaint = false;
    2883           0 :             manager->SetPendingScrollUpdateForNextTransaction(id,
    2884           0 :                 { mScrollGeneration, CSSPoint::FromAppUnits(GetScrollPosition()) });
    2885           0 :             mOuter->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
    2886             :             PAINT_SKIP_LOG("Skipping due to APZ-forwarded main-thread scroll\n");
    2887             :           }
    2888             :         }
    2889             :       }
    2890             :     }
    2891             :   }
    2892             : 
    2893           0 :   if (schedulePaint) {
    2894           0 :     mOuter->SchedulePaint();
    2895             : 
    2896           0 :     if (needFrameVisibilityUpdate) {
    2897           0 :       presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
    2898             :     }
    2899             :   }
    2900             : 
    2901           0 :   if (mOuter->ChildrenHavePerspective()) {
    2902             :     // The overflow areas of descendants may depend on the scroll position,
    2903             :     // so ensure they get updated.
    2904             : 
    2905             :     // First we recompute the overflow areas of the transformed children
    2906             :     // that use the perspective. FinishAndStoreOverflow only calls this
    2907             :     // if the size changes, so we need to do it manually.
    2908           0 :     mOuter->RecomputePerspectiveChildrenOverflow(mOuter);
    2909             : 
    2910             :     // Update the overflow for the scrolled frame to take any changes from the
    2911             :     // children into account.
    2912           0 :     mScrolledFrame->UpdateOverflow();
    2913             : 
    2914             :     // Update the overflow for the outer so that we recompute scrollbars.
    2915           0 :     mOuter->UpdateOverflow();
    2916             :   }
    2917             : 
    2918           0 :   ScheduleSyntheticMouseMove();
    2919             : 
    2920             :   { // scope the AutoScrollbarRepaintSuppression
    2921           0 :     AutoScrollbarRepaintSuppression repaintSuppression(this, !schedulePaint);
    2922           0 :     AutoWeakFrame weakFrame(mOuter);
    2923           0 :     UpdateScrollbarPosition();
    2924           0 :     if (!weakFrame.IsAlive()) {
    2925           0 :       return;
    2926             :     }
    2927             :   }
    2928             : 
    2929             :   presContext->RecordInteractionTime(
    2930             :     nsPresContext::InteractionType::eScrollInteraction,
    2931           0 :     TimeStamp::Now());
    2932             : 
    2933           0 :   PostScrollEvent();
    2934             : 
    2935             :   // notify the listeners.
    2936           0 :   for (uint32_t i = 0; i < mListeners.Length(); i++) {
    2937           0 :     mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
    2938             :   }
    2939             : 
    2940           0 :   nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
    2941           0 :   if (docShell) {
    2942           0 :     docShell->NotifyScrollObservers();
    2943             :   }
    2944             : }
    2945             : 
    2946             : static int32_t
    2947           0 : MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
    2948             : {
    2949           0 :   int32_t maxZIndex = -1;
    2950           0 :   for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
    2951           0 :     maxZIndex = std::max(maxZIndex, item->ZIndex());
    2952             :   }
    2953           0 :   return maxZIndex;
    2954             : }
    2955             : 
    2956             : // Finds the max z-index of the items in aList that meet the following conditions
    2957             : //   1) have z-index auto or z-index >= 0.
    2958             : //   2) aFrame is a proper ancestor of the item's frame.
    2959             : // Returns -1 if there is no such item.
    2960             : static int32_t
    2961           0 : MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
    2962             : {
    2963           0 :   int32_t maxZIndex = -1;
    2964           0 :   for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
    2965           0 :     nsIFrame* itemFrame = item->Frame();
    2966             :     // Perspective items return the scroll frame as their Frame(), so consider
    2967             :     // their TransformFrame() instead.
    2968           0 :     if (item->GetType() == nsDisplayItem::TYPE_PERSPECTIVE) {
    2969           0 :       itemFrame = static_cast<nsDisplayPerspective*>(item)->TransformFrame();
    2970             :     }
    2971           0 :     if (nsLayoutUtils::IsProperAncestorFrame(aFrame, itemFrame)) {
    2972           0 :       maxZIndex = std::max(maxZIndex, item->ZIndex());
    2973             :     }
    2974             :   }
    2975           0 :   return maxZIndex;
    2976             : }
    2977             : 
    2978             : template<class T>
    2979             : static void
    2980           0 : AppendInternalItemToTop(const nsDisplayListSet& aLists,
    2981             :                         T* aItem,
    2982             :                         int32_t aZIndex)
    2983             : {
    2984           0 :   if (aZIndex >= 0) {
    2985           0 :     aItem->SetOverrideZIndex(aZIndex);
    2986           0 :     aLists.PositionedDescendants()->AppendNewToTop(aItem);
    2987             :   } else {
    2988           0 :     aLists.Content()->AppendNewToTop(aItem);
    2989             :   }
    2990           0 : }
    2991             : 
    2992             : static const uint32_t APPEND_OWN_LAYER = 0x1;
    2993             : static const uint32_t APPEND_POSITIONED = 0x2;
    2994             : static const uint32_t APPEND_SCROLLBAR_CONTAINER = 0x4;
    2995             : 
    2996             : static void
    2997           6 : AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
    2998             :             nsDisplayList* aSource, nsIFrame* aSourceFrame, uint32_t aFlags)
    2999             : {
    3000           6 :   if (aSource->IsEmpty())
    3001           6 :     return;
    3002             : 
    3003             :   nsDisplayWrapList* newItem;
    3004           0 :   const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
    3005           0 :   if (aFlags & APPEND_OWN_LAYER) {
    3006           0 :     uint32_t flags = (aFlags & APPEND_SCROLLBAR_CONTAINER)
    3007           0 :                      ? nsDisplayOwnLayer::SCROLLBAR_CONTAINER
    3008           0 :                      : 0;
    3009           0 :     FrameMetrics::ViewID scrollTarget = (aFlags & APPEND_SCROLLBAR_CONTAINER)
    3010           0 :                                         ? aBuilder->GetCurrentScrollbarTarget()
    3011           0 :                                         : FrameMetrics::NULL_SCROLL_ID;
    3012           0 :     newItem = new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource, asr, flags, scrollTarget);
    3013             :   } else {
    3014           0 :     newItem = new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource, asr);
    3015             :   }
    3016             : 
    3017           0 :   if (aFlags & APPEND_POSITIONED) {
    3018             :     // We want overlay scrollbars to always be on top of the scrolled content,
    3019             :     // but we don't want them to unnecessarily cover overlapping elements from
    3020             :     // outside our scroll frame.
    3021           0 :     int32_t zIndex = MaxZIndexInList(aLists.PositionedDescendants(), aBuilder);
    3022           0 :     AppendInternalItemToTop(aLists, newItem, zIndex);
    3023             :   } else {
    3024           0 :     aLists.BorderBackground()->AppendNewToTop(newItem);
    3025             :   }
    3026             : }
    3027             : 
    3028             : struct HoveredStateComparator
    3029             : {
    3030           4 :   bool Equals(nsIFrame* A, nsIFrame* B) const {
    3031           4 :     bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
    3032           4 :                                              nsGkAtoms::hover);
    3033           4 :     bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
    3034           4 :                                              nsGkAtoms::hover);
    3035           4 :     return aHovered == bHovered;
    3036             :   }
    3037           4 :   bool LessThan(nsIFrame* A, nsIFrame* B) const {
    3038           4 :     bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
    3039           4 :                                              nsGkAtoms::hover);
    3040           4 :     bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
    3041           4 :                                              nsGkAtoms::hover);
    3042           4 :     return !aHovered && bHovered;
    3043             :   }
    3044             : };
    3045             : 
    3046             : void
    3047         462 : ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder*   aBuilder,
    3048             :                                        const nsRect&           aDirtyRect,
    3049             :                                        const nsDisplayListSet& aLists,
    3050             :                                        bool                    aCreateLayer,
    3051             :                                        bool                    aPositioned)
    3052             : {
    3053         462 :   nsITheme* theme = mOuter->PresContext()->GetTheme();
    3054         924 :   if (theme &&
    3055         462 :       theme->ShouldHideScrollbars()) {
    3056         460 :     return;
    3057             :   }
    3058             : 
    3059             :   bool overlayScrollbars =
    3060         462 :     LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
    3061             : 
    3062         464 :   AutoTArray<nsIFrame*, 3> scrollParts;
    3063         936 :   for (nsIFrame* kid : mOuter->PrincipalChildList()) {
    3064         486 :     if (kid == mScrolledFrame ||
    3065          12 :         (kid->IsAbsPosContainingBlock() || overlayScrollbars) != aPositioned) {
    3066         468 :       continue;
    3067             :     }
    3068             : 
    3069           6 :     scrollParts.AppendElement(kid);
    3070             :   }
    3071         462 :   if (scrollParts.IsEmpty()) {
    3072         460 :     return;
    3073             :   }
    3074             : 
    3075             :   // We can't check will-change budget during display list building phase.
    3076             :   // This means that we will build scroll bar layers for out of budget
    3077             :   // will-change: scroll position.
    3078           2 :   mozilla::layers::FrameMetrics::ViewID scrollTargetId = IsMaybeScrollingActive()
    3079           2 :     ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
    3080           2 :     : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
    3081             : 
    3082           2 :   scrollParts.Sort(HoveredStateComparator());
    3083             : 
    3084           4 :   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
    3085             :   // Don't let scrollparts extent outside our frame's border-box, if these are
    3086             :   // viewport scrollbars. They would create layerization problems. This wouldn't
    3087             :   // normally be an issue but themes can add overflow areas to scrollbar parts.
    3088           2 :   if (mIsRoot) {
    3089             :     clipState.ClipContentDescendants(
    3090           2 :         mOuter->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mOuter));
    3091             :   }
    3092             : 
    3093           8 :   for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
    3094           6 :     uint32_t flags = 0;
    3095           6 :     uint32_t appendToTopFlags = 0;
    3096           6 :     if (scrollParts[i] == mVScrollbarBox) {
    3097           2 :       flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
    3098           2 :       appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER;
    3099             :     }
    3100           6 :     if (scrollParts[i] == mHScrollbarBox) {
    3101           2 :       flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
    3102           2 :       appendToTopFlags |= APPEND_SCROLLBAR_CONTAINER;
    3103             :     }
    3104             : 
    3105             :     // The display port doesn't necessarily include the scrollbars, so just
    3106             :     // include all of the scrollbars if we are in a RCD-RSF. We only do
    3107             :     // this for the root scrollframe of the root content document, which is
    3108             :     // zoomable, and where the scrollbar sizes are bounded by the widget.
    3109          12 :     nsRect dirty = mIsRoot && mOuter->PresContext()->IsRootContentDocument()
    3110          12 :                    ? scrollParts[i]->GetVisualOverflowRectRelativeToParent()
    3111          12 :                    : aDirtyRect;
    3112             :     nsDisplayListBuilder::AutoBuildingDisplayList
    3113           6 :       buildingForChild(aBuilder, scrollParts[i],
    3114          18 :                        dirty + mOuter->GetOffsetTo(scrollParts[i]), true);
    3115             : 
    3116             :     // Always create layers for overlay scrollbars so that we don't create a
    3117             :     // giant layer covering the whole scrollport if both scrollbars are visible.
    3118           6 :     bool isOverlayScrollbar = (flags != 0) && overlayScrollbars;
    3119           6 :     bool createLayer = aCreateLayer || isOverlayScrollbar ||
    3120           6 :                        gfxPrefs::AlwaysLayerizeScrollbarTrackTestOnly();
    3121             : 
    3122             :     nsDisplayListBuilder::AutoCurrentScrollbarInfoSetter
    3123          12 :       infoSetter(aBuilder, scrollTargetId, flags, createLayer);
    3124          12 :     nsDisplayListCollection partList;
    3125           6 :     mOuter->BuildDisplayListForChild(
    3126           6 :       aBuilder, scrollParts[i], dirty, partList,
    3127           6 :       nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
    3128             : 
    3129           6 :     if (createLayer) {
    3130           6 :       appendToTopFlags |= APPEND_OWN_LAYER;
    3131             :     }
    3132           6 :     if (aPositioned) {
    3133           0 :       appendToTopFlags |= APPEND_POSITIONED;
    3134             :     }
    3135             : 
    3136             :     // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
    3137             :     // partList.PositionedDescendants().
    3138           6 :     ::AppendToTop(aBuilder, aLists,
    3139           6 :                   partList.PositionedDescendants(), scrollParts[i],
    3140           6 :                   appendToTopFlags);
    3141             :   }
    3142             : }
    3143             : 
    3144             : /* static */ bool ScrollFrameHelper::sFrameVisPrefsCached = false;
    3145             : /* static */ uint32_t ScrollFrameHelper::sHorzExpandScrollPort = 0;
    3146             : /* static */ uint32_t ScrollFrameHelper::sVertExpandScrollPort = 1;
    3147             : /* static */ int32_t ScrollFrameHelper::sHorzScrollFraction = 2;
    3148             : /* static */ int32_t ScrollFrameHelper::sVertScrollFraction = 2;
    3149             : 
    3150             : /* static */ void
    3151          43 : ScrollFrameHelper::EnsureFrameVisPrefsCached()
    3152             : {
    3153          43 :   if (!sFrameVisPrefsCached) {
    3154             :     Preferences::AddUintVarCache(&sHorzExpandScrollPort,
    3155           2 :       "layout.framevisibility.numscrollportwidths", (uint32_t)0);
    3156             :     Preferences::AddUintVarCache(&sVertExpandScrollPort,
    3157           2 :       "layout.framevisibility.numscrollportheights", 1);
    3158             : 
    3159             :     Preferences::AddIntVarCache(&sHorzScrollFraction,
    3160           2 :       "layout.framevisibility.amountscrollbeforeupdatehorizontal", 2);
    3161             :     Preferences::AddIntVarCache(&sVertScrollFraction,
    3162           2 :       "layout.framevisibility.amountscrollbeforeupdatevertical", 2);
    3163             : 
    3164           2 :     sFrameVisPrefsCached = true;
    3165             :   }
    3166          43 : }
    3167             : 
    3168             : nsRect
    3169           1 : ScrollFrameHelper::ExpandRectToNearlyVisible(const nsRect& aRect) const
    3170             : {
    3171             :   // We don't want to expand a rect in a direction that we can't scroll, so we
    3172             :   // check the scroll range.
    3173           2 :   nsRect scrollRange = GetScrollRangeForClamping();
    3174           1 :   nsPoint scrollPos = GetScrollPosition();
    3175           1 :   nsMargin expand(0, 0, 0, 0);
    3176             : 
    3177           1 :   nscoord vertShift = sVertExpandScrollPort * aRect.height;
    3178           1 :   if (scrollRange.y < scrollPos.y) {
    3179           0 :     expand.top = vertShift;
    3180             :   }
    3181           1 :   if (scrollPos.y < scrollRange.YMost()) {
    3182           0 :     expand.bottom = vertShift;
    3183             :   }
    3184             : 
    3185           1 :   nscoord horzShift = sHorzExpandScrollPort * aRect.width;
    3186           1 :   if (scrollRange.x < scrollPos.x) {
    3187           0 :     expand.left = horzShift;
    3188             :   }
    3189           1 :   if (scrollPos.x < scrollRange.XMost()) {
    3190           0 :     expand.right = horzShift;
    3191             :   }
    3192             : 
    3193           1 :   nsRect rect = aRect;
    3194           1 :   rect.Inflate(expand);
    3195           2 :   return rect;
    3196             : }
    3197             : 
    3198             : static bool
    3199           0 : ShouldBeClippedByFrame(nsIFrame* aClipFrame, nsIFrame* aClippedFrame)
    3200             : {
    3201           0 :   return nsLayoutUtils::IsProperAncestorFrame(aClipFrame, aClippedFrame);
    3202             : }
    3203             : 
    3204             : static void
    3205           0 : ClipItemsExceptCaret(nsDisplayList* aList,
    3206             :                      nsDisplayListBuilder* aBuilder,
    3207             :                      nsIFrame* aClipFrame,
    3208             :                      const DisplayItemClipChain* aExtraClip,
    3209             :                      nsDataHashtable<nsPtrHashKey<const DisplayItemClipChain>, const DisplayItemClipChain*>& aCache)
    3210             : {
    3211           0 :   for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
    3212           0 :     if (!ShouldBeClippedByFrame(aClipFrame, i->Frame())) {
    3213           0 :       continue;
    3214             :     }
    3215             : 
    3216           0 :     if (i->GetType() != nsDisplayItem::TYPE_CARET) {
    3217           0 :       const DisplayItemClipChain* clip = i->GetClipChain();
    3218           0 :       const DisplayItemClipChain* intersection = nullptr;
    3219           0 :       if (aCache.Get(clip, &intersection)) {
    3220           0 :         i->SetClipChain(intersection);
    3221             :       } else {
    3222           0 :         i->IntersectClip(aBuilder, aExtraClip);
    3223           0 :         aCache.Put(clip, i->GetClipChain());
    3224             :       }
    3225             :     }
    3226           0 :     nsDisplayList* children = i->GetSameCoordinateSystemChildren();
    3227           0 :     if (children) {
    3228           0 :       ClipItemsExceptCaret(children, aBuilder, aClipFrame, aExtraClip, aCache);
    3229             :     }
    3230             :   }
    3231           0 : }
    3232             : 
    3233             : static void
    3234           0 : ClipListsExceptCaret(nsDisplayListCollection* aLists,
    3235             :                      nsDisplayListBuilder* aBuilder,
    3236             :                      nsIFrame* aClipFrame,
    3237             :                      const DisplayItemClipChain* aExtraClip)
    3238             : {
    3239           0 :   nsDataHashtable<nsPtrHashKey<const DisplayItemClipChain>, const DisplayItemClipChain*> cache;
    3240           0 :   ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aExtraClip, cache);
    3241           0 :   ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aExtraClip, cache);
    3242           0 :   ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aExtraClip, cache);
    3243           0 :   ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aExtraClip, cache);
    3244           0 :   ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aExtraClip, cache);
    3245           0 :   ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aExtraClip, cache);
    3246           0 : }
    3247             : 
    3248             : void
    3249         249 : ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    3250             :                                     const nsRect&           aDirtyRect,
    3251             :                                     const nsDisplayListSet& aLists)
    3252             : {
    3253         249 :   if (aBuilder->IsForFrameVisibility()) {
    3254           0 :     NotifyApproximateFrameVisibilityUpdate(false);
    3255             :   }
    3256             : 
    3257         249 :   mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
    3258             : 
    3259         249 :   if (aBuilder->IsPaintingToWindow()) {
    3260         231 :     mScrollPosAtLastPaint = GetScrollPosition();
    3261         231 :     if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
    3262           0 :       MarkNotRecentlyScrolled();
    3263             :     }
    3264         231 :     if (IsMaybeScrollingActive()) {
    3265           2 :       if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
    3266           1 :         mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
    3267             :       }
    3268             :     } else {
    3269         229 :       mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
    3270             :     }
    3271             :   }
    3272             : 
    3273             :   // It's safe to get this value before the DecideScrollableLayer call below
    3274             :   // because that call cannot create a displayport for root scroll frames,
    3275             :   // and hence it cannot create an ignore scroll frame.
    3276             :   bool ignoringThisScrollFrame =
    3277         249 :     aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping();
    3278             : 
    3279             :   // Overflow clipping can never clip frames outside our subtree, so there
    3280             :   // is no need to worry about whether we are a moving frame that might clip
    3281             :   // non-moving frames.
    3282             :   // Not all our descendants will be clipped by overflow clipping, but all
    3283             :   // the ones that aren't clipped will be out of flow frames that have already
    3284             :   // had dirty rects saved for them by their parent frames calling
    3285             :   // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
    3286             :   // dirty rect here.
    3287         480 :   nsRect dirtyRect = aDirtyRect;
    3288         249 :   if (!ignoringThisScrollFrame) {
    3289         231 :     dirtyRect = dirtyRect.Intersect(mScrollPort);
    3290             :   }
    3291             : 
    3292         249 :   Unused << DecideScrollableLayer(aBuilder, &dirtyRect,
    3293         249 :               /* aAllowCreateDisplayPort = */ !mIsRoot);
    3294             : 
    3295         480 :   bool usingDisplayPort = aBuilder->IsPaintingToWindow() &&
    3296         480 :     nsLayoutUtils::HasDisplayPort(mOuter->GetContent());
    3297             : 
    3298         249 :   if (aBuilder->IsForFrameVisibility()) {
    3299             :     // We expand the dirty rect to catch frames just outside of the scroll port.
    3300             :     // We use the dirty rect instead of the whole scroll port to prevent
    3301             :     // too much expansion in the presence of very large (bigger than the
    3302             :     // viewport) scroll ports.
    3303           0 :     dirtyRect = ExpandRectToNearlyVisible(dirtyRect);
    3304             :   }
    3305             : 
    3306             :   // We put non-overlay scrollbars in their own layers when this is the root
    3307             :   // scroll frame and we are a toplevel content document. In this situation,
    3308             :   // the scrollbar(s) would normally be assigned their own layer anyway, since
    3309             :   // they're not scrolled with the rest of the document. But when both
    3310             :   // scrollbars are visible, the layer's visible rectangle would be the size
    3311             :   // of the viewport, so most layer implementations would create a layer buffer
    3312             :   // that's much larger than necessary. Creating independent layers for each
    3313             :   // scrollbar works around the problem.
    3314         269 :   bool createLayersForScrollbars = mIsRoot &&
    3315         269 :     mOuter->PresContext()->IsRootContentDocument();
    3316             : 
    3317         249 :   nsIScrollableFrame* sf = do_QueryFrame(mOuter);
    3318         249 :   MOZ_ASSERT(sf);
    3319             : 
    3320         249 :   if (ignoringThisScrollFrame) {
    3321             :     // Root scrollframes have FrameMetrics and clipping on their container
    3322             :     // layers, so don't apply clipping again.
    3323          18 :     mAddClipRectToLayer = false;
    3324             : 
    3325             :     // If we are a root scroll frame that has a display port we want to add
    3326             :     // scrollbars, they will be children of the scrollable layer, but they get
    3327             :     // adjusted by the APZC automatically.
    3328          18 :     bool addScrollBars = mIsRoot && usingDisplayPort && !aBuilder->IsForEventDelivery();
    3329             : 
    3330          18 :     if (addScrollBars) {
    3331             :       // Add classic scrollbars.
    3332           0 :       AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
    3333           0 :                           createLayersForScrollbars, false);
    3334             :     }
    3335             : 
    3336             :     {
    3337          36 :       nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
    3338          36 :       if (aBuilder->IsPaintingToWindow() &&
    3339          18 :           gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) {
    3340           0 :         asrSetter.EnterScrollFrame(sf);
    3341           0 :         aBuilder->SetActiveScrolledRootForRootScrollframe(aBuilder->CurrentActiveScrolledRoot());
    3342             :       }
    3343             : 
    3344             :       // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
    3345             :       // The scrolled frame shouldn't have its own background/border, so we
    3346             :       // can just pass aLists directly.
    3347          36 :       mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame,
    3348          36 :                                        dirtyRect, aLists);
    3349             :     }
    3350             : 
    3351          18 :     if (addScrollBars) {
    3352             :       // Add overlay scrollbars.
    3353           0 :       AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
    3354           0 :                           createLayersForScrollbars, true);
    3355             :     }
    3356             : 
    3357          18 :     return;
    3358             :   }
    3359             : 
    3360             :   // Root scrollframes have FrameMetrics and clipping on their container
    3361             :   // layers, so don't apply clipping again.
    3362         231 :   mAddClipRectToLayer =
    3363         231 :     !(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden());
    3364             : 
    3365             :   // Whether we might want to build a scrollable layer for this scroll frame
    3366             :   // at some point in the future. This controls whether we add the information
    3367             :   // to the layer tree (a scroll info layer if necessary, and add the right
    3368             :   // area to the dispatch to content layer event regions) necessary to activate
    3369             :   // a scroll frame so it creates a scrollable layer.
    3370         231 :   bool couldBuildLayer = false;
    3371         231 :   if (aBuilder->IsPaintingToWindow()) {
    3372         231 :     if (mWillBuildScrollableLayer) {
    3373           2 :       couldBuildLayer = true;
    3374             :     } else {
    3375         229 :       couldBuildLayer =
    3376         458 :         nsLayoutUtils::AsyncPanZoomEnabled(mOuter) &&
    3377         229 :         WantAsyncScroll() &&
    3378             :         // If we are using containers for root frames, and we are the root
    3379             :         // scroll frame for the display root, then we don't need a scroll
    3380             :         // info layer. nsDisplayList::PaintForFrame already calls
    3381             :         // ComputeFrameMetrics for us.
    3382           0 :         (!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
    3383           0 :          (aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
    3384             :     }
    3385             :   }
    3386             : 
    3387             :   // Now display the scrollbars and scrollcorner. These parts are drawn
    3388             :   // in the border-background layer, on top of our own background and
    3389             :   // borders and underneath borders and backgrounds of later elements
    3390             :   // in the tree.
    3391             :   // Note that this does not apply for overlay scrollbars; those are drawn
    3392             :   // in the positioned-elements layer on top of everything else by the call
    3393             :   // to AppendScrollPartsTo(..., true) further down.
    3394         231 :   AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
    3395         231 :                       createLayersForScrollbars, false);
    3396             : 
    3397         231 :   const nsStyleDisplay* disp = mOuter->StyleDisplay();
    3398         231 :   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
    3399           0 :     aBuilder->AddToWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize());
    3400             :   }
    3401             : 
    3402         231 :   mScrollParentID = aBuilder->GetCurrentScrollParentId();
    3403             : 
    3404         462 :   Maybe<nsRect> contentBoxClip;
    3405         462 :   Maybe<DisplayItemClipChain> extraContentBoxClipForNonCaretContent;
    3406         231 :   if (MOZ_UNLIKELY(mOuter->StyleDisplay()->mOverflowClipBox ==
    3407             :                      NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
    3408             :     // We only clip if there is *scrollable* overflow, to avoid clipping
    3409             :     // *visual* overflow unnecessarily.
    3410         170 :     nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
    3411         170 :     nsRect so = mScrolledFrame->GetScrollableOverflowRect();
    3412         170 :     if (clipRect.width != so.width || clipRect.height != so.height ||
    3413         170 :         so.x < 0 || so.y < 0) {
    3414           0 :       clipRect.Deflate(mOuter->GetUsedPadding());
    3415             : 
    3416             :       // The non-inflated clip needs to be set on all non-caret items.
    3417             :       // We prepare an extra DisplayItemClipChain here that will be intersected
    3418             :       // with those items after they've been created.
    3419           0 :       const ActiveScrolledRoot* asr = aBuilder->CurrentActiveScrolledRoot();
    3420           0 :       extraContentBoxClipForNonCaretContent = Some(DisplayItemClipChain{ DisplayItemClip(), asr, nullptr });
    3421           0 :       extraContentBoxClipForNonCaretContent->mClip.SetTo(clipRect);
    3422             : 
    3423           0 :       nsIFrame* caretFrame = aBuilder->GetCaretFrame();
    3424             :       // Avoid clipping it in a zero-height line box (heuristic only).
    3425           0 :       if (caretFrame && caretFrame->GetRect().height != 0) {
    3426           0 :         nsRect caretRect = aBuilder->GetCaretRect();
    3427             :         // Allow the caret to stick out of the content box clip by half the
    3428             :         // caret height on the top, and its full width on the right.
    3429           0 :         nsRect inflatedClip = clipRect;
    3430           0 :         inflatedClip.Inflate(nsMargin(caretRect.height / 2, caretRect.width, 0, 0));
    3431           0 :         contentBoxClip = Some(inflatedClip);
    3432             :       }
    3433             :     }
    3434             :   }
    3435             : 
    3436         462 :   nsDisplayListCollection scrolledContent;
    3437             :   {
    3438             :     // Note that setting the current scroll parent id here means that positioned children
    3439             :     // of this scroll info layer will pick up the scroll info layer as their scroll handoff
    3440             :     // parent. This is intentional because that is what happens for positioned children
    3441             :     // of scroll layers, and we want to maintain consistent behaviour between scroll layers
    3442             :     // and scroll info layers.
    3443             :     nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
    3444             :         aBuilder,
    3445           2 :         couldBuildLayer && mScrolledFrame->GetContent()
    3446           2 :             ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
    3447         464 :             : aBuilder->GetCurrentScrollParentId());
    3448             : 
    3449         462 :     nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
    3450             :     // Our override of GetBorderRadii ensures we never have a radius at
    3451             :     // the corners where we have a scrollbar.
    3452             :     nscoord radii[8];
    3453         231 :     bool haveRadii = mOuter->GetPaddingBoxBorderRadii(radii);
    3454         231 :     if (mIsRoot) {
    3455           2 :       clipRect.SizeTo(nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
    3456           2 :       if (mOuter->PresContext()->IsRootContentDocument()) {
    3457           2 :         double res = mOuter->PresContext()->PresShell()->GetResolution();
    3458           2 :         clipRect.width = NSToCoordRound(clipRect.width / res);
    3459           2 :         clipRect.height = NSToCoordRound(clipRect.height / res);
    3460             :       }
    3461             :     }
    3462             : 
    3463         462 :     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
    3464         231 :     if (mClipAllDescendants) {
    3465           2 :       clipState.ClipContentDescendants(clipRect, haveRadii ? radii : nullptr);
    3466             :     } else {
    3467         229 :       clipState.ClipContainingBlockDescendants(clipRect, haveRadii ? radii : nullptr);
    3468             :     }
    3469             : 
    3470         462 :     Maybe<DisplayListClipState::AutoSaveRestore> contentBoxClipState;;
    3471         231 :     if (contentBoxClip) {
    3472           0 :       contentBoxClipState.emplace(aBuilder);
    3473           0 :       if (mClipAllDescendants) {
    3474           0 :         contentBoxClipState->ClipContentDescendants(*contentBoxClip);
    3475             :       } else {
    3476           0 :         contentBoxClipState->ClipContainingBlockDescendants(*contentBoxClip);
    3477             :       }
    3478             :     }
    3479             : 
    3480         462 :     nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
    3481         231 :     if (mWillBuildScrollableLayer) {
    3482           2 :       asrSetter.EnterScrollFrame(sf);
    3483             :     }
    3484             : 
    3485         231 :     if (mIsScrollableLayerInRootContainer) {
    3486           0 :       aBuilder->SetActiveScrolledRootForRootScrollframe(aBuilder->CurrentActiveScrolledRoot());
    3487             :     }
    3488             : 
    3489             :     {
    3490             :       // Clip our contents to the unsnapped scrolled rect. This makes sure that
    3491             :       // we don't have display items over the subpixel seam at the edge of the
    3492             :       // scrolled area.
    3493         462 :       DisplayListClipState::AutoSaveRestore scrolledRectClipState(aBuilder);
    3494             :       nsRect scrolledRectClip =
    3495         462 :         GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
    3496         924 :                                          mScrollPort.Size()) + mScrolledFrame->GetPosition();
    3497         231 :       if (usingDisplayPort) {
    3498             :         // Clip the contents to the display port.
    3499             :         // The dirty rect already acts kind of like a clip, in that
    3500             :         // FrameLayerBuilder intersects item bounds and opaque regions with
    3501             :         // it, but it doesn't have the consistent snapping behavior of a
    3502             :         // true clip.
    3503             :         // For a case where this makes a difference, imagine the following
    3504             :         // scenario: The display port has an edge that falls on a fractional
    3505             :         // layer pixel, and there's an opaque display item that covers the
    3506             :         // whole display port up until that fractional edge, and there is a
    3507             :         // transparent display item that overlaps the edge. We want to prevent
    3508             :         // this transparent item from enlarging the scrolled layer's visible
    3509             :         // region beyond its opaque region. The dirty rect doesn't do that -
    3510             :         // it gets rounded out, whereas a true clip gets rounded to nearest
    3511             :         // pixels.
    3512             :         // If there is no display port, we don't need this because the clip
    3513             :         // from the scroll port is still applied.
    3514           2 :         scrolledRectClip = scrolledRectClip.Intersect(dirtyRect);
    3515             :       }
    3516             :       scrolledRectClipState.ClipContainingBlockDescendants(
    3517         231 :         scrolledRectClip + aBuilder->ToReferenceFrame(mOuter));
    3518             : 
    3519         231 :       mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
    3520             :     }
    3521             : 
    3522         231 :     if (extraContentBoxClipForNonCaretContent) {
    3523             :       // The items were built while the inflated content box clip was in
    3524             :       // effect, so that the caret wasn't clipped unnecessarily. We apply
    3525             :       // the non-inflated clip to the non-caret items now, by intersecting
    3526             :       // it with their existing clip.
    3527           0 :       ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame,
    3528           0 :                            extraContentBoxClipForNonCaretContent.ptr());
    3529             :     }
    3530             : 
    3531         231 :     if (aBuilder->IsPaintingToWindow()) {
    3532         231 :       mIsScrollParent = idSetter.ShouldForceLayerForScrollParent();
    3533             :     }
    3534         231 :     if (idSetter.ShouldForceLayerForScrollParent() &&
    3535           0 :         !gfxPrefs::LayoutUseContainersForRootFrames())
    3536             :     {
    3537             :       // Note that forcing layerization of scroll parents follows the scroll
    3538             :       // handoff chain which is subject to the out-of-flow-frames caveat noted
    3539             :       // above (where the idSetter variable is created).
    3540             :       //
    3541             :       // This is not compatible when using containes for root scrollframes.
    3542           0 :       MOZ_ASSERT(couldBuildLayer && mScrolledFrame->GetContent());
    3543           0 :       if (!mWillBuildScrollableLayer) {
    3544             :         // Set a displayport so next paint we don't have to force layerization
    3545             :         // after the fact.
    3546           0 :         nsLayoutUtils::SetDisplayPortMargins(mOuter->GetContent(),
    3547           0 :                                              mOuter->PresContext()->PresShell(),
    3548           0 :                                              ScreenMargin(),
    3549             :                                              0,
    3550           0 :                                              nsLayoutUtils::RepaintMode::DoNotRepaint);
    3551             :         // Call DecideScrollableLayer to recompute mWillBuildScrollableLayer and
    3552             :         // recompute the current animated geometry root if needed.
    3553             :         // It's too late to change the dirty rect so pass a copy.
    3554           0 :         nsRect copyOfDirtyRect = dirtyRect;
    3555           0 :         Unused << DecideScrollableLayer(aBuilder, &copyOfDirtyRect,
    3556             :                     /* aAllowCreateDisplayPort = */ false);
    3557           0 :         if (mWillBuildScrollableLayer) {
    3558           0 :           asrSetter.InsertScrollFrame(sf);
    3559             :         }
    3560             :       }
    3561             :     }
    3562             :   }
    3563             : 
    3564         231 :   if (mWillBuildScrollableLayer) {
    3565           2 :     aBuilder->ForceLayerForScrollParent();
    3566             :   }
    3567             : 
    3568         231 :   if (couldBuildLayer) {
    3569             :     // Make sure that APZ will dispatch events back to content so we can create
    3570             :     // a displayport for this frame. We'll add the item later on.
    3571           2 :     nsDisplayLayerEventRegions* inactiveRegionItem = nullptr;
    3572           6 :     if (aBuilder->IsPaintingToWindow() &&
    3573           2 :         !mWillBuildScrollableLayer &&
    3574           0 :         aBuilder->IsBuildingLayerEventRegions())
    3575             :     {
    3576           0 :       inactiveRegionItem = new (aBuilder) nsDisplayLayerEventRegions(aBuilder, mScrolledFrame);
    3577           0 :       inactiveRegionItem->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
    3578             :     }
    3579             : 
    3580           2 :     if (inactiveRegionItem) {
    3581             :       int32_t zIndex =
    3582           0 :         MaxZIndexInListOfItemsContainedInFrame(scrolledContent.PositionedDescendants(), mOuter);
    3583           0 :       AppendInternalItemToTop(scrolledContent, inactiveRegionItem, zIndex);
    3584             :     }
    3585             : 
    3586           2 :     if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) {
    3587           0 :       aBuilder->AppendNewScrollInfoItemForHoisting(
    3588             :         new (aBuilder) nsDisplayScrollInfoLayer(aBuilder, mScrolledFrame,
    3589           0 :                                                 mOuter));
    3590             :     }
    3591             :   }
    3592             :   // Now display overlay scrollbars and the resizer, if we have one.
    3593         231 :   AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent,
    3594         231 :                       createLayersForScrollbars, true);
    3595         231 :   scrolledContent.MoveTo(aLists);
    3596             : }
    3597             : 
    3598             : bool
    3599         269 : ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
    3600             :                                          nsRect* aDirtyRect,
    3601             :                                          bool aAllowCreateDisplayPort)
    3602             : {
    3603             :   // Save and check if this changes so we can recompute the current agr.
    3604         269 :   bool oldWillBuildScrollableLayer = mWillBuildScrollableLayer;
    3605             : 
    3606         269 :   nsIContent* content = mOuter->GetContent();
    3607         269 :   bool wasUsingDisplayPort = nsLayoutUtils::HasDisplayPort(content);
    3608         269 :   bool usingDisplayPort = wasUsingDisplayPort;
    3609             : 
    3610         269 :   if (aBuilder->IsPaintingToWindow()) {
    3611         233 :     if (aAllowCreateDisplayPort) {
    3612         231 :       nsLayoutUtils::MaybeCreateDisplayPort(*aBuilder, mOuter);
    3613             : 
    3614         462 :       nsRect displayportBase = *aDirtyRect;
    3615         231 :       nsPresContext* pc = mOuter->PresContext();
    3616         231 :       if (mIsRoot && (pc->IsRootContentDocument() || !pc->GetParentPresContext())) {
    3617           2 :         displayportBase =
    3618           4 :           nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
    3619             :       } else {
    3620             :         // Make the displayport base equal to the dirty rect restricted to
    3621             :         // the scrollport and the root composition bounds, relative to the
    3622             :         // scrollport.
    3623         229 :         displayportBase = aDirtyRect->Intersect(mScrollPort);
    3624             : 
    3625             :         // Only restrict to the root composition bounds if necessary,
    3626             :         // as the required coordinate transformation is expensive.
    3627             :         // Note that we call HasDisplayPort again instead of using
    3628             :         // wasUsingDisplayPort because we might have just created a display port.
    3629         229 :         if (nsLayoutUtils::HasDisplayPort(content)) {
    3630             :           const nsPresContext* rootPresContext =
    3631           0 :             pc->GetToplevelContentDocumentPresContext();
    3632           0 :           if (!rootPresContext) {
    3633           0 :             rootPresContext = pc->GetRootPresContext();
    3634             :           }
    3635           0 :           if (rootPresContext) {
    3636           0 :             const nsIPresShell* const rootPresShell = rootPresContext->PresShell();
    3637           0 :             nsIFrame* rootFrame = rootPresShell->GetRootScrollFrame();
    3638           0 :             if (!rootFrame) {
    3639           0 :               rootFrame = rootPresShell->GetRootFrame();
    3640             :             }
    3641           0 :             if (rootFrame) {
    3642             :               nsRect rootCompBounds =
    3643           0 :                 nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootFrame));
    3644             : 
    3645             :               // If rootFrame is the RCD-RSF then CalculateCompositionSizeForFrame
    3646             :               // did not take the document's resolution into account, so we must.
    3647           0 :               if (rootPresContext->IsRootContentDocument() &&
    3648           0 :                   rootFrame == rootPresShell->GetRootScrollFrame()) {
    3649           0 :                 rootCompBounds = rootCompBounds.RemoveResolution(rootPresShell->GetResolution());
    3650             :               }
    3651             : 
    3652             :               // We want to convert the root composition bounds from the coordinate
    3653             :               // space of |rootFrame| to the coordinate space of |mOuter|. We do
    3654             :               // that with the TransformRect call below. However, since we care
    3655             :               // about the root composition bounds relative to what the user is
    3656             :               // actually seeing, we also need to incorporate the APZ callback
    3657             :               // transforms into this. Most of the time those transforms are
    3658             :               // negligible, but in some cases (e.g. when a zoom is applied on
    3659             :               // an overflow:hidden document) it is not (see bug 1280013).
    3660             :               // XXX: Eventually we may want to create a modified version of
    3661             :               // TransformRect that includes the APZ callback transforms
    3662             :               // directly.
    3663           0 :               nsLayoutUtils::TransformRect(rootFrame, mOuter, rootCompBounds);
    3664           0 :               rootCompBounds += CSSPoint::ToAppUnits(
    3665           0 :                   nsLayoutUtils::GetCumulativeApzCallbackTransform(mOuter));
    3666             : 
    3667             :               // We want to limit displayportBase to be no larger than rootCompBounds on
    3668             :               // either axis, but we don't want to just blindly intersect the two, because
    3669             :               // rootCompBounds might be offset from where displayportBase is (see bug
    3670             :               // 1327095 comment 8). Instead, we translate rootCompBounds so as to
    3671             :               // maximize the overlap with displayportBase, and *then* do the intersection.
    3672           0 :               if (rootCompBounds.x > displayportBase.x && rootCompBounds.XMost() > displayportBase.XMost()) {
    3673             :                 // rootCompBounds is at a greater x-position for both left and right, so translate it such
    3674             :                 // that the XMost() values are the same. This will line up the right edge of the two rects,
    3675             :                 // and might mean that rootCompbounds.x is smaller than displayportBase.x. We can avoid that
    3676             :                 // by taking the min of the x delta and XMost() delta, but it doesn't really matter because
    3677             :                 // the intersection between the two rects below will end up the same.
    3678           0 :                 rootCompBounds.x -= (rootCompBounds.XMost() - displayportBase.XMost());
    3679           0 :               } else if (rootCompBounds.x < displayportBase.x && rootCompBounds.XMost() < displayportBase.XMost()) {
    3680             :                 // Analaogous code for when the rootCompBounds is at a smaller x-position.
    3681           0 :                 rootCompBounds.x = displayportBase.x;
    3682             :               }
    3683             :               // Do the same for y-axis
    3684           0 :               if (rootCompBounds.y > displayportBase.y && rootCompBounds.YMost() > displayportBase.YMost()) {
    3685           0 :                 rootCompBounds.y -= (rootCompBounds.YMost() - displayportBase.YMost());
    3686           0 :               } else if (rootCompBounds.y < displayportBase.y && rootCompBounds.YMost() < displayportBase.YMost()) {
    3687           0 :                 rootCompBounds.y = displayportBase.y;
    3688             :               }
    3689             : 
    3690             :               // Now we can do the intersection
    3691           0 :               displayportBase = displayportBase.Intersect(rootCompBounds);
    3692             :             }
    3693             :           }
    3694             :         }
    3695             : 
    3696         229 :         displayportBase -= mScrollPort.TopLeft();
    3697             :       }
    3698             : 
    3699         231 :       nsLayoutUtils::SetDisplayPortBase(mOuter->GetContent(), displayportBase);
    3700             :     }
    3701             : 
    3702             :     // If we don't have aAllowCreateDisplayPort == true then should have already
    3703             :     // been called with aAllowCreateDisplayPort == true which should have set a
    3704             :     // displayport base.
    3705         233 :     MOZ_ASSERT(content->GetProperty(nsGkAtoms::DisplayPortBase));
    3706         466 :     nsRect displayPort;
    3707             :     usingDisplayPort =
    3708         233 :       nsLayoutUtils::GetDisplayPort(content, &displayPort, RelativeTo::ScrollFrame);
    3709             : 
    3710         233 :     if (usingDisplayPort) {
    3711             :       // Override the dirty rectangle if the displayport has been set.
    3712           4 :       *aDirtyRect = displayPort;
    3713         229 :     } else if (mIsRoot) {
    3714             :       // The displayPort getter takes care of adjusting for resolution. So if
    3715             :       // we have resolution but no displayPort then we need to adjust for
    3716             :       // resolution here.
    3717           0 :       nsIPresShell* presShell = mOuter->PresContext()->PresShell();
    3718           0 :       *aDirtyRect = aDirtyRect->RemoveResolution(
    3719           0 :         presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f);
    3720             :     }
    3721             :   }
    3722             : 
    3723             :   // Since making new layers is expensive, only create a scrollable layer
    3724             :   // for some scroll frames.
    3725             :   // When a displayport is being used, force building of a layer so that
    3726             :   // the compositor can find the scrollable layer for async scrolling.
    3727             :   // If the element is marked 'scrollgrab', also force building of a layer
    3728             :   // so that APZ can implement scroll grabbing.
    3729         269 :   mWillBuildScrollableLayer = usingDisplayPort || nsContentUtils::HasScrollgrab(content);
    3730             : 
    3731             :   // The cached animated geometry root for the display builder is out of
    3732             :   // date if we just introduced a new animated geometry root.
    3733         269 :   if ((oldWillBuildScrollableLayer != mWillBuildScrollableLayer) || (wasUsingDisplayPort != usingDisplayPort)) {
    3734           1 :     aBuilder->RecomputeCurrentAnimatedGeometryRoot();
    3735             :   }
    3736             : 
    3737         269 :   if (gfxPrefs::LayoutUseContainersForRootFrames() && mWillBuildScrollableLayer && mIsRoot) {
    3738           0 :     mIsScrollableLayerInRootContainer = true;
    3739             :   }
    3740             : 
    3741         269 :   return mWillBuildScrollableLayer;
    3742             : }
    3743             : 
    3744             : 
    3745             : Maybe<ScrollMetadata>
    3746           3 : ScrollFrameHelper::ComputeScrollMetadata(Layer* aLayer,
    3747             :                                          nsIFrame* aContainerReferenceFrame,
    3748             :                                          const ContainerLayerParameters& aParameters,
    3749             :                                          const DisplayItemClip* aClip) const
    3750             : {
    3751           3 :   if (!mWillBuildScrollableLayer || mIsScrollableLayerInRootContainer) {
    3752           0 :     return Nothing();
    3753             :   }
    3754             : 
    3755           3 :   nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
    3756             : 
    3757           6 :   Maybe<nsRect> parentLayerClip;
    3758             :   // For containerful frames, the clip is on the container layer.
    3759           9 :   if (aClip &&
    3760           3 :       (!gfxPrefs::LayoutUseContainersForRootFrames() || mAddClipRectToLayer)) {
    3761           3 :     parentLayerClip = Some(aClip->GetClipRect());
    3762             :   }
    3763             : 
    3764           3 :   bool isRootContent = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
    3765           3 :   bool thisScrollFrameUsesAsyncScrolling = nsLayoutUtils::UsesAsyncScrolling(mOuter);
    3766           3 :   if (!thisScrollFrameUsesAsyncScrolling) {
    3767           0 :     if (parentLayerClip) {
    3768             :       // If APZ is not enabled, we still need the displayport to be clipped
    3769             :       // in the compositor.
    3770             :       ParentLayerIntRect displayportClip =
    3771             :         ViewAs<ParentLayerPixel>(
    3772           0 :           parentLayerClip->ScaleToNearestPixels(
    3773           0 :             aParameters.mXScale,
    3774           0 :             aParameters.mYScale,
    3775           0 :             mScrolledFrame->PresContext()->AppUnitsPerDevPixel()));
    3776             : 
    3777           0 :       ParentLayerIntRect layerClip;
    3778           0 :       if (const ParentLayerIntRect* origClip = aLayer->GetClipRect().ptrOr(nullptr)) {
    3779           0 :         layerClip = displayportClip.Intersect(*origClip);
    3780             :       } else {
    3781           0 :         layerClip = displayportClip;
    3782             :       }
    3783           0 :       aLayer->SetClipRect(Some(layerClip));
    3784             :     }
    3785             : 
    3786             :     // Return early, since if we don't use APZ we don't need FrameMetrics.
    3787           0 :     return Nothing();
    3788             :   }
    3789             : 
    3790           3 :   MOZ_ASSERT(mScrolledFrame->GetContent());
    3791             : 
    3792           6 :   nsRect scrollport = mScrollPort + toReferenceFrame;
    3793             : 
    3794          12 :   return Some(nsLayoutUtils::ComputeScrollMetadata(
    3795           6 :     mScrolledFrame, mOuter, mOuter->GetContent(),
    3796           3 :     aContainerReferenceFrame, aLayer, mScrollParentID,
    3797           3 :     scrollport, parentLayerClip, isRootContent, aParameters));
    3798             : }
    3799             : 
    3800             : bool
    3801           0 : ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
    3802             : {
    3803             :   // Use the right rect depending on if a display port is set.
    3804           0 :   nsRect displayPort;
    3805             :   bool usingDisplayport =
    3806           0 :     nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort, RelativeTo::ScrollFrame);
    3807           0 :   return aRect.Intersects(ExpandRectToNearlyVisible(usingDisplayport ? displayPort : mScrollPort));
    3808             : }
    3809             : 
    3810          98 : static void HandleScrollPref(nsIScrollable *aScrollable, int32_t aOrientation,
    3811             :                              uint8_t& aValue)
    3812             : {
    3813             :   int32_t pref;
    3814          98 :   aScrollable->GetDefaultScrollbarPreferences(aOrientation, &pref);
    3815          98 :   switch (pref) {
    3816             :     case nsIScrollable::Scrollbar_Auto:
    3817             :       // leave |aValue| untouched
    3818          98 :       break;
    3819             :     case nsIScrollable::Scrollbar_Never:
    3820           0 :       aValue = NS_STYLE_OVERFLOW_HIDDEN;
    3821           0 :       break;
    3822             :     case nsIScrollable::Scrollbar_Always:
    3823           0 :       aValue = NS_STYLE_OVERFLOW_SCROLL;
    3824           0 :       break;
    3825             :   }
    3826          98 : }
    3827             : 
    3828             : ScrollbarStyles
    3829        1278 : ScrollFrameHelper::GetScrollbarStylesFromFrame() const
    3830             : {
    3831        1278 :   nsPresContext* presContext = mOuter->PresContext();
    3832        1278 :   if (!presContext->IsDynamic() &&
    3833           0 :       !(mIsRoot && presContext->HasPaginatedScrolling())) {
    3834           0 :     return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
    3835             :   }
    3836             : 
    3837        1278 :   if (!mIsRoot) {
    3838        1190 :     const nsStyleDisplay* disp = mOuter->StyleDisplay();
    3839        1190 :     return ScrollbarStyles(disp);
    3840             :   }
    3841             : 
    3842         176 :   ScrollbarStyles result = presContext->GetViewportScrollbarStylesOverride();
    3843         176 :   nsCOMPtr<nsISupports> container = presContext->GetContainerWeak();
    3844         176 :   nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(container);
    3845          88 :   if (scrollable) {
    3846          49 :     HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_X,
    3847          49 :                      result.mHorizontal);
    3848          49 :     HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_Y,
    3849          49 :                      result.mVertical);
    3850             :   }
    3851          88 :   return result;
    3852             : }
    3853             : 
    3854             : nsRect
    3855         478 : ScrollFrameHelper::GetScrollRange() const
    3856             : {
    3857         478 :   return GetScrollRange(mScrollPort.width, mScrollPort.height);
    3858             : }
    3859             : 
    3860             : nsRect
    3861         633 : ScrollFrameHelper::GetScrollRange(nscoord aWidth, nscoord aHeight) const
    3862             : {
    3863         633 :   nsRect range = GetScrolledRect();
    3864         633 :   range.width = std::max(range.width - aWidth, 0);
    3865         633 :   range.height = std::max(range.height - aHeight, 0);
    3866         633 :   return range;
    3867             : }
    3868             : 
    3869             : nsRect
    3870         155 : ScrollFrameHelper::GetScrollRangeForClamping() const
    3871             : {
    3872         155 :   if (!ShouldClampScrollPosition()) {
    3873             :     return nsRect(nscoord_MIN/2, nscoord_MIN/2,
    3874           0 :                   nscoord_MAX - nscoord_MIN/2, nscoord_MAX - nscoord_MIN/2);
    3875             :   }
    3876         155 :   nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
    3877         155 :   return GetScrollRange(scrollPortSize.width, scrollPortSize.height);
    3878             : }
    3879             : 
    3880             : nsSize
    3881         661 : ScrollFrameHelper::GetScrollPositionClampingScrollPortSize() const
    3882             : {
    3883         661 :   nsIPresShell* presShell = mOuter->PresContext()->PresShell();
    3884         661 :   if (mIsRoot && presShell->IsScrollPositionClampingScrollPortSizeSet()) {
    3885           0 :     return presShell->GetScrollPositionClampingScrollPortSize();
    3886             :   }
    3887         661 :   return mScrollPort.Size();
    3888             : }
    3889             : 
    3890             : static void
    3891           0 : AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord)
    3892             : {
    3893           0 :   if (aDelta < 0) {
    3894           0 :     *aCoord = nscoord_MIN;
    3895           0 :   } else if (aDelta > 0) {
    3896           0 :     *aCoord = nscoord_MAX;
    3897             :   }
    3898           0 : }
    3899             : 
    3900             : /**
    3901             :  * Calculate lower/upper scrollBy range in given direction.
    3902             :  * @param aDelta specifies scrollBy direction, if 0 then range will be 0 size
    3903             :  * @param aPos desired destination in AppUnits
    3904             :  * @param aNeg/PosTolerance defines relative range distance
    3905             :  *   below and above of aPos point
    3906             :  * @param aMultiplier used for conversion of tolerance into appUnis
    3907             :  */
    3908             : static void
    3909           0 : CalcRangeForScrollBy(int32_t aDelta, nscoord aPos,
    3910             :                      float aNegTolerance,
    3911             :                      float aPosTolerance,
    3912             :                      nscoord aMultiplier,
    3913             :                      nscoord* aLower, nscoord* aUpper)
    3914             : {
    3915           0 :   if (!aDelta) {
    3916           0 :     *aLower = *aUpper = aPos;
    3917           0 :     return;
    3918             :   }
    3919           0 :   *aLower = aPos - NSToCoordRound(aMultiplier * (aDelta > 0 ? aNegTolerance : aPosTolerance));
    3920           0 :   *aUpper = aPos + NSToCoordRound(aMultiplier * (aDelta > 0 ? aPosTolerance : aNegTolerance));
    3921             : }
    3922             : 
    3923             : void
    3924           0 : ScrollFrameHelper::ScrollBy(nsIntPoint aDelta,
    3925             :                             nsIScrollableFrame::ScrollUnit aUnit,
    3926             :                             nsIScrollableFrame::ScrollMode aMode,
    3927             :                             nsIntPoint* aOverflow,
    3928             :                             nsIAtom *aOrigin,
    3929             :                             nsIScrollableFrame::ScrollMomentum aMomentum,
    3930             :                             nsIScrollbarMediator::ScrollSnapMode aSnap)
    3931             : {
    3932             :   // When a smooth scroll is being processed on a frame, mouse wheel and trackpad
    3933             :   // momentum scroll event updates must notcancel the SMOOTH or SMOOTH_MSD
    3934             :   // scroll animations, enabling Javascript that depends on them to be responsive
    3935             :   // without forcing the user to wait for the fling animations to completely stop.
    3936           0 :   switch (aMomentum) {
    3937             :   case nsIScrollableFrame::NOT_MOMENTUM:
    3938           0 :     mIgnoreMomentumScroll = false;
    3939           0 :     break;
    3940             :   case nsIScrollableFrame::SYNTHESIZED_MOMENTUM_EVENT:
    3941           0 :     if (mIgnoreMomentumScroll) {
    3942           0 :       return;
    3943             :     }
    3944           0 :     break;
    3945             :   }
    3946             : 
    3947           0 :   if (mAsyncSmoothMSDScroll != nullptr) {
    3948             :     // When CSSOM-View scroll-behavior smooth scrolling is interrupted,
    3949             :     // the scroll is not completed to avoid non-smooth snapping to the
    3950             :     // prior smooth scroll's destination.
    3951           0 :     mDestination = GetScrollPosition();
    3952             :   }
    3953             : 
    3954           0 :   nsSize deltaMultiplier;
    3955             :   float negativeTolerance;
    3956             :   float positiveTolerance;
    3957           0 :   if (!aOrigin){
    3958           0 :     aOrigin = nsGkAtoms::other;
    3959             :   }
    3960           0 :   bool isGenericOrigin = (aOrigin == nsGkAtoms::other);
    3961           0 :   switch (aUnit) {
    3962             :   case nsIScrollableFrame::DEVICE_PIXELS: {
    3963             :     nscoord appUnitsPerDevPixel =
    3964           0 :       mOuter->PresContext()->AppUnitsPerDevPixel();
    3965           0 :     deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
    3966           0 :     if (isGenericOrigin){
    3967           0 :       aOrigin = nsGkAtoms::pixels;
    3968             :     }
    3969           0 :     negativeTolerance = positiveTolerance = 0.5f;
    3970           0 :     break;
    3971             :   }
    3972             :   case nsIScrollableFrame::LINES: {
    3973           0 :     deltaMultiplier = GetLineScrollAmount();
    3974           0 :     if (isGenericOrigin){
    3975           0 :       aOrigin = nsGkAtoms::lines;
    3976             :     }
    3977           0 :     negativeTolerance = positiveTolerance = 0.1f;
    3978           0 :     break;
    3979             :   }
    3980             :   case nsIScrollableFrame::PAGES: {
    3981           0 :     deltaMultiplier = GetPageScrollAmount();
    3982           0 :     if (isGenericOrigin){
    3983           0 :       aOrigin = nsGkAtoms::pages;
    3984             :     }
    3985           0 :     negativeTolerance = 0.05f;
    3986           0 :     positiveTolerance = 0;
    3987           0 :     break;
    3988             :   }
    3989             :   case nsIScrollableFrame::WHOLE: {
    3990           0 :     nsPoint pos = GetScrollPosition();
    3991           0 :     AdjustForWholeDelta(aDelta.x, &pos.x);
    3992           0 :     AdjustForWholeDelta(aDelta.y, &pos.y);
    3993           0 :     if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
    3994           0 :       GetSnapPointForDestination(aUnit, mDestination, pos);
    3995             :     }
    3996           0 :     ScrollTo(pos, aMode);
    3997             :     // 'this' might be destroyed here
    3998           0 :     if (aOverflow) {
    3999           0 :       *aOverflow = nsIntPoint(0, 0);
    4000             :     }
    4001           0 :     return;
    4002             :   }
    4003             :   default:
    4004           0 :     NS_ERROR("Invalid scroll mode");
    4005           0 :     return;
    4006             :   }
    4007             : 
    4008           0 :   nsPoint newPos = mDestination + nsPoint(aDelta.x*deltaMultiplier.width, aDelta.y*deltaMultiplier.height);
    4009             : 
    4010           0 :   if (aSnap == nsIScrollableFrame::ENABLE_SNAP) {
    4011           0 :     ScrollbarStyles styles = GetScrollbarStylesFromFrame();
    4012           0 :     if (styles.mScrollSnapTypeY != NS_STYLE_SCROLL_SNAP_TYPE_NONE ||
    4013           0 :         styles.mScrollSnapTypeX != NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
    4014           0 :       nscoord appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
    4015           0 :       deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
    4016           0 :       negativeTolerance = 0.1f;
    4017           0 :       positiveTolerance = 0;
    4018           0 :       nsIScrollableFrame::ScrollUnit snapUnit = aUnit;
    4019           0 :       if (aOrigin == nsGkAtoms::mouseWheel) {
    4020             :         // When using a clicky scroll wheel, snap point selection works the same
    4021             :         // as keyboard up/down/left/right navigation, but with varying amounts
    4022             :         // of scroll delta.
    4023           0 :         snapUnit = nsIScrollableFrame::LINES;
    4024             :       }
    4025           0 :       GetSnapPointForDestination(snapUnit, mDestination, newPos);
    4026             :     }
    4027             :   }
    4028             : 
    4029             :   // Calculate desired range values.
    4030             :   nscoord rangeLowerX, rangeUpperX, rangeLowerY, rangeUpperY;
    4031           0 :   CalcRangeForScrollBy(aDelta.x, newPos.x, negativeTolerance, positiveTolerance,
    4032           0 :                        deltaMultiplier.width, &rangeLowerX, &rangeUpperX);
    4033           0 :   CalcRangeForScrollBy(aDelta.y, newPos.y, negativeTolerance, positiveTolerance,
    4034           0 :                        deltaMultiplier.height, &rangeLowerY, &rangeUpperY);
    4035             :   nsRect range(rangeLowerX,
    4036             :                rangeLowerY,
    4037             :                rangeUpperX - rangeLowerX,
    4038           0 :                rangeUpperY - rangeLowerY);
    4039           0 :   AutoWeakFrame weakFrame(mOuter);
    4040           0 :   ScrollToWithOrigin(newPos, aMode, aOrigin, &range);
    4041           0 :   if (!weakFrame.IsAlive()) {
    4042           0 :     return;
    4043             :   }
    4044             : 
    4045           0 :   if (aOverflow) {
    4046           0 :     nsPoint clampAmount = newPos - mDestination;
    4047           0 :     float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
    4048           0 :     *aOverflow = nsIntPoint(
    4049             :         NSAppUnitsToIntPixels(clampAmount.x, appUnitsPerDevPixel),
    4050             :         NSAppUnitsToIntPixels(clampAmount.y, appUnitsPerDevPixel));
    4051             :   }
    4052             : 
    4053           0 :   if (aUnit == nsIScrollableFrame::DEVICE_PIXELS &&
    4054           0 :       !nsLayoutUtils::AsyncPanZoomEnabled(mOuter)) {
    4055             :     // When APZ is disabled, we must track the velocity
    4056             :     // on the main thread; otherwise, the APZC will manage this.
    4057           0 :     mVelocityQueue.Sample(GetScrollPosition());
    4058             :   }
    4059             : }
    4060             : 
    4061             : void
    4062           0 : ScrollFrameHelper::ScrollSnap(nsIScrollableFrame::ScrollMode aMode)
    4063             : {
    4064           0 :   float flingSensitivity = gfxPrefs::ScrollSnapPredictionSensitivity();
    4065           0 :   int maxVelocity = gfxPrefs::ScrollSnapPredictionMaxVelocity();
    4066           0 :   maxVelocity = nsPresContext::CSSPixelsToAppUnits(maxVelocity);
    4067           0 :   int maxOffset = maxVelocity * flingSensitivity;
    4068           0 :   nsPoint velocity = mVelocityQueue.GetVelocity();
    4069             :   // Multiply each component individually to avoid integer multiply
    4070           0 :   nsPoint predictedOffset = nsPoint(velocity.x * flingSensitivity,
    4071           0 :                                     velocity.y * flingSensitivity);
    4072           0 :   predictedOffset.Clamp(maxOffset);
    4073           0 :   nsPoint pos = GetScrollPosition();
    4074           0 :   nsPoint destinationPos = pos + predictedOffset;
    4075           0 :   ScrollSnap(destinationPos, aMode);
    4076           0 : }
    4077             : 
    4078             : void
    4079           0 : ScrollFrameHelper::ScrollSnap(const nsPoint &aDestination,
    4080             :                               nsIScrollableFrame::ScrollMode aMode)
    4081             : {
    4082           0 :   nsRect scrollRange = GetScrollRangeForClamping();
    4083           0 :   nsPoint pos = GetScrollPosition();
    4084           0 :   nsPoint snapDestination = scrollRange.ClampPoint(aDestination);
    4085           0 :   if (GetSnapPointForDestination(nsIScrollableFrame::DEVICE_PIXELS,
    4086             :                                                  pos,
    4087             :                                                  snapDestination)) {
    4088           0 :     ScrollTo(snapDestination, aMode);
    4089             :   }
    4090           0 : }
    4091             : 
    4092             : nsSize
    4093           9 : ScrollFrameHelper::GetLineScrollAmount() const
    4094             : {
    4095             :   RefPtr<nsFontMetrics> fm =
    4096          18 :     nsLayoutUtils::GetInflatedFontMetricsForFrame(mOuter);
    4097           9 :   NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
    4098             :   static nscoord sMinLineScrollAmountInPixels = -1;
    4099           9 :   if (sMinLineScrollAmountInPixels < 0) {
    4100             :     Preferences::AddIntVarCache(&sMinLineScrollAmountInPixels,
    4101           2 :                                 "mousewheel.min_line_scroll_amount", 1);
    4102             :   }
    4103           9 :   int32_t appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
    4104             :   nscoord minScrollAmountInAppUnits =
    4105           9 :     std::max(1, sMinLineScrollAmountInPixels) * appUnitsPerDevPixel;
    4106           9 :   nscoord horizontalAmount = fm ? fm->AveCharWidth() : 0;
    4107           9 :   nscoord verticalAmount = fm ? fm->MaxHeight() : 0;
    4108          18 :   return nsSize(std::max(horizontalAmount, minScrollAmountInAppUnits),
    4109          27 :                 std::max(verticalAmount, minScrollAmountInAppUnits));
    4110             : }
    4111             : 
    4112             : /**
    4113             :  * Compute the scrollport size excluding any fixed-pos headers and
    4114             :  * footers. A header or footer is an box that spans that entire width
    4115             :  * of the viewport and touches the top (or bottom, respectively) of the
    4116             :  * viewport. We also want to consider fixed elements that stack or overlap
    4117             :  * to effectively create a larger header or footer. Headers and footers that
    4118             :  * cover more than a third of the the viewport are ignored since they
    4119             :  * probably aren't true headers and footers and we don't want to restrict
    4120             :  * scrolling too much in such cases. This is a bit conservative --- some
    4121             :  * pages use elements as headers or footers that don't span the entire width
    4122             :  * of the viewport --- but it should be a good start.
    4123             :  */
    4124             : struct TopAndBottom
    4125             : {
    4126           0 :   TopAndBottom(nscoord aTop, nscoord aBottom) : top(aTop), bottom(aBottom) {}
    4127             : 
    4128             :   nscoord top, bottom;
    4129             : };
    4130             : struct TopComparator
    4131             : {
    4132           0 :   bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
    4133           0 :     return A.top == B.top;
    4134             :   }
    4135           0 :   bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
    4136           0 :     return A.top < B.top;
    4137             :   }
    4138             : };
    4139             : struct ReverseBottomComparator
    4140             : {
    4141           0 :   bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
    4142           0 :     return A.bottom == B.bottom;
    4143             :   }
    4144           0 :   bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
    4145           0 :     return A.bottom > B.bottom;
    4146             :   }
    4147             : };
    4148             : static nsSize
    4149           3 : GetScrollPortSizeExcludingHeadersAndFooters(nsIFrame* aViewportFrame,
    4150             :                                             const nsRect& aScrollPort)
    4151             : {
    4152           6 :   AutoTArray<TopAndBottom, 50> list;
    4153           3 :   nsFrameList fixedFrames = aViewportFrame->GetChildList(nsIFrame::kFixedList);
    4154           3 :   for (nsFrameList::Enumerator iterator(fixedFrames); !iterator.AtEnd();
    4155           0 :        iterator.Next()) {
    4156           0 :     nsIFrame* f = iterator.get();
    4157           0 :     nsRect r = f->GetRectRelativeToSelf();
    4158           0 :     r = nsLayoutUtils::TransformFrameRectToAncestor(f, r, aViewportFrame);
    4159           0 :     r = r.Intersect(aScrollPort);
    4160           0 :     if ((r.width >= aScrollPort.width / 2 ||
    4161           0 :          r.width >= NSIntPixelsToAppUnits(800, AppUnitsPerCSSPixel())) &&
    4162           0 :         r.height <= aScrollPort.height/3) {
    4163           0 :       list.AppendElement(TopAndBottom(r.y, r.YMost()));
    4164             :     }
    4165             :   }
    4166             : 
    4167           3 :   list.Sort(TopComparator());
    4168           3 :   nscoord headerBottom = 0;
    4169           3 :   for (uint32_t i = 0; i < list.Length(); ++i) {
    4170           0 :     if (list[i].top <= headerBottom) {
    4171           0 :       headerBottom = std::max(headerBottom, list[i].bottom);
    4172             :     }
    4173             :   }
    4174             : 
    4175           3 :   list.Sort(ReverseBottomComparator());
    4176           3 :   nscoord footerTop = aScrollPort.height;
    4177           3 :   for (uint32_t i = 0; i < list.Length(); ++i) {
    4178           0 :     if (list[i].bottom >= footerTop) {
    4179           0 :       footerTop = std::min(footerTop, list[i].top);
    4180             :     }
    4181             :   }
    4182             : 
    4183           3 :   headerBottom = std::min(aScrollPort.height/3, headerBottom);
    4184           3 :   footerTop = std::max(aScrollPort.height - aScrollPort.height/3, footerTop);
    4185             : 
    4186           6 :   return nsSize(aScrollPort.width, footerTop - headerBottom);
    4187             : }
    4188             : 
    4189             : nsSize
    4190           3 : ScrollFrameHelper::GetPageScrollAmount() const
    4191             : {
    4192           3 :   nsSize lineScrollAmount = GetLineScrollAmount();
    4193           3 :   nsSize effectiveScrollPortSize;
    4194           3 :   if (mIsRoot) {
    4195             :     // Reduce effective scrollport height by the height of any fixed-pos
    4196             :     // headers or footers
    4197           3 :     nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
    4198             :     effectiveScrollPortSize =
    4199           3 :       GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
    4200             :   } else {
    4201           0 :     effectiveScrollPortSize = mScrollPort.Size();
    4202             :   }
    4203             :   // The page increment is the size of the page, minus the smaller of
    4204             :   // 10% of the size or 2 lines.
    4205           9 :   return nsSize(
    4206           3 :     effectiveScrollPortSize.width -
    4207           6 :       std::min(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
    4208           3 :     effectiveScrollPortSize.height -
    4209           9 :       std::min(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
    4210             : }
    4211             : 
    4212             :   /**
    4213             :    * this code is resposible for restoring the scroll position back to some
    4214             :    * saved position. if the user has not moved the scroll position manually
    4215             :    * we keep scrolling down until we get to our original position. keep in
    4216             :    * mind that content could incrementally be coming in. we only want to stop
    4217             :    * when we reach our new position.
    4218             :    */
    4219             : void
    4220         177 : ScrollFrameHelper::ScrollToRestoredPosition()
    4221             : {
    4222         177 :   if (mRestorePos.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
    4223         177 :     return;
    4224             :   }
    4225             :   // make sure our scroll position did not change for where we last put
    4226             :   // it. if it does then the user must have moved it, and we no longer
    4227             :   // need to restore.
    4228             :   //
    4229             :   // In the RTL case, we check whether the scroll position changed using the
    4230             :   // logical scroll position, but we scroll to the physical scroll position in
    4231             :   // all cases
    4232             : 
    4233             :   // if we didn't move, we still need to restore
    4234           0 :   if (GetLogicalScrollPosition() == mLastPos) {
    4235             :     // if our desired position is different to the scroll position, scroll.
    4236             :     // remember that we could be incrementally loading so we may enter
    4237             :     // and scroll many times.
    4238           0 :     if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
    4239           0 :       LoadingState state = GetPageLoadingState();
    4240           0 :       if (state == LoadingState::Stopped && !NS_SUBTREE_DIRTY(mOuter)) {
    4241           0 :         return;
    4242             :       }
    4243           0 :       nsPoint scrollToPos = mRestorePos;
    4244           0 :       if (!IsPhysicalLTR()) {
    4245             :         // convert from logical to physical scroll position
    4246           0 :         scrollToPos.x = mScrollPort.x -
    4247           0 :           (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
    4248             :       }
    4249           0 :       AutoWeakFrame weakFrame(mOuter);
    4250           0 :       ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
    4251           0 :                          nsGkAtoms::restore, nullptr);
    4252           0 :       if (!weakFrame.IsAlive()) {
    4253           0 :         return;
    4254             :       }
    4255           0 :       if (state == LoadingState::Loading || NS_SUBTREE_DIRTY(mOuter)) {
    4256             :         // If we're trying to do a history scroll restore, then we want to
    4257             :         // keep trying this until we succeed, because the page can be loading
    4258             :         // incrementally. So re-get the scroll position for the next iteration,
    4259             :         // it might not be exactly equal to mRestorePos due to rounding and
    4260             :         // clamping.
    4261           0 :         mLastPos = GetLogicalScrollPosition();
    4262           0 :         return;
    4263             :       }
    4264             :     }
    4265             :     // If we get here, either we reached the desired position (mLastPos ==
    4266             :     // mRestorePos) or we're not trying to do a history scroll restore, so
    4267             :     // we can stop after the scroll attempt above.
    4268           0 :     mRestorePos.y = -1;
    4269           0 :     mLastPos.x = -1;
    4270           0 :     mLastPos.y = -1;
    4271             :   } else {
    4272             :     // user moved the position, so we won't need to restore
    4273           0 :     mLastPos.x = -1;
    4274           0 :     mLastPos.y = -1;
    4275             :   }
    4276             : }
    4277             : 
    4278             : auto
    4279           0 : ScrollFrameHelper::GetPageLoadingState() -> LoadingState
    4280             : {
    4281           0 :   bool loadCompleted = false, stopped = false;
    4282           0 :   nsCOMPtr<nsIDocShell> ds = mOuter->GetContent()->GetComposedDoc()->GetDocShell();
    4283           0 :   if (ds) {
    4284           0 :     nsCOMPtr<nsIContentViewer> cv;
    4285           0 :     ds->GetContentViewer(getter_AddRefs(cv));
    4286           0 :     cv->GetLoadCompleted(&loadCompleted);
    4287           0 :     cv->GetIsStopped(&stopped);
    4288             :   }
    4289           0 :   return loadCompleted ? (stopped ? LoadingState::Stopped : LoadingState::Loaded)
    4290           0 :                        : LoadingState::Loading;
    4291             : }
    4292             : 
    4293             : nsresult
    4294          11 : ScrollFrameHelper::FireScrollPortEvent()
    4295             : {
    4296          11 :   mAsyncScrollPortEvent.Forget();
    4297             : 
    4298             :   // Keep this in sync with PostOverflowEvent().
    4299          11 :   nsSize scrollportSize = mScrollPort.Size();
    4300          11 :   nsSize childSize = GetScrolledRect().Size();
    4301             : 
    4302          11 :   bool newVerticalOverflow = childSize.height > scrollportSize.height;
    4303          11 :   bool vertChanged = mVerticalOverflow != newVerticalOverflow;
    4304             : 
    4305          11 :   bool newHorizontalOverflow = childSize.width > scrollportSize.width;
    4306          11 :   bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
    4307             : 
    4308          11 :   if (!vertChanged && !horizChanged) {
    4309           0 :     return NS_OK;
    4310             :   }
    4311             : 
    4312             :   // If both either overflowed or underflowed then we dispatch only one
    4313             :   // DOM event.
    4314          11 :   bool both = vertChanged && horizChanged &&
    4315          11 :                 newVerticalOverflow == newHorizontalOverflow;
    4316             :   InternalScrollPortEvent::OrientType orient;
    4317          11 :   if (both) {
    4318           1 :     orient = InternalScrollPortEvent::eBoth;
    4319           1 :     mHorizontalOverflow = newHorizontalOverflow;
    4320           1 :     mVerticalOverflow = newVerticalOverflow;
    4321          10 :   } else if (vertChanged) {
    4322           5 :     orient = InternalScrollPortEvent::eVertical;
    4323           5 :     mVerticalOverflow = newVerticalOverflow;
    4324           5 :     if (horizChanged) {
    4325             :       // We need to dispatch a separate horizontal DOM event. Do that the next
    4326             :       // time around since dispatching the vertical DOM event might destroy
    4327             :       // the frame.
    4328           0 :       PostOverflowEvent();
    4329             :     }
    4330             :   } else {
    4331           5 :     orient = InternalScrollPortEvent::eHorizontal;
    4332           5 :     mHorizontalOverflow = newHorizontalOverflow;
    4333             :   }
    4334             : 
    4335             :   InternalScrollPortEvent event(true,
    4336          11 :     (orient == InternalScrollPortEvent::eHorizontal ? mHorizontalOverflow :
    4337             :                                                       mVerticalOverflow) ?
    4338          22 :     eScrollPortOverflow : eScrollPortUnderflow, nullptr);
    4339          11 :   event.mOrient = orient;
    4340          11 :   return EventDispatcher::Dispatch(mOuter->GetContent(),
    4341          22 :                                    mOuter->PresContext(), &event);
    4342             : }
    4343             : 
    4344             : void
    4345          86 : ScrollFrameHelper::ReloadChildFrames()
    4346             : {
    4347          86 :   mScrolledFrame = nullptr;
    4348          86 :   mHScrollbarBox = nullptr;
    4349          86 :   mVScrollbarBox = nullptr;
    4350          86 :   mScrollCornerBox = nullptr;
    4351          86 :   mResizerBox = nullptr;
    4352             : 
    4353         141 :   for (nsIFrame* frame : mOuter->PrincipalChildList()) {
    4354          55 :     nsIContent* content = frame->GetContent();
    4355          55 :     if (content == mOuter->GetContent()) {
    4356          43 :       NS_ASSERTION(!mScrolledFrame, "Already found the scrolled frame");
    4357          43 :       mScrolledFrame = frame;
    4358             :     } else {
    4359          24 :       nsAutoString value;
    4360          12 :       content->GetAttr(kNameSpaceID_None, nsGkAtoms::orient, value);
    4361          12 :       if (!value.IsEmpty()) {
    4362             :         // probably a scrollbar then
    4363           8 :         if (value.LowerCaseEqualsLiteral("horizontal")) {
    4364           4 :           NS_ASSERTION(!mHScrollbarBox, "Found multiple horizontal scrollbars?");
    4365           4 :           mHScrollbarBox = frame;
    4366             :         } else {
    4367           4 :           NS_ASSERTION(!mVScrollbarBox, "Found multiple vertical scrollbars?");
    4368           4 :           mVScrollbarBox = frame;
    4369             :         }
    4370           4 :       } else if (content->IsXULElement(nsGkAtoms::resizer)) {
    4371           0 :         NS_ASSERTION(!mResizerBox, "Found multiple resizers");
    4372           0 :         mResizerBox = frame;
    4373           4 :       } else if (content->IsXULElement(nsGkAtoms::scrollcorner)) {
    4374             :         // probably a scrollcorner
    4375           4 :         NS_ASSERTION(!mScrollCornerBox, "Found multiple scrollcorners");
    4376           4 :         mScrollCornerBox = frame;
    4377             :       }
    4378             :     }
    4379             :   }
    4380          86 : }
    4381             : 
    4382             : nsresult
    4383          43 : ScrollFrameHelper::CreateAnonymousContent(
    4384             :   nsTArray<nsIAnonymousContentCreator::ContentInfo>& aElements)
    4385             : {
    4386          43 :   nsPresContext* presContext = mOuter->PresContext();
    4387          43 :   nsIFrame* parent = mOuter->GetParent();
    4388             : 
    4389             :   // Don't create scrollbars if we're an SVG document being used as an image,
    4390             :   // or if we're printing/print previewing.
    4391             :   // (In the printing case, we allow scrollbars if this is the child of the
    4392             :   // viewport & paginated scrolling is enabled, because then we must be the
    4393             :   // scroll frame for the print preview window, & that does need scrollbars.)
    4394          86 :   if (presContext->Document()->IsBeingUsedAsImage() ||
    4395          22 :       (!presContext->IsDynamic() &&
    4396           0 :        !(mIsRoot && presContext->HasPaginatedScrolling()))) {
    4397          21 :     mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
    4398          21 :     return NS_OK;
    4399             :   }
    4400             : 
    4401             :   // Check if the frame is resizable. Note:
    4402             :   // "The effect of the resize property on generated content is undefined.
    4403             :   //  Implementations should not apply the resize property to generated
    4404             :   //  content." [1]
    4405             :   // For info on what is generated content, see [2].
    4406             :   // [1]: https://drafts.csswg.org/css-ui/#resize
    4407             :   // [2]: https://www.w3.org/TR/CSS2/generate.html#content
    4408          22 :   int8_t resizeStyle = mOuter->StyleDisplay()->mResize;
    4409          22 :   bool isResizable = resizeStyle != NS_STYLE_RESIZE_NONE &&
    4410          22 :                      !mOuter->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT);
    4411             : 
    4412          22 :   nsIScrollableFrame *scrollable = do_QueryFrame(mOuter);
    4413             : 
    4414             :   // If we're the scrollframe for the root, then we want to construct
    4415             :   // our scrollbar frames no matter what.  That way later dynamic
    4416             :   // changes to propagated overflow styles will show or hide
    4417             :   // scrollbars on the viewport without requiring frame reconstruction
    4418             :   // of the viewport (good!).
    4419             :   bool canHaveHorizontal;
    4420             :   bool canHaveVertical;
    4421          22 :   if (!mIsRoot) {
    4422          24 :     ScrollbarStyles styles = scrollable->GetScrollbarStyles();
    4423          20 :     canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
    4424          20 :     canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
    4425          20 :     if (!canHaveHorizontal && !canHaveVertical && !isResizable) {
    4426             :       // Nothing to do.
    4427          16 :       return NS_OK;
    4428             :     }
    4429             :   } else {
    4430           2 :     canHaveHorizontal = true;
    4431           2 :     canHaveVertical = true;
    4432             :   }
    4433             : 
    4434             :   // The anonymous <div> used by <inputs> never gets scrollbars.
    4435           6 :   nsITextControlFrame* textFrame = do_QueryFrame(parent);
    4436           6 :   if (textFrame) {
    4437             :     // Make sure we are not a text area.
    4438           4 :     nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
    4439           4 :     if (!textAreaElement) {
    4440           4 :       mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
    4441           4 :       return NS_OK;
    4442             :     }
    4443             :   }
    4444             : 
    4445             :   nsNodeInfoManager *nodeInfoManager =
    4446           2 :     presContext->Document()->NodeInfoManager();
    4447           4 :   RefPtr<NodeInfo> nodeInfo;
    4448           4 :   nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbar, nullptr,
    4449             :                                           kNameSpaceID_XUL,
    4450           2 :                                           nsIDOMNode::ELEMENT_NODE);
    4451           2 :   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
    4452             : 
    4453           2 :   if (canHaveHorizontal) {
    4454           4 :     RefPtr<NodeInfo> ni = nodeInfo;
    4455           2 :     NS_TrustedNewXULElement(getter_AddRefs(mHScrollbarContent), ni.forget());
    4456             : #ifdef DEBUG
    4457             :     // Scrollbars can get restyled by theme changes.  Whether such a restyle
    4458             :     // will actually reconstruct them correctly if it involves a frame
    4459             :     // reconstruct... I don't know.  :(
    4460           2 :     mHScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
    4461           4 :                                     reinterpret_cast<void*>(true));
    4462             : #endif // DEBUG
    4463             : 
    4464           4 :     mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
    4465           6 :                                 NS_LITERAL_STRING("horizontal"), false);
    4466           4 :     mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
    4467           6 :                                 NS_LITERAL_STRING("always"), false);
    4468           2 :     if (mIsRoot) {
    4469           4 :       mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
    4470           6 :                                   NS_LITERAL_STRING("true"), false);
    4471             :     }
    4472           2 :     if (!aElements.AppendElement(mHScrollbarContent))
    4473           0 :       return NS_ERROR_OUT_OF_MEMORY;
    4474             :   }
    4475             : 
    4476           2 :   if (canHaveVertical) {
    4477           4 :     RefPtr<NodeInfo> ni = nodeInfo;
    4478           2 :     NS_TrustedNewXULElement(getter_AddRefs(mVScrollbarContent), ni.forget());
    4479             : #ifdef DEBUG
    4480             :     // Scrollbars can get restyled by theme changes.  Whether such a restyle
    4481             :     // will actually reconstruct them correctly if it involves a frame
    4482             :     // reconstruct... I don't know.  :(
    4483           2 :     mVScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
    4484           4 :                                     reinterpret_cast<void*>(true));
    4485             : #endif // DEBUG
    4486             : 
    4487           4 :     mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
    4488           6 :                                 NS_LITERAL_STRING("vertical"), false);
    4489           4 :     mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
    4490           6 :                                 NS_LITERAL_STRING("always"), false);
    4491           2 :     if (mIsRoot) {
    4492           4 :       mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
    4493           6 :                                   NS_LITERAL_STRING("true"), false);
    4494             :     }
    4495           2 :     if (!aElements.AppendElement(mVScrollbarContent))
    4496           0 :       return NS_ERROR_OUT_OF_MEMORY;
    4497             :   }
    4498             : 
    4499           2 :   if (isResizable) {
    4500           0 :     RefPtr<NodeInfo> nodeInfo;
    4501           0 :     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nullptr,
    4502             :                                             kNameSpaceID_XUL,
    4503           0 :                                             nsIDOMNode::ELEMENT_NODE);
    4504           0 :     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
    4505             : 
    4506           0 :     NS_TrustedNewXULElement(getter_AddRefs(mResizerContent), nodeInfo.forget());
    4507             : 
    4508           0 :     nsAutoString dir;
    4509           0 :     switch (resizeStyle) {
    4510             :       case NS_STYLE_RESIZE_HORIZONTAL:
    4511           0 :         if (IsScrollbarOnRight()) {
    4512           0 :           dir.AssignLiteral("right");
    4513             :         }
    4514             :         else {
    4515           0 :           dir.AssignLiteral("left");
    4516             :         }
    4517           0 :         break;
    4518             :       case NS_STYLE_RESIZE_VERTICAL:
    4519           0 :         dir.AssignLiteral("bottom");
    4520           0 :         break;
    4521             :       case NS_STYLE_RESIZE_BOTH:
    4522           0 :         dir.AssignLiteral("bottomend");
    4523           0 :         break;
    4524             :       default:
    4525           0 :         NS_WARNING("only resizable types should have resizers");
    4526             :     }
    4527           0 :     mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, false);
    4528             : 
    4529           0 :     if (mIsRoot) {
    4530           0 :       nsIContent* browserRoot = GetBrowserRoot(mOuter->GetContent());
    4531           0 :       mCollapsedResizer = !(browserRoot &&
    4532           0 :                             browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer));
    4533             :     }
    4534             :     else {
    4535           0 :       mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
    4536           0 :                                     NS_LITERAL_STRING("_parent"), false);
    4537             :     }
    4538             : 
    4539           0 :     mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
    4540           0 :                                   NS_LITERAL_STRING("always"), false);
    4541             : 
    4542           0 :     if (!aElements.AppendElement(mResizerContent))
    4543           0 :       return NS_ERROR_OUT_OF_MEMORY;
    4544             :   }
    4545             : 
    4546           2 :   if (canHaveHorizontal && canHaveVertical) {
    4547           4 :     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nullptr,
    4548             :                                             kNameSpaceID_XUL,
    4549           2 :                                             nsIDOMNode::ELEMENT_NODE);
    4550           2 :     NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo.forget());
    4551           2 :     if (!aElements.AppendElement(mScrollCornerContent))
    4552           0 :       return NS_ERROR_OUT_OF_MEMORY;
    4553             :   }
    4554             : 
    4555           2 :   return NS_OK;
    4556             : }
    4557             : 
    4558             : void
    4559           0 : ScrollFrameHelper::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
    4560             :                                                 uint32_t aFilter)
    4561             : {
    4562           0 :   if (mHScrollbarContent) {
    4563           0 :     aElements.AppendElement(mHScrollbarContent);
    4564             :   }
    4565             : 
    4566           0 :   if (mVScrollbarContent) {
    4567           0 :     aElements.AppendElement(mVScrollbarContent);
    4568             :   }
    4569             : 
    4570           0 :   if (mScrollCornerContent) {
    4571           0 :     aElements.AppendElement(mScrollCornerContent);
    4572             :   }
    4573             : 
    4574           0 :   if (mResizerContent) {
    4575           0 :     aElements.AppendElement(mResizerContent);
    4576             :   }
    4577           0 : }
    4578             : 
    4579             : void
    4580           7 : ScrollFrameHelper::Destroy()
    4581             : {
    4582           7 :   if (mScrollbarActivity) {
    4583           0 :     mScrollbarActivity->Destroy();
    4584           0 :     mScrollbarActivity = nullptr;
    4585             :   }
    4586             : 
    4587             :   // Unbind any content created in CreateAnonymousContent from the tree
    4588           7 :   nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent);
    4589           7 :   nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent);
    4590           7 :   nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent);
    4591           7 :   nsContentUtils::DestroyAnonymousContent(&mResizerContent);
    4592             : 
    4593           7 :   if (mPostedReflowCallback) {
    4594           0 :     mOuter->PresContext()->PresShell()->CancelReflowCallback(this);
    4595           0 :     mPostedReflowCallback = false;
    4596             :   }
    4597             : 
    4598           7 :   if (mDisplayPortExpiryTimer) {
    4599           0 :     mDisplayPortExpiryTimer->Cancel();
    4600           0 :     mDisplayPortExpiryTimer = nullptr;
    4601             :   }
    4602           7 :   if (mActivityExpirationState.IsTracked()) {
    4603           0 :     gScrollFrameActivityTracker->RemoveObject(this);
    4604             :   }
    4605           7 :   if (gScrollFrameActivityTracker &&
    4606           7 :       gScrollFrameActivityTracker->IsEmpty()) {
    4607           0 :     delete gScrollFrameActivityTracker;
    4608           0 :     gScrollFrameActivityTracker = nullptr;
    4609             :   }
    4610             : 
    4611           7 :   if (mScrollActivityTimer) {
    4612           0 :     mScrollActivityTimer->Cancel();
    4613           0 :     mScrollActivityTimer = nullptr;
    4614             :   }
    4615           7 : }
    4616             : 
    4617             : /**
    4618             :  * Called when we want to update the scrollbar position, either because scrolling happened
    4619             :  * or the user moved the scrollbar position and we need to undo that (e.g., when the user
    4620             :  * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
    4621             :  * to its initial position for the start of the smooth sequence).
    4622             :  */
    4623             : void
    4624           0 : ScrollFrameHelper::UpdateScrollbarPosition()
    4625             : {
    4626           0 :   AutoWeakFrame weakFrame(mOuter);
    4627           0 :   mFrameIsUpdatingScrollbar = true;
    4628             : 
    4629           0 :   nsPoint pt = GetScrollPosition();
    4630           0 :   if (mVScrollbarBox) {
    4631           0 :     SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
    4632           0 :                       pt.y - GetScrolledRect().y);
    4633           0 :     if (!weakFrame.IsAlive()) {
    4634           0 :       return;
    4635             :     }
    4636             :   }
    4637           0 :   if (mHScrollbarBox) {
    4638           0 :     SetCoordAttribute(mHScrollbarBox->GetContent(), nsGkAtoms::curpos,
    4639           0 :                       pt.x - GetScrolledRect().x);
    4640           0 :     if (!weakFrame.IsAlive()) {
    4641           0 :       return;
    4642             :     }
    4643             :   }
    4644             : 
    4645           0 :   mFrameIsUpdatingScrollbar = false;
    4646             : }
    4647             : 
    4648           7 : void ScrollFrameHelper::CurPosAttributeChanged(nsIContent* aContent)
    4649             : {
    4650           7 :   NS_ASSERTION(aContent, "aContent must not be null");
    4651           7 :   NS_ASSERTION((mHScrollbarBox && mHScrollbarBox->GetContent() == aContent) ||
    4652             :                (mVScrollbarBox && mVScrollbarBox->GetContent() == aContent),
    4653             :                "unexpected child");
    4654             : 
    4655             :   // Attribute changes on the scrollbars happen in one of three ways:
    4656             :   // 1) The scrollbar changed the attribute in response to some user event
    4657             :   // 2) We changed the attribute in response to a ScrollPositionDidChange
    4658             :   // callback from the scrolling view
    4659             :   // 3) We changed the attribute to adjust the scrollbars for the start
    4660             :   // of a smooth scroll operation
    4661             :   //
    4662             :   // In cases 2 and 3 we do not need to scroll because we're just
    4663             :   // updating our scrollbar.
    4664           7 :   if (mFrameIsUpdatingScrollbar)
    4665          11 :     return;
    4666             : 
    4667           3 :   nsRect scrolledRect = GetScrolledRect();
    4668             : 
    4669           3 :   nsPoint current = GetScrollPosition() - scrolledRect.TopLeft();
    4670           3 :   nsPoint dest;
    4671           3 :   nsRect allowedRange;
    4672           3 :   dest.x = GetCoordAttribute(mHScrollbarBox, nsGkAtoms::curpos, current.x,
    4673             :                              &allowedRange.x, &allowedRange.width);
    4674           3 :   dest.y = GetCoordAttribute(mVScrollbarBox, nsGkAtoms::curpos, current.y,
    4675             :                              &allowedRange.y, &allowedRange.height);
    4676           3 :   current += scrolledRect.TopLeft();
    4677           3 :   dest += scrolledRect.TopLeft();
    4678           3 :   allowedRange += scrolledRect.TopLeft();
    4679             : 
    4680             :   // Don't try to scroll if we're already at an acceptable place.
    4681             :   // Don't call Contains here since Contains returns false when the point is
    4682             :   // on the bottom or right edge of the rectangle.
    4683           3 :   if (allowedRange.ClampPoint(current) == current) {
    4684           3 :     return;
    4685             :   }
    4686             : 
    4687           0 :   if (mScrollbarActivity) {
    4688           0 :     RefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
    4689           0 :     scrollbarActivity->ActivityOccurred();
    4690             :   }
    4691             : 
    4692           0 :   bool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
    4693           0 :   if (isSmooth) {
    4694             :     // Make sure an attribute-setting callback occurs even if the view
    4695             :     // didn't actually move yet.  We need to make sure other listeners
    4696             :     // see that the scroll position is not (yet) what they thought it
    4697             :     // was.
    4698           0 :     AutoWeakFrame weakFrame(mOuter);
    4699           0 :     UpdateScrollbarPosition();
    4700           0 :     if (!weakFrame.IsAlive()) {
    4701           0 :       return;
    4702             :     }
    4703             :   }
    4704           0 :   ScrollToWithOrigin(dest,
    4705             :                      isSmooth ? nsIScrollableFrame::SMOOTH : nsIScrollableFrame::INSTANT,
    4706           0 :                      nsGkAtoms::scrollbars, &allowedRange);
    4707             :   // 'this' might be destroyed here
    4708             : }
    4709             : 
    4710             : /* ============= Scroll events ========== */
    4711             : 
    4712           0 : ScrollFrameHelper::ScrollEvent::ScrollEvent(ScrollFrameHelper* aHelper)
    4713           0 :   : mHelper(aHelper)
    4714             : {
    4715           0 :   mDriver = mHelper->mOuter->PresContext()->RefreshDriver();
    4716           0 :   mDriver->AddRefreshObserver(this, FlushType::Layout);
    4717           0 : }
    4718             : 
    4719           0 : ScrollFrameHelper::ScrollEvent::~ScrollEvent()
    4720             : {
    4721           0 :   if (mDriver) {
    4722           0 :     mDriver->RemoveRefreshObserver(this, FlushType::Layout);
    4723           0 :     mDriver = nullptr;
    4724             :   }
    4725           0 : }
    4726             : 
    4727             : void
    4728           0 : ScrollFrameHelper::ScrollEvent::WillRefresh(mozilla::TimeStamp aTime)
    4729             : {
    4730           0 :   mDriver->RemoveRefreshObserver(this, FlushType::Layout);
    4731           0 :   mDriver = nullptr;
    4732           0 :   mHelper->FireScrollEvent();
    4733           0 : }
    4734             : 
    4735             : void
    4736           0 : ScrollFrameHelper::FireScrollEvent()
    4737             : {
    4738           0 :   AutoProfilerTracing tracing("Paint", "FireScrollEvent");
    4739           0 :   MOZ_ASSERT(mScrollEvent);
    4740           0 :   mScrollEvent = nullptr;
    4741             : 
    4742           0 :   ActiveLayerTracker::SetCurrentScrollHandlerFrame(mOuter);
    4743           0 :   WidgetGUIEvent event(true, eScroll, nullptr);
    4744           0 :   nsEventStatus status = nsEventStatus_eIgnore;
    4745           0 :   nsIContent* content = mOuter->GetContent();
    4746           0 :   nsPresContext* prescontext = mOuter->PresContext();
    4747             :   // Fire viewport scroll events at the document (where they
    4748             :   // will bubble to the window)
    4749           0 :   mozilla::layers::ScrollLinkedEffectDetector detector(content->GetComposedDoc());
    4750           0 :   if (mIsRoot) {
    4751           0 :     nsIDocument* doc = content->GetUncomposedDoc();
    4752           0 :     if (doc) {
    4753           0 :       prescontext->SetTelemetryScrollY(GetScrollPosition().y);
    4754           0 :       EventDispatcher::Dispatch(doc, prescontext, &event, nullptr,  &status);
    4755             :     }
    4756             :   } else {
    4757             :     // scroll events fired at elements don't bubble (although scroll events
    4758             :     // fired at documents do, to the window)
    4759           0 :     event.mFlags.mBubbles = false;
    4760           0 :     EventDispatcher::Dispatch(content, prescontext, &event, nullptr, &status);
    4761             :   }
    4762           0 :   ActiveLayerTracker::SetCurrentScrollHandlerFrame(nullptr);
    4763           0 : }
    4764             : 
    4765             : void
    4766           0 : ScrollFrameHelper::PostScrollEvent()
    4767             : {
    4768           0 :   if (mScrollEvent) {
    4769           0 :     return;
    4770             :   }
    4771             : 
    4772             :   // The ScrollEvent constructor registers itself with the refresh driver.
    4773           0 :   mScrollEvent = new ScrollEvent(this);
    4774             : }
    4775             : 
    4776             : NS_IMETHODIMP
    4777          11 : ScrollFrameHelper::AsyncScrollPortEvent::Run()
    4778             : {
    4779          11 :   if (mHelper) {
    4780          11 :     mHelper->mOuter->PresContext()->GetPresShell()->
    4781          11 :       FlushPendingNotifications(FlushType::InterruptibleLayout);
    4782             :   }
    4783          11 :   return mHelper ? mHelper->FireScrollPortEvent() : NS_OK;
    4784             : }
    4785             : 
    4786             : bool
    4787           0 : nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
    4788             : {
    4789           0 :   if (!mHelper.mHScrollbarBox) {
    4790           0 :     return true;
    4791             :   }
    4792             : 
    4793           0 :   return AddRemoveScrollbar(aState, aOnBottom, true, true);
    4794             : }
    4795             : 
    4796             : bool
    4797           0 : nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
    4798             : {
    4799           0 :   if (!mHelper.mVScrollbarBox) {
    4800           0 :     return true;
    4801             :   }
    4802             : 
    4803           0 :   return AddRemoveScrollbar(aState, aOnRight, false, true);
    4804             : }
    4805             : 
    4806             : void
    4807           0 : nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
    4808             : {
    4809             :   // removing a scrollbar should always fit
    4810           0 :   DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnBottom, true, false);
    4811           0 :   NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
    4812           0 : }
    4813             : 
    4814             : void
    4815           0 : nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
    4816             : {
    4817             :   // removing a scrollbar should always fit
    4818           0 :   DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnRight, false, false);
    4819           0 :   NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
    4820           0 : }
    4821             : 
    4822             : bool
    4823           0 : nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState,
    4824             :                                      bool aOnRightOrBottom, bool aHorizontal, bool aAdd)
    4825             : {
    4826           0 :   if (aHorizontal) {
    4827           0 :      if (mHelper.mNeverHasHorizontalScrollbar || !mHelper.mHScrollbarBox)
    4828           0 :        return false;
    4829             : 
    4830           0 :      nsSize hSize = mHelper.mHScrollbarBox->GetXULPrefSize(aState);
    4831           0 :      nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
    4832             : 
    4833           0 :      mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, aAdd);
    4834             : 
    4835             :      bool hasHorizontalScrollbar;
    4836           0 :      bool fit = AddRemoveScrollbar(hasHorizontalScrollbar,
    4837             :                                      mHelper.mScrollPort.y,
    4838             :                                      mHelper.mScrollPort.height,
    4839           0 :                                      hSize.height, aOnRightOrBottom, aAdd);
    4840           0 :      mHelper.mHasHorizontalScrollbar = hasHorizontalScrollbar;    // because mHasHorizontalScrollbar is a bool
    4841           0 :      if (!fit)
    4842           0 :         mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, !aAdd);
    4843             : 
    4844           0 :      return fit;
    4845             :   } else {
    4846           0 :      if (mHelper.mNeverHasVerticalScrollbar || !mHelper.mVScrollbarBox)
    4847           0 :        return false;
    4848             : 
    4849           0 :      nsSize vSize = mHelper.mVScrollbarBox->GetXULPrefSize(aState);
    4850           0 :      nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
    4851             : 
    4852           0 :      mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, aAdd);
    4853             : 
    4854             :      bool hasVerticalScrollbar;
    4855           0 :      bool fit = AddRemoveScrollbar(hasVerticalScrollbar,
    4856             :                                      mHelper.mScrollPort.x,
    4857             :                                      mHelper.mScrollPort.width,
    4858           0 :                                      vSize.width, aOnRightOrBottom, aAdd);
    4859           0 :      mHelper.mHasVerticalScrollbar = hasVerticalScrollbar;    // because mHasVerticalScrollbar is a bool
    4860           0 :      if (!fit)
    4861           0 :         mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, !aAdd);
    4862             : 
    4863           0 :      return fit;
    4864             :   }
    4865             : }
    4866             : 
    4867             : bool
    4868           0 : nsXULScrollFrame::AddRemoveScrollbar(bool& aHasScrollbar, nscoord& aXY,
    4869             :                                      nscoord& aSize, nscoord aSbSize,
    4870             :                                      bool aOnRightOrBottom, bool aAdd)
    4871             : {
    4872           0 :    nscoord size = aSize;
    4873           0 :    nscoord xy = aXY;
    4874             : 
    4875           0 :    if (size != NS_INTRINSICSIZE) {
    4876           0 :      if (aAdd) {
    4877           0 :         size -= aSbSize;
    4878           0 :         if (!aOnRightOrBottom && size >= 0)
    4879           0 :           xy += aSbSize;
    4880             :      } else {
    4881           0 :         size += aSbSize;
    4882           0 :         if (!aOnRightOrBottom)
    4883           0 :           xy -= aSbSize;
    4884             :      }
    4885             :    }
    4886             : 
    4887             :    // not enough room? Yes? Return true.
    4888           0 :    if (size >= 0) {
    4889           0 :        aHasScrollbar = aAdd;
    4890           0 :        aSize = size;
    4891           0 :        aXY = xy;
    4892           0 :        return true;
    4893             :    }
    4894             : 
    4895           0 :    aHasScrollbar = false;
    4896           0 :    return false;
    4897             : }
    4898             : 
    4899             : void
    4900          38 : nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState,
    4901             :                                    const nsPoint& aScrollPosition)
    4902             : {
    4903          38 :   uint32_t oldflags = aState.LayoutFlags();
    4904          76 :   nsRect childRect = nsRect(mHelper.mScrollPort.TopLeft() - aScrollPosition,
    4905         152 :                             mHelper.mScrollPort.Size());
    4906          38 :   int32_t flags = NS_FRAME_NO_MOVE_VIEW;
    4907             : 
    4908          38 :   nsSize minSize = mHelper.mScrolledFrame->GetXULMinSize(aState);
    4909             : 
    4910          38 :   if (minSize.height > childRect.height)
    4911           7 :     childRect.height = minSize.height;
    4912             : 
    4913          38 :   if (minSize.width > childRect.width)
    4914           4 :     childRect.width = minSize.width;
    4915             : 
    4916             :   // TODO: Handle transformed children that inherit perspective
    4917             :   // from this frame. See AdjustForPerspective for how we handle
    4918             :   // this for HTML scroll frames.
    4919             : 
    4920          38 :   aState.SetLayoutFlags(flags);
    4921          38 :   ClampAndSetBounds(aState, childRect, aScrollPosition);
    4922          38 :   mHelper.mScrolledFrame->XULLayout(aState);
    4923             : 
    4924          38 :   childRect = mHelper.mScrolledFrame->GetRect();
    4925             : 
    4926          76 :   if (childRect.width < mHelper.mScrollPort.width ||
    4927          38 :       childRect.height < mHelper.mScrollPort.height)
    4928             :   {
    4929           0 :     childRect.width = std::max(childRect.width, mHelper.mScrollPort.width);
    4930           0 :     childRect.height = std::max(childRect.height, mHelper.mScrollPort.height);
    4931             : 
    4932             :     // remove overflow areas when we update the bounds,
    4933             :     // because we've already accounted for it
    4934             :     // REVIEW: Have we accounted for both?
    4935           0 :     ClampAndSetBounds(aState, childRect, aScrollPosition, true);
    4936             :   }
    4937             : 
    4938          38 :   aState.SetLayoutFlags(oldflags);
    4939             : 
    4940          38 : }
    4941             : 
    4942         163 : void ScrollFrameHelper::PostOverflowEvent()
    4943             : {
    4944         163 :   if (mAsyncScrollPortEvent.IsPending()) {
    4945         157 :     return;
    4946             :   }
    4947             : 
    4948             :   // Keep this in sync with FireScrollPortEvent().
    4949         158 :   nsSize scrollportSize = mScrollPort.Size();
    4950         158 :   nsSize childSize = GetScrolledRect().Size();
    4951             : 
    4952         158 :   bool newVerticalOverflow = childSize.height > scrollportSize.height;
    4953         158 :   bool vertChanged = mVerticalOverflow != newVerticalOverflow;
    4954             : 
    4955         158 :   bool newHorizontalOverflow = childSize.width > scrollportSize.width;
    4956         158 :   bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
    4957             : 
    4958         158 :   if (!vertChanged && !horizChanged) {
    4959         147 :     return;
    4960             :   }
    4961             : 
    4962          11 :   nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext();
    4963          11 :   if (!rpc) {
    4964           0 :     return;
    4965             :   }
    4966             : 
    4967          11 :   mAsyncScrollPortEvent = new AsyncScrollPortEvent(this);
    4968          11 :   rpc->AddWillPaintObserver(mAsyncScrollPortEvent.get());
    4969             : }
    4970             : 
    4971             : nsIFrame*
    4972        2369 : ScrollFrameHelper::GetFrameForDir() const
    4973             : {
    4974        2369 :   nsIFrame *frame = mOuter;
    4975             :   // XXX This is a bit on the slow side.
    4976        2369 :   if (mIsRoot) {
    4977             :     // If we're the root scrollframe, we need the root element's style data.
    4978         282 :     nsPresContext *presContext = mOuter->PresContext();
    4979         282 :     nsIDocument *document = presContext->Document();
    4980         282 :     Element *root = document->GetRootElement();
    4981             : 
    4982             :     // But for HTML and XHTML we want the body element.
    4983         564 :     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
    4984         282 :     if (htmlDoc) {
    4985          48 :       Element *bodyElement = document->GetBodyElement();
    4986          48 :       if (bodyElement) {
    4987          48 :         root = bodyElement; // we can trust the document to hold on to it
    4988             :       }
    4989             :     }
    4990             : 
    4991         282 :     if (root) {
    4992         282 :       nsIFrame *rootsFrame = root->GetPrimaryFrame();
    4993         282 :       if (rootsFrame) {
    4994         282 :         frame = rootsFrame;
    4995             :       }
    4996             :     }
    4997             :   }
    4998             : 
    4999        2369 :   return frame;
    5000             : }
    5001             : 
    5002             : bool
    5003         289 : ScrollFrameHelper::IsScrollbarOnRight() const
    5004             : {
    5005         289 :   nsPresContext *presContext = mOuter->PresContext();
    5006             : 
    5007             :   // The position of the scrollbar in top-level windows depends on the pref
    5008             :   // layout.scrollbar.side. For non-top-level elements, it depends only on the
    5009             :   // directionaliy of the element (equivalent to a value of "1" for the pref).
    5010         289 :   if (!mIsRoot) {
    5011         202 :     return IsPhysicalLTR();
    5012             :   }
    5013          87 :   switch (presContext->GetCachedIntPref(kPresContext_ScrollbarSide)) {
    5014             :     default:
    5015             :     case 0: // UI directionality
    5016          87 :       return presContext->GetCachedIntPref(kPresContext_BidiDirection)
    5017          87 :              == IBMBIDI_TEXTDIRECTION_LTR;
    5018             :     case 1: // Document / content directionality
    5019           0 :       return IsPhysicalLTR();
    5020             :     case 2: // Always right
    5021           0 :       return true;
    5022             :     case 3: // Always left
    5023           0 :       return false;
    5024             :   }
    5025             : }
    5026             : 
    5027             : bool
    5028         464 : ScrollFrameHelper::IsMaybeScrollingActive() const
    5029             : {
    5030         464 :   const nsStyleDisplay* disp = mOuter->StyleDisplay();
    5031         464 :   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
    5032           0 :     return true;
    5033             :   }
    5034             : 
    5035         928 :   return mHasBeenScrolledRecently ||
    5036         922 :          IsAlwaysActive() ||
    5037         922 :          mWillBuildScrollableLayer;
    5038             : }
    5039             : 
    5040             : bool
    5041         233 : ScrollFrameHelper::IsScrollingActive(nsDisplayListBuilder* aBuilder) const
    5042             : {
    5043         233 :   const nsStyleDisplay* disp = mOuter->StyleDisplay();
    5044         699 :   if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL) &&
    5045         233 :     aBuilder->IsInWillChangeBudget(mOuter, GetScrollPositionClampingScrollPortSize())) {
    5046           0 :     return true;
    5047             :   }
    5048             : 
    5049         466 :   return mHasBeenScrolledRecently ||
    5050         462 :          IsAlwaysActive() ||
    5051         462 :          mWillBuildScrollableLayer;
    5052             : }
    5053             : 
    5054             : /**
    5055             :  * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
    5056             :  * cause any of the scrollbars to need to be reflowed.
    5057             :  */
    5058             : nsresult
    5059          38 : nsXULScrollFrame::XULLayout(nsBoxLayoutState& aState)
    5060             : {
    5061          38 :   bool scrollbarRight = IsScrollbarOnRight();
    5062          38 :   bool scrollbarBottom = true;
    5063             : 
    5064             :   // get the content rect
    5065          76 :   nsRect clientRect(0,0,0,0);
    5066          38 :   GetXULClientRect(clientRect);
    5067             : 
    5068          76 :   nsRect oldScrollAreaBounds = mHelper.mScrollPort;
    5069          38 :   nsPoint oldScrollPosition = mHelper.GetLogicalScrollPosition();
    5070             : 
    5071             :   // the scroll area size starts off as big as our content area
    5072          38 :   mHelper.mScrollPort = clientRect;
    5073             : 
    5074             :   /**************
    5075             :    Our basic strategy here is to first try laying out the content with
    5076             :    the scrollbars in their current state. We're hoping that that will
    5077             :    just "work"; the content will overflow wherever there's a scrollbar
    5078             :    already visible. If that does work, then there's no need to lay out
    5079             :    the scrollarea. Otherwise we fix up the scrollbars; first we add a
    5080             :    vertical one to scroll the content if necessary, or remove it if
    5081             :    it's not needed. Then we reflow the content if the scrollbar
    5082             :    changed.  Then we add a horizontal scrollbar if necessary (or
    5083             :    remove if not needed), and if that changed, we reflow the content
    5084             :    again. At this point, any scrollbars that are needed to scroll the
    5085             :    content have been added.
    5086             : 
    5087             :    In the second phase we check to see if any scrollbars are too small
    5088             :    to display, and if so, we remove them. We check the horizontal
    5089             :    scrollbar first; removing it might make room for the vertical
    5090             :    scrollbar, and if we have room for just one scrollbar we'll save
    5091             :    the vertical one.
    5092             : 
    5093             :    Finally we position and size the scrollbars and scrollcorner (the
    5094             :    square that is needed in the corner of the window when two
    5095             :    scrollbars are visible), and reflow any fixed position views
    5096             :    (if we're the viewport and we added or removed a scrollbar).
    5097             :    **************/
    5098             : 
    5099          76 :   ScrollbarStyles styles = GetScrollbarStyles();
    5100             : 
    5101             :   // Look at our style do we always have vertical or horizontal scrollbars?
    5102          38 :   if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
    5103           0 :     mHelper.mHasHorizontalScrollbar = true;
    5104          38 :   if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
    5105           0 :     mHelper.mHasVerticalScrollbar = true;
    5106             : 
    5107          38 :   if (mHelper.mHasHorizontalScrollbar)
    5108           0 :     AddHorizontalScrollbar(aState, scrollbarBottom);
    5109             : 
    5110          38 :   if (mHelper.mHasVerticalScrollbar)
    5111           0 :     AddVerticalScrollbar(aState, scrollbarRight);
    5112             : 
    5113             :   // layout our the scroll area
    5114          38 :   LayoutScrollArea(aState, oldScrollPosition);
    5115             : 
    5116             :   // now look at the content area and see if we need scrollbars or not
    5117          38 :   bool needsLayout = false;
    5118             : 
    5119             :   // if we have 'auto' scrollbars look at the vertical case
    5120          38 :   if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
    5121             :     // These are only good until the call to LayoutScrollArea.
    5122          76 :     nsRect scrolledRect = mHelper.GetScrolledRect();
    5123             : 
    5124             :     // There are two cases to consider
    5125          45 :     if (scrolledRect.height <= mHelper.mScrollPort.height ||
    5126           7 :         styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
    5127          76 :       if (mHelper.mHasVerticalScrollbar) {
    5128             :         // We left room for the vertical scrollbar, but it's not needed;
    5129             :         // remove it.
    5130           0 :         RemoveVerticalScrollbar(aState, scrollbarRight);
    5131           0 :         needsLayout = true;
    5132             :       }
    5133             :     } else {
    5134           0 :       if (!mHelper.mHasVerticalScrollbar) {
    5135             :         // We didn't leave room for the vertical scrollbar, but it turns
    5136             :         // out we needed it
    5137           0 :         if (AddVerticalScrollbar(aState, scrollbarRight)) {
    5138           0 :           needsLayout = true;
    5139             :         }
    5140             :       }
    5141             :     }
    5142             : 
    5143             :     // ok layout at the right size
    5144          38 :     if (needsLayout) {
    5145           0 :       nsBoxLayoutState resizeState(aState);
    5146           0 :       LayoutScrollArea(resizeState, oldScrollPosition);
    5147           0 :       needsLayout = false;
    5148             :     }
    5149             :   }
    5150             : 
    5151             : 
    5152             :   // if scrollbars are auto look at the horizontal case
    5153          38 :   if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
    5154             :   {
    5155             :     // These are only good until the call to LayoutScrollArea.
    5156          76 :     nsRect scrolledRect = mHelper.GetScrolledRect();
    5157             : 
    5158             :     // if the child is wider that the scroll area
    5159             :     // and we don't have a scrollbar add one.
    5160          38 :     if ((scrolledRect.width > mHelper.mScrollPort.width)
    5161           8 :         && styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
    5162             : 
    5163           0 :       if (!mHelper.mHasHorizontalScrollbar) {
    5164             :         // no scrollbar?
    5165           0 :         if (AddHorizontalScrollbar(aState, scrollbarBottom)) {
    5166             : 
    5167             :           // if we added a horizontal scrollbar and we did not have a vertical
    5168             :           // there is a chance that by adding the horizontal scrollbar we will
    5169             :           // suddenly need a vertical scrollbar. Is a special case but it's
    5170             :           // important.
    5171             :           //
    5172             :           // But before we do that we need to relayout, since it's
    5173             :           // possible that the contents will flex as a result of adding a
    5174             :           // horizontal scrollbar and avoid the need for a vertical
    5175             :           // scrollbar.
    5176             :           //
    5177             :           // So instead of setting needsLayout to true here, do the
    5178             :           // layout immediately, and then consider whether to add the
    5179             :           // vertical scrollbar (and then maybe layout again).
    5180             :           {
    5181           0 :             nsBoxLayoutState resizeState(aState);
    5182           0 :             LayoutScrollArea(resizeState, oldScrollPosition);
    5183           0 :             needsLayout = false;
    5184             :           }
    5185             : 
    5186             :           // Refresh scrolledRect because we called LayoutScrollArea.
    5187           0 :           scrolledRect = mHelper.GetScrolledRect();
    5188             : 
    5189           0 :           if (styles.mVertical == NS_STYLE_OVERFLOW_AUTO &&
    5190           0 :               !mHelper.mHasVerticalScrollbar &&
    5191           0 :               scrolledRect.height > mHelper.mScrollPort.height) {
    5192           0 :             if (AddVerticalScrollbar(aState, scrollbarRight)) {
    5193           0 :               needsLayout = true;
    5194             :             }
    5195             :           }
    5196             :         }
    5197             : 
    5198           0 :       }
    5199             :     } else {
    5200             :       // if the area is smaller or equal to and we have a scrollbar then
    5201             :       // remove it.
    5202          38 :       if (mHelper.mHasHorizontalScrollbar) {
    5203           0 :         RemoveHorizontalScrollbar(aState, scrollbarBottom);
    5204           0 :         needsLayout = true;
    5205             :       }
    5206             :     }
    5207             :   }
    5208             : 
    5209             :   // we only need to set the rect. The inner child stays the same size.
    5210          38 :   if (needsLayout) {
    5211           0 :     nsBoxLayoutState resizeState(aState);
    5212           0 :     LayoutScrollArea(resizeState, oldScrollPosition);
    5213           0 :     needsLayout = false;
    5214             :   }
    5215             : 
    5216             :   // get the preferred size of the scrollbars
    5217          38 :   nsSize hMinSize(0, 0);
    5218          38 :   if (mHelper.mHScrollbarBox && mHelper.mHasHorizontalScrollbar) {
    5219           0 :     GetScrollbarMetrics(aState, mHelper.mHScrollbarBox, &hMinSize, nullptr, false);
    5220             :   }
    5221          38 :   nsSize vMinSize(0, 0);
    5222          38 :   if (mHelper.mVScrollbarBox && mHelper.mHasVerticalScrollbar) {
    5223           0 :     GetScrollbarMetrics(aState, mHelper.mVScrollbarBox, &vMinSize, nullptr, true);
    5224             :   }
    5225             : 
    5226             :   // Disable scrollbars that are too small
    5227             :   // Disable horizontal scrollbar first. If we have to disable only one
    5228             :   // scrollbar, we'd rather keep the vertical scrollbar.
    5229             :   // Note that we always give horizontal scrollbars their preferred height,
    5230             :   // never their min-height. So check that there's room for the preferred height.
    5231          38 :   if (mHelper.mHasHorizontalScrollbar &&
    5232           0 :       (hMinSize.width > clientRect.width - vMinSize.width
    5233           0 :        || hMinSize.height > clientRect.height)) {
    5234           0 :     RemoveHorizontalScrollbar(aState, scrollbarBottom);
    5235           0 :     needsLayout = true;
    5236             :   }
    5237             :   // Now disable vertical scrollbar if necessary
    5238          38 :   if (mHelper.mHasVerticalScrollbar &&
    5239           0 :       (vMinSize.height > clientRect.height - hMinSize.height
    5240           0 :        || vMinSize.width > clientRect.width)) {
    5241           0 :     RemoveVerticalScrollbar(aState, scrollbarRight);
    5242           0 :     needsLayout = true;
    5243             :   }
    5244             : 
    5245             :   // we only need to set the rect. The inner child stays the same size.
    5246          38 :   if (needsLayout) {
    5247           0 :     nsBoxLayoutState resizeState(aState);
    5248           0 :     LayoutScrollArea(resizeState, oldScrollPosition);
    5249             :   }
    5250             : 
    5251          38 :   if (!mHelper.mSuppressScrollbarUpdate) {
    5252          38 :     mHelper.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds);
    5253             :   }
    5254          38 :   if (!mHelper.mPostedReflowCallback) {
    5255             :     // Make sure we'll try scrolling to restored position
    5256           0 :     PresContext()->PresShell()->PostReflowCallback(&mHelper);
    5257           0 :     mHelper.mPostedReflowCallback = true;
    5258             :   }
    5259          38 :   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
    5260          30 :     mHelper.mHadNonInitialReflow = true;
    5261             :   }
    5262             : 
    5263          38 :   mHelper.UpdateSticky();
    5264             : 
    5265             :   // Set up overflow areas for block frames for the benefit of
    5266             :   // text-overflow.
    5267          38 :   nsIFrame* f = mHelper.mScrolledFrame->GetContentInsertionFrame();
    5268          38 :   if (nsLayoutUtils::GetAsBlock(f)) {
    5269           0 :     nsRect origRect = f->GetRect();
    5270           0 :     nsRect clippedRect = origRect;
    5271           0 :     clippedRect.MoveBy(mHelper.mScrollPort.TopLeft());
    5272           0 :     clippedRect.IntersectRect(clippedRect, mHelper.mScrollPort);
    5273           0 :     nsOverflowAreas overflow = f->GetOverflowAreas();
    5274           0 :     f->FinishAndStoreOverflow(overflow, clippedRect.Size());
    5275           0 :     clippedRect.MoveTo(origRect.TopLeft());
    5276           0 :     f->SetRect(clippedRect);
    5277             :   }
    5278             : 
    5279          38 :   mHelper.UpdatePrevScrolledRect();
    5280             : 
    5281          38 :   mHelper.PostOverflowEvent();
    5282          76 :   return NS_OK;
    5283             : }
    5284             : 
    5285             : void
    5286           6 : ScrollFrameHelper::FinishReflowForScrollbar(nsIContent* aContent,
    5287             :                                                 nscoord aMinXY, nscoord aMaxXY,
    5288             :                                                 nscoord aCurPosXY,
    5289             :                                                 nscoord aPageIncrement,
    5290             :                                                 nscoord aIncrement)
    5291             : {
    5292             :   // Scrollbars assume zero is the minimum position, so translate for them.
    5293           6 :   SetCoordAttribute(aContent, nsGkAtoms::curpos, aCurPosXY - aMinXY);
    5294           6 :   SetScrollbarEnabled(aContent, aMaxXY - aMinXY);
    5295           6 :   SetCoordAttribute(aContent, nsGkAtoms::maxpos, aMaxXY - aMinXY);
    5296           6 :   SetCoordAttribute(aContent, nsGkAtoms::pageincrement, aPageIncrement);
    5297           6 :   SetCoordAttribute(aContent, nsGkAtoms::increment, aIncrement);
    5298           6 : }
    5299             : 
    5300             : bool
    5301         155 : ScrollFrameHelper::ReflowFinished()
    5302             : {
    5303         155 :   mPostedReflowCallback = false;
    5304             : 
    5305         155 :   if (NS_SUBTREE_DIRTY(mOuter)) {
    5306             :     // We will get another call after the next reflow and scrolling
    5307             :     // later is less janky.
    5308           1 :     return false;
    5309             :   }
    5310             : 
    5311         308 :   nsAutoScriptBlocker scriptBlocker;
    5312         154 :   ScrollToRestoredPosition();
    5313             : 
    5314             :   // Clamp current scroll position to new bounds. Normally this won't
    5315             :   // do anything.
    5316         154 :   nsPoint currentScrollPos = GetScrollPosition();
    5317         154 :   ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
    5318         154 :   if (!mAsyncScroll && !mAsyncSmoothMSDScroll && !mApzSmoothScrollDestination) {
    5319             :     // We need to have mDestination track the current scroll position,
    5320             :     // in case it falls outside the new reflow area. mDestination is used
    5321             :     // by ScrollBy as its starting position.
    5322         154 :     mDestination = GetScrollPosition();
    5323             :   }
    5324             : 
    5325         154 :   if (!mUpdateScrollbarAttributes) {
    5326          39 :     return false;
    5327             :   }
    5328         115 :   mUpdateScrollbarAttributes = false;
    5329             : 
    5330             :   // Update scrollbar attributes.
    5331         115 :   nsPresContext* presContext = mOuter->PresContext();
    5332             : 
    5333         115 :   if (mMayHaveDirtyFixedChildren) {
    5334          18 :     mMayHaveDirtyFixedChildren = false;
    5335          18 :     nsIFrame* parentFrame = mOuter->GetParent();
    5336           0 :     for (nsIFrame* fixedChild =
    5337          18 :            parentFrame->GetChildList(nsIFrame::kFixedList).FirstChild();
    5338          18 :          fixedChild; fixedChild = fixedChild->GetNextSibling()) {
    5339             :       // force a reflow of the fixed child
    5340           0 :       presContext->PresShell()->
    5341             :         FrameNeedsReflow(fixedChild, nsIPresShell::eResize,
    5342           0 :                          NS_FRAME_HAS_DIRTY_CHILDREN);
    5343             :     }
    5344             :   }
    5345             : 
    5346         230 :   nsRect scrolledContentRect = GetScrolledRect();
    5347         115 :   nsSize scrollClampingScrollPort = GetScrollPositionClampingScrollPortSize();
    5348         115 :   nscoord minX = scrolledContentRect.x;
    5349         115 :   nscoord maxX = scrolledContentRect.XMost() - scrollClampingScrollPort.width;
    5350         115 :   nscoord minY = scrolledContentRect.y;
    5351         115 :   nscoord maxY = scrolledContentRect.YMost() - scrollClampingScrollPort.height;
    5352             : 
    5353             :   // Suppress handling of the curpos attribute changes we make here.
    5354         115 :   NS_ASSERTION(!mFrameIsUpdatingScrollbar, "We shouldn't be reentering here");
    5355         115 :   mFrameIsUpdatingScrollbar = true;
    5356             : 
    5357             :   nsCOMPtr<nsIContent> vScroll =
    5358         230 :     mVScrollbarBox ? mVScrollbarBox->GetContent() : nullptr;
    5359             :   nsCOMPtr<nsIContent> hScroll =
    5360         230 :     mHScrollbarBox ? mHScrollbarBox->GetContent() : nullptr;
    5361             : 
    5362             :   // Note, in some cases mOuter may get deleted while finishing reflow
    5363             :   // for scrollbars. XXXmats is this still true now that we have a script
    5364             :   // blocker in this scope? (if not, remove the weak frame checks below).
    5365         115 :   if (vScroll || hScroll) {
    5366           6 :     AutoWeakFrame weakFrame(mOuter);
    5367           3 :     nsPoint scrollPos = GetScrollPosition();
    5368           3 :     nsSize lineScrollAmount = GetLineScrollAmount();
    5369           3 :     if (vScroll) {
    5370             :       const double kScrollMultiplier =
    5371           3 :         Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
    5372           3 :                             NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
    5373           3 :       nscoord increment = lineScrollAmount.height * kScrollMultiplier;
    5374             :       // We normally use (scrollArea.height - increment) for height
    5375             :       // of page scrolling.  However, it is too small when
    5376             :       // increment is very large. (If increment is larger than
    5377             :       // scrollArea.height, direction of scrolling will be opposite).
    5378             :       // To avoid it, we use (float(scrollArea.height) * 0.8) as
    5379             :       // lower bound value of height of page scrolling. (bug 383267)
    5380             :       // XXX shouldn't we use GetPageScrollAmount here?
    5381           3 :       nscoord pageincrement = nscoord(scrollClampingScrollPort.height - increment);
    5382           3 :       nscoord pageincrementMin = nscoord(float(scrollClampingScrollPort.height) * 0.8);
    5383           3 :       FinishReflowForScrollbar(vScroll, minY, maxY, scrollPos.y,
    5384           3 :                                std::max(pageincrement, pageincrementMin),
    5385           3 :                                increment);
    5386             :     }
    5387           3 :     if (hScroll) {
    5388             :       const double kScrollMultiplier =
    5389           3 :         Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
    5390           3 :                             NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
    5391           3 :       nscoord increment = lineScrollAmount.width * kScrollMultiplier;
    5392           3 :       FinishReflowForScrollbar(hScroll, minX, maxX, scrollPos.x,
    5393           3 :                                nscoord(float(scrollClampingScrollPort.width) * 0.8),
    5394           3 :                                increment);
    5395             :     }
    5396           3 :     NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
    5397             :   }
    5398             : 
    5399         115 :   mFrameIsUpdatingScrollbar = false;
    5400             :   // We used to rely on the curpos attribute changes above to scroll the
    5401             :   // view.  However, for scrolling to the left of the viewport, we
    5402             :   // rescale the curpos attribute, which means that operations like
    5403             :   // resizing the window while it is scrolled all the way to the left
    5404             :   // hold the curpos attribute constant at 0 while still requiring
    5405             :   // scrolling.  So we suppress the effect of the changes above with
    5406             :   // mFrameIsUpdatingScrollbar and call CurPosAttributeChanged here.
    5407             :   // (It actually even works some of the time without this, thanks to
    5408             :   // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
    5409             :   // we hide the scrollbar on a large size change, such as
    5410             :   // maximization.)
    5411         115 :   if (!mHScrollbarBox && !mVScrollbarBox)
    5412         112 :     return false;
    5413           3 :   CurPosAttributeChanged(mVScrollbarBox ? mVScrollbarBox->GetContent()
    5414           3 :                                         : mHScrollbarBox->GetContent());
    5415           3 :   return true;
    5416             : }
    5417             : 
    5418             : void
    5419           0 : ScrollFrameHelper::ReflowCallbackCanceled()
    5420             : {
    5421           0 :   mPostedReflowCallback = false;
    5422           0 : }
    5423             : 
    5424             : bool
    5425           0 : ScrollFrameHelper::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
    5426             : {
    5427           0 :   nsIScrollableFrame* sf = do_QueryFrame(mOuter);
    5428           0 :   ScrollbarStyles ss = sf->GetScrollbarStyles();
    5429             : 
    5430             :   // Reflow when the change in overflow leads to one of our scrollbars
    5431             :   // changing or might require repositioning the scrolled content due to
    5432             :   // reduced extents.
    5433           0 :   nsRect scrolledRect = GetScrolledRect();
    5434           0 :   uint32_t overflowChange = GetOverflowChange(scrolledRect, mPrevScrolledRect);
    5435           0 :   mPrevScrolledRect = scrolledRect;
    5436             : 
    5437           0 :   bool needReflow = false;
    5438           0 :   nsPoint scrollPosition = GetScrollPosition();
    5439           0 :   if (overflowChange & nsIScrollableFrame::HORIZONTAL) {
    5440           0 :     if (ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.x) {
    5441           0 :       needReflow = true;
    5442             :     }
    5443             :   }
    5444           0 :   if (overflowChange & nsIScrollableFrame::VERTICAL) {
    5445           0 :     if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN || scrollPosition.y) {
    5446           0 :       needReflow = true;
    5447             :     }
    5448             :   }
    5449             : 
    5450           0 :   if (needReflow) {
    5451             :     // If there are scrollbars, or we're not at the beginning of the pane,
    5452             :     // the scroll position may change. In this case, mark the frame as
    5453             :     // needing reflow. Don't use NS_FRAME_IS_DIRTY as dirty as that means
    5454             :     // we have to reflow the frame and all its descendants, and we don't
    5455             :     // have to do that here. Only this frame needs to be reflowed.
    5456           0 :     mOuter->PresContext()->PresShell()->FrameNeedsReflow(
    5457           0 :       mOuter, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
    5458             :     // Ensure that next time nsHTMLScrollFrame::Reflow runs, we don't skip
    5459             :     // updating the scrollbars. (Because the overflow area of the scrolled
    5460             :     // frame has probably just been updated, Reflow won't see it change.)
    5461           0 :     mSkippedScrollbarLayout = true;
    5462           0 :     return false;  // reflowing will update overflow
    5463             :   }
    5464           0 :   PostOverflowEvent();
    5465           0 :   return mOuter->nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
    5466             : }
    5467             : 
    5468             : void
    5469         163 : ScrollFrameHelper::UpdateSticky()
    5470             : {
    5471             :   StickyScrollContainer* ssc = StickyScrollContainer::
    5472         163 :     GetStickyScrollContainerForScrollFrame(mOuter);
    5473         163 :   if (ssc) {
    5474           0 :     nsIScrollableFrame* scrollFrame = do_QueryFrame(mOuter);
    5475           0 :     ssc->UpdatePositions(scrollFrame->GetScrollPosition(), mOuter);
    5476             :   }
    5477         163 : }
    5478             : 
    5479             : void
    5480         163 : ScrollFrameHelper::UpdatePrevScrolledRect()
    5481             : {
    5482         163 :   mPrevScrolledRect = GetScrolledRect();
    5483         163 : }
    5484             : 
    5485             : void
    5486           6 : ScrollFrameHelper::AdjustScrollbarRectForResizer(
    5487             :                          nsIFrame* aFrame, nsPresContext* aPresContext,
    5488             :                          nsRect& aRect, bool aHasResizer, bool aVertical)
    5489             : {
    5490           6 :   if ((aVertical ? aRect.width : aRect.height) == 0) {
    5491          12 :     return;
    5492             :   }
    5493             : 
    5494             :   // if a content resizer is present, use its size. Otherwise, check if the
    5495             :   // widget has a resizer.
    5496           0 :   nsRect resizerRect;
    5497           0 :   if (aHasResizer) {
    5498           0 :     resizerRect = mResizerBox->GetRect();
    5499             :   }
    5500             :   else {
    5501           0 :     nsPoint offset;
    5502           0 :     nsIWidget* widget = aFrame->GetNearestWidget(offset);
    5503           0 :     LayoutDeviceIntRect widgetRect;
    5504           0 :     if (!widget || !widget->ShowsResizeIndicator(&widgetRect)) {
    5505           0 :       return;
    5506             :     }
    5507             : 
    5508           0 :     resizerRect = nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
    5509           0 :                          aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
    5510             :                          aPresContext->DevPixelsToAppUnits(widgetRect.width),
    5511             :                          aPresContext->DevPixelsToAppUnits(widgetRect.height));
    5512             :   }
    5513             : 
    5514           0 :   if (resizerRect.Contains(aRect.BottomRight() - nsPoint(1, 1))) {
    5515           0 :     if (aVertical) {
    5516           0 :       aRect.height = std::max(0, resizerRect.y - aRect.y);
    5517             :     } else {
    5518           0 :       aRect.width = std::max(0, resizerRect.x - aRect.x);
    5519             :     }
    5520           0 :   } else if (resizerRect.Contains(aRect.BottomLeft() + nsPoint(1, -1))) {
    5521           0 :     if (aVertical) {
    5522           0 :       aRect.height = std::max(0, resizerRect.y - aRect.y);
    5523             :     } else {
    5524           0 :       nscoord xmost = aRect.XMost();
    5525           0 :       aRect.x = std::max(aRect.x, resizerRect.XMost());
    5526           0 :       aRect.width = xmost - aRect.x;
    5527             :     }
    5528             :   }
    5529             : }
    5530             : 
    5531             : static void
    5532         124 : AdjustOverlappingScrollbars(nsRect& aVRect, nsRect& aHRect)
    5533             : {
    5534         124 :   if (aVRect.IsEmpty() || aHRect.IsEmpty())
    5535         124 :     return;
    5536             : 
    5537           0 :   const nsRect oldVRect = aVRect;
    5538           0 :   const nsRect oldHRect = aHRect;
    5539           0 :   if (oldVRect.Contains(oldHRect.BottomRight() - nsPoint(1, 1))) {
    5540           0 :     aHRect.width = std::max(0, oldVRect.x - oldHRect.x);
    5541           0 :   } else if (oldVRect.Contains(oldHRect.BottomLeft() - nsPoint(0, 1))) {
    5542           0 :     nscoord overlap = std::min(oldHRect.width, oldVRect.XMost() - oldHRect.x);
    5543           0 :     aHRect.x += overlap;
    5544           0 :     aHRect.width -= overlap;
    5545             :   }
    5546           0 :   if (oldHRect.Contains(oldVRect.BottomRight() - nsPoint(1, 1))) {
    5547           0 :     aVRect.height = std::max(0, oldHRect.y - oldVRect.y);
    5548             :   }
    5549             : }
    5550             : 
    5551             : void
    5552         124 : ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState,
    5553             :                                         const nsRect& aContentArea,
    5554             :                                         const nsRect& aOldScrollArea)
    5555             : {
    5556         124 :   NS_ASSERTION(!mSuppressScrollbarUpdate,
    5557             :                "This should have been suppressed");
    5558             : 
    5559         124 :   nsIPresShell* presShell = mOuter->PresContext()->PresShell();
    5560             : 
    5561         124 :   bool hasResizer = HasResizer();
    5562         124 :   bool scrollbarOnLeft = !IsScrollbarOnRight();
    5563             :   bool overlayScrollBarsWithZoom =
    5564         124 :     mIsRoot && LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) &&
    5565         124 :     presShell->IsScrollPositionClampingScrollPortSizeSet();
    5566             : 
    5567         124 :   nsSize scrollPortClampingSize = mScrollPort.Size();
    5568         124 :   double res = 1.0;
    5569         124 :   if (overlayScrollBarsWithZoom) {
    5570           0 :     scrollPortClampingSize = presShell->GetScrollPositionClampingScrollPortSize();
    5571           0 :     res = presShell->GetCumulativeResolution();
    5572             :   }
    5573             : 
    5574             :   // place the scrollcorner
    5575         124 :   if (mScrollCornerBox || mResizerBox) {
    5576           3 :     NS_PRECONDITION(!mScrollCornerBox || mScrollCornerBox->IsXULBoxFrame(), "Must be a box frame!");
    5577             : 
    5578           6 :     nsRect r(0, 0, 0, 0);
    5579           3 :     if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) {
    5580             :       // scrollbar (if any) on left
    5581           0 :       r.x = aContentArea.x;
    5582           0 :       r.width = mScrollPort.x - aContentArea.x;
    5583           0 :       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
    5584             :     } else {
    5585             :       // scrollbar (if any) on right
    5586           3 :       r.width = aContentArea.XMost() - mScrollPort.XMost();
    5587           3 :       r.x = aContentArea.XMost() - r.width;
    5588           3 :       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
    5589             :     }
    5590           3 :     if (aContentArea.y != mScrollPort.y) {
    5591           0 :       NS_ERROR("top scrollbars not supported");
    5592             :     } else {
    5593             :       // scrollbar (if any) on bottom
    5594           3 :       r.height = aContentArea.YMost() - mScrollPort.YMost();
    5595           3 :       r.y = aContentArea.YMost() - r.height;
    5596           3 :       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
    5597             :     }
    5598             : 
    5599           3 :     if (mScrollCornerBox) {
    5600           3 :       nsBoxFrame::LayoutChildAt(aState, mScrollCornerBox, r);
    5601             :     }
    5602             : 
    5603           3 :     if (hasResizer) {
    5604             :       // if a resizer is present, get its size. Assume a default size of 15 pixels.
    5605           0 :       nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
    5606           0 :       nsSize resizerMinSize = mResizerBox->GetXULMinSize(aState);
    5607             : 
    5608           0 :       nscoord vScrollbarWidth = mVScrollbarBox ?
    5609           0 :         mVScrollbarBox->GetXULPrefSize(aState).width : defaultSize;
    5610           0 :       r.width = std::max(std::max(r.width, vScrollbarWidth), resizerMinSize.width);
    5611           0 :       if (aContentArea.x == mScrollPort.x && !scrollbarOnLeft) {
    5612           0 :         r.x = aContentArea.XMost() - r.width;
    5613             :       }
    5614             : 
    5615           0 :       nscoord hScrollbarHeight = mHScrollbarBox ?
    5616           0 :         mHScrollbarBox->GetXULPrefSize(aState).height : defaultSize;
    5617           0 :       r.height = std::max(std::max(r.height, hScrollbarHeight), resizerMinSize.height);
    5618           0 :       if (aContentArea.y == mScrollPort.y) {
    5619           0 :         r.y = aContentArea.YMost() - r.height;
    5620             :       }
    5621             : 
    5622           0 :       nsBoxFrame::LayoutChildAt(aState, mResizerBox, r);
    5623           3 :     } else if (mResizerBox) {
    5624             :       // otherwise lay out the resizer with an empty rectangle
    5625           0 :       nsBoxFrame::LayoutChildAt(aState, mResizerBox, nsRect());
    5626             :     }
    5627             :   }
    5628             : 
    5629         124 :   nsPresContext* presContext = mScrolledFrame->PresContext();
    5630         248 :   nsRect vRect;
    5631         124 :   if (mVScrollbarBox) {
    5632           3 :     NS_PRECONDITION(mVScrollbarBox->IsXULBoxFrame(), "Must be a box frame!");
    5633           3 :     vRect = mScrollPort;
    5634           3 :     if (overlayScrollBarsWithZoom) {
    5635           0 :       vRect.height = NSToCoordRound(res * scrollPortClampingSize.height);
    5636             :     }
    5637           3 :     vRect.width = aContentArea.width - mScrollPort.width;
    5638           3 :     vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.x + NSToCoordRound(res * scrollPortClampingSize.width);
    5639           3 :     if (mHasVerticalScrollbar) {
    5640           0 :       nsMargin margin;
    5641           0 :       mVScrollbarBox->GetXULMargin(margin);
    5642           0 :       vRect.Deflate(margin);
    5643             :     }
    5644           3 :     AdjustScrollbarRectForResizer(mOuter, presContext, vRect, hasResizer, true);
    5645             :   }
    5646             : 
    5647         248 :   nsRect hRect;
    5648         124 :   if (mHScrollbarBox) {
    5649           3 :     NS_PRECONDITION(mHScrollbarBox->IsXULBoxFrame(), "Must be a box frame!");
    5650           3 :     hRect = mScrollPort;
    5651           3 :     if (overlayScrollBarsWithZoom) {
    5652           0 :       hRect.width = NSToCoordRound(res * scrollPortClampingSize.width);
    5653             :     }
    5654           3 :     hRect.height = aContentArea.height - mScrollPort.height;
    5655           3 :     hRect.y = mScrollPort.y + NSToCoordRound(res * scrollPortClampingSize.height);
    5656           3 :     if (mHasHorizontalScrollbar) {
    5657           0 :       nsMargin margin;
    5658           0 :       mHScrollbarBox->GetXULMargin(margin);
    5659           0 :       hRect.Deflate(margin);
    5660             :     }
    5661           3 :     AdjustScrollbarRectForResizer(mOuter, presContext, hRect, hasResizer, false);
    5662             :   }
    5663             : 
    5664         124 :   if (!LookAndFeel::GetInt(LookAndFeel::eIntID_AllowOverlayScrollbarsOverlap)) {
    5665         124 :     AdjustOverlappingScrollbars(vRect, hRect);
    5666             :   }
    5667         124 :   if (mVScrollbarBox) {
    5668           3 :     nsBoxFrame::LayoutChildAt(aState, mVScrollbarBox, vRect);
    5669             :   }
    5670         124 :   if (mHScrollbarBox) {
    5671           3 :     nsBoxFrame::LayoutChildAt(aState, mHScrollbarBox, hRect);
    5672             :   }
    5673             : 
    5674             :   // may need to update fixed position children of the viewport,
    5675             :   // if the client area changed size because of an incremental
    5676             :   // reflow of a descendant.  (If the outer frame is dirty, the fixed
    5677             :   // children will be re-laid out anyway)
    5678         571 :   if (aOldScrollArea.Size() != mScrollPort.Size() &&
    5679         542 :       !(mOuter->GetStateBits() & NS_FRAME_IS_DIRTY) &&
    5680          46 :       mIsRoot) {
    5681          18 :     mMayHaveDirtyFixedChildren = true;
    5682             :   }
    5683             : 
    5684             :   // post reflow callback to modify scrollbar attributes
    5685         124 :   mUpdateScrollbarAttributes = true;
    5686         124 :   if (!mPostedReflowCallback) {
    5687          38 :     aState.PresContext()->PresShell()->PostReflowCallback(this);
    5688          38 :     mPostedReflowCallback = true;
    5689             :   }
    5690         124 : }
    5691             : 
    5692             : #if DEBUG
    5693          22 : static bool ShellIsAlive(nsWeakPtr& aWeakPtr)
    5694             : {
    5695          44 :   nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aWeakPtr));
    5696          44 :   return !!shell;
    5697             : }
    5698             : #endif
    5699             : 
    5700             : void
    5701           6 : ScrollFrameHelper::SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos)
    5702             : {
    5703             :   DebugOnly<nsWeakPtr> weakShell(
    5704          12 :     do_GetWeakReference(mOuter->PresContext()->PresShell()));
    5705           6 :   if (aMaxPos) {
    5706           0 :     aContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
    5707             :   } else {
    5708           6 :     aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
    5709          12 :                       NS_LITERAL_STRING("true"), true);
    5710             :   }
    5711           6 :   MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
    5712           6 : }
    5713             : 
    5714             : void
    5715          24 : ScrollFrameHelper::SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom,
    5716             :                                          nscoord aSize)
    5717             : {
    5718             :   DebugOnly<nsWeakPtr> weakShell(
    5719          40 :     do_GetWeakReference(mOuter->PresContext()->PresShell()));
    5720             :   // convert to pixels
    5721          24 :   int32_t pixelSize = nsPresContext::AppUnitsToIntCSSPixels(aSize);
    5722             : 
    5723             :   // only set the attribute if it changed.
    5724             : 
    5725          40 :   nsAutoString newValue;
    5726          24 :   newValue.AppendInt(pixelSize);
    5727             : 
    5728          24 :   if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters)) {
    5729           8 :     return;
    5730             :   }
    5731             : 
    5732          32 :   AutoWeakFrame weakFrame(mOuter);
    5733          32 :   nsCOMPtr<nsIContent> kungFuDeathGrip = aContent;
    5734          16 :   aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, true);
    5735          16 :   MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
    5736          16 :   if (!weakFrame.IsAlive()) {
    5737           0 :     return;
    5738             :   }
    5739             : 
    5740          16 :   if (mScrollbarActivity) {
    5741           0 :     RefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
    5742           0 :     scrollbarActivity->ActivityOccurred();
    5743             :   }
    5744             : }
    5745             : 
    5746             : static void
    5747           0 : ReduceRadii(nscoord aXBorder, nscoord aYBorder,
    5748             :             nscoord& aXRadius, nscoord& aYRadius)
    5749             : {
    5750             :   // In order to ensure that the inside edge of the border has no
    5751             :   // curvature, we need at least one of its radii to be zero.
    5752           0 :   if (aXRadius <= aXBorder || aYRadius <= aYBorder)
    5753           0 :     return;
    5754             : 
    5755             :   // For any corner where we reduce the radii, preserve the corner's shape.
    5756           0 :   double ratio = std::max(double(aXBorder) / aXRadius,
    5757           0 :                         double(aYBorder) / aYRadius);
    5758           0 :   aXRadius *= ratio;
    5759           0 :   aYRadius *= ratio;
    5760             : }
    5761             : 
    5762             : /**
    5763             :  * Implement an override for nsIFrame::GetBorderRadii to ensure that
    5764             :  * the clipping region for the border radius does not clip the scrollbars.
    5765             :  *
    5766             :  * In other words, we require that the border radius be reduced until the
    5767             :  * inner border radius at the inner edge of the border is 0 wherever we
    5768             :  * have scrollbars.
    5769             :  */
    5770             : bool
    5771         237 : ScrollFrameHelper::GetBorderRadii(const nsSize& aFrameSize,
    5772             :                                   const nsSize& aBorderArea,
    5773             :                                   Sides aSkipSides,
    5774             :                                   nscoord aRadii[8]) const
    5775             : {
    5776         237 :   if (!mOuter->nsContainerFrame::GetBorderRadii(aFrameSize, aBorderArea,
    5777             :                                                 aSkipSides, aRadii)) {
    5778         213 :     return false;
    5779             :   }
    5780             : 
    5781             :   // Since we can use GetActualScrollbarSizes (rather than
    5782             :   // GetDesiredScrollbarSizes) since this doesn't affect reflow, we
    5783             :   // probably should.
    5784          24 :   nsMargin sb = GetActualScrollbarSizes();
    5785          24 :   nsMargin border = mOuter->GetUsedBorder();
    5786             : 
    5787          24 :   if (sb.left > 0 || sb.top > 0) {
    5788           0 :     ReduceRadii(border.left, border.top,
    5789             :                 aRadii[eCornerTopLeftX],
    5790           0 :                 aRadii[eCornerTopLeftY]);
    5791             :   }
    5792             : 
    5793          24 :   if (sb.top > 0 || sb.right > 0) {
    5794           0 :     ReduceRadii(border.right, border.top,
    5795           0 :                 aRadii[eCornerTopRightX],
    5796           0 :                 aRadii[eCornerTopRightY]);
    5797             :   }
    5798             : 
    5799          24 :   if (sb.right > 0 || sb.bottom > 0) {
    5800           0 :     ReduceRadii(border.right, border.bottom,
    5801           0 :                 aRadii[eCornerBottomRightX],
    5802           0 :                 aRadii[eCornerBottomRightY]);
    5803             :   }
    5804             : 
    5805          24 :   if (sb.bottom > 0 || sb.left > 0) {
    5806           0 :     ReduceRadii(border.left, border.bottom,
    5807           0 :                 aRadii[eCornerBottomLeftX],
    5808           0 :                 aRadii[eCornerBottomLeftY]);
    5809             :   }
    5810             : 
    5811          24 :   return true;
    5812             : }
    5813             : 
    5814             : static nscoord
    5815        1560 : SnapCoord(nscoord aCoord, double aRes, nscoord aAppUnitsPerPixel)
    5816             : {
    5817        1560 :   double snappedToLayerPixels = NS_round((aRes*aCoord)/aAppUnitsPerPixel);
    5818        1560 :   return NSToCoordRoundWithClamp(snappedToLayerPixels*aAppUnitsPerPixel/aRes);
    5819             : }
    5820             : 
    5821             : nsRect
    5822        1159 : ScrollFrameHelper::GetScrolledRect() const
    5823             : {
    5824             :   nsRect result =
    5825        2318 :     GetUnsnappedScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
    5826        3477 :                                      mScrollPort.Size());
    5827             : 
    5828        1159 :   if (result.width < mScrollPort.width) {
    5829           0 :     NS_WARNING("Scrolled rect smaller than scrollport?");
    5830             :   }
    5831        1159 :   if (result.height < mScrollPort.height) {
    5832           0 :     NS_WARNING("Scrolled rect smaller than scrollport?");
    5833             :   }
    5834             : 
    5835             :   // Expand / contract the result by up to half a layer pixel so that scrolling
    5836             :   // to the right / bottom edge does not change the layer pixel alignment of
    5837             :   // the scrolled contents.
    5838             : 
    5839        2318 :   if (result.x == 0 && result.y == 0 &&
    5840        2202 :       result.width == mScrollPort.width &&
    5841        1043 :       result.height == mScrollPort.height) {
    5842             :     // The edges that we would snap are already aligned with the scroll port,
    5843             :     // so we can skip all the work below.
    5844         769 :     return result;
    5845             :   }
    5846             : 
    5847             :   // For that, we first convert the scroll port and the scrolled rect to rects
    5848             :   // relative to the reference frame, since that's the space where painting does
    5849             :   // snapping.
    5850         390 :   nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
    5851         390 :   nsIFrame* referenceFrame = nsLayoutUtils::GetReferenceFrame(mOuter);
    5852         390 :   nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(referenceFrame);
    5853         780 :   nsRect scrollPort(mScrollPort.TopLeft() + toReferenceFrame, scrollPortSize);
    5854         780 :   nsRect scrolledRect = result + scrollPort.TopLeft();
    5855             : 
    5856         390 :   if (scrollPort.Overflows() || scrolledRect.Overflows()) {
    5857           0 :     return result;
    5858             :   }
    5859             : 
    5860             :   // Now, snap the bottom right corner of both of these rects.
    5861             :   // We snap to layer pixels, so we need to respect the layer's scale.
    5862         390 :   nscoord appUnitsPerDevPixel = mScrolledFrame->PresContext()->AppUnitsPerDevPixel();
    5863         390 :   gfxSize scale = FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame);
    5864         390 :   if (scale.IsEmpty()) {
    5865           0 :     scale = gfxSize(1.0f, 1.0f);
    5866             :   }
    5867             : 
    5868             :   // Compute bounds for the scroll position, and computed the snapped scrolled
    5869             :   // rect from the scroll position bounds.
    5870         390 :   nscoord snappedScrolledAreaBottom = SnapCoord(scrolledRect.YMost(), scale.height, appUnitsPerDevPixel);
    5871         390 :   nscoord snappedScrollPortBottom = SnapCoord(scrollPort.YMost(), scale.height, appUnitsPerDevPixel);
    5872         390 :   nscoord maximumScrollOffsetY = snappedScrolledAreaBottom - snappedScrollPortBottom;
    5873         390 :   result.SetBottomEdge(scrollPort.height + maximumScrollOffsetY);
    5874             : 
    5875         390 :   if (GetScrolledFrameDir() == NS_STYLE_DIRECTION_LTR) {
    5876         390 :     nscoord snappedScrolledAreaRight = SnapCoord(scrolledRect.XMost(), scale.width, appUnitsPerDevPixel);
    5877         390 :     nscoord snappedScrollPortRight = SnapCoord(scrollPort.XMost(), scale.width, appUnitsPerDevPixel);
    5878         390 :     nscoord maximumScrollOffsetX = snappedScrolledAreaRight - snappedScrollPortRight;
    5879         390 :     result.SetRightEdge(scrollPort.width + maximumScrollOffsetX);
    5880             :   } else {
    5881             :     // In RTL, the scrolled area's right edge is at scrollPort.XMost(),
    5882             :     // and the scrolled area's x position is zero or negative. We want
    5883             :     // the right edge to stay flush with the scroll port, so we snap the
    5884             :     // left edge.
    5885           0 :     nscoord snappedScrolledAreaLeft = SnapCoord(scrolledRect.x, scale.width, appUnitsPerDevPixel);
    5886           0 :     nscoord snappedScrollPortLeft = SnapCoord(scrollPort.x, scale.width, appUnitsPerDevPixel);
    5887           0 :     nscoord minimumScrollOffsetX = snappedScrolledAreaLeft - snappedScrollPortLeft;
    5888           0 :     result.SetLeftEdge(minimumScrollOffsetX);
    5889             :   }
    5890             : 
    5891         390 :   return result;
    5892             : }
    5893             : 
    5894             : 
    5895             : uint8_t
    5896        2032 : ScrollFrameHelper::GetScrolledFrameDir() const
    5897             : {
    5898             :   // If the scrolled frame has unicode-bidi: plaintext, the paragraph
    5899             :   // direction set by the text content overrides the direction of the frame
    5900        2032 :   if (mScrolledFrame->StyleTextReset()->mUnicodeBidi &
    5901             :       NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
    5902           0 :     nsIFrame* childFrame = mScrolledFrame->PrincipalChildList().FirstChild();
    5903           0 :     if (childFrame) {
    5904           0 :       return (nsBidiPresUtils::ParagraphDirection(childFrame) == NSBIDI_LTR)
    5905           0 :              ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
    5906             :     }
    5907             :   }
    5908             : 
    5909        2032 :   return IsBidiLTR() ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL;
    5910             : }
    5911             : 
    5912             : nsRect
    5913        1642 : ScrollFrameHelper::GetUnsnappedScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
    5914             :                                                     const nsSize& aScrollPortSize) const
    5915             : {
    5916        1642 :   return nsLayoutUtils::GetScrolledRect(mScrolledFrame,
    5917             :                                         aScrolledFrameOverflowArea,
    5918        3284 :                                         aScrollPortSize, GetScrolledFrameDir());
    5919             : }
    5920             : 
    5921             : nsMargin
    5922          42 : ScrollFrameHelper::GetActualScrollbarSizes() const
    5923             : {
    5924          84 :   nsRect r = mOuter->GetPaddingRect() - mOuter->GetPosition();
    5925             : 
    5926          42 :   return nsMargin(mScrollPort.y - r.y,
    5927          42 :                   r.XMost() - mScrollPort.XMost(),
    5928          42 :                   r.YMost() - mScrollPort.YMost(),
    5929         210 :                   mScrollPort.x - r.x);
    5930             : }
    5931             : 
    5932             : void
    5933         172 : ScrollFrameHelper::SetScrollbarVisibility(nsIFrame* aScrollbar, bool aVisible)
    5934             : {
    5935         172 :   nsScrollbarFrame* scrollbar = do_QueryFrame(aScrollbar);
    5936         172 :   if (scrollbar) {
    5937             :     // See if we have a mediator.
    5938           6 :     nsIScrollbarMediator* mediator = scrollbar->GetScrollbarMediator();
    5939           6 :     if (mediator) {
    5940             :       // Inform the mediator of the visibility change.
    5941           6 :       mediator->VisibilityChanged(aVisible);
    5942             :     }
    5943             :   }
    5944         172 : }
    5945             : 
    5946             : nscoord
    5947           6 : ScrollFrameHelper::GetCoordAttribute(nsIFrame* aBox, nsIAtom* aAtom,
    5948             :                                          nscoord aDefaultValue,
    5949             :                                          nscoord* aRangeStart,
    5950             :                                          nscoord* aRangeLength)
    5951             : {
    5952           6 :   if (aBox) {
    5953           6 :     nsIContent* content = aBox->GetContent();
    5954             : 
    5955           6 :     nsAutoString value;
    5956           6 :     content->GetAttr(kNameSpaceID_None, aAtom, value);
    5957           6 :     if (!value.IsEmpty()) {
    5958             :       nsresult error;
    5959             :       // convert it to appunits
    5960           6 :       nscoord result = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
    5961           6 :       nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
    5962             :       // Any nscoord value that would round to the attribute value when converted
    5963             :       // to CSS pixels is allowed.
    5964           6 :       *aRangeStart = result - halfPixel;
    5965           6 :       *aRangeLength = halfPixel*2 - 1;
    5966           6 :       return result;
    5967             :     }
    5968             :   }
    5969             : 
    5970             :   // Only this exact default value is allowed.
    5971           0 :   *aRangeStart = aDefaultValue;
    5972           0 :   *aRangeLength = 0;
    5973           0 :   return aDefaultValue;
    5974             : }
    5975             : 
    5976             : nsPresState*
    5977          11 : ScrollFrameHelper::SaveState() const
    5978             : {
    5979          11 :   nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
    5980          11 :   if (mediator) {
    5981             :     // child handles its own scroll state, so don't bother saving state here
    5982           0 :     return nullptr;
    5983             :   }
    5984             : 
    5985             :   // Don't store a scroll state if we never have been scrolled or restored
    5986             :   // a previous scroll state, and we're not in the middle of a smooth scroll.
    5987          11 :   bool isInSmoothScroll = IsProcessingAsyncScroll() || mLastSmoothScrollOrigin;
    5988          11 :   if (!mHasBeenScrolled && !mDidHistoryRestore && !isInSmoothScroll) {
    5989          11 :     return nullptr;
    5990             :   }
    5991             : 
    5992           0 :   nsPresState* state = new nsPresState();
    5993             :   bool allowScrollOriginDowngrade =
    5994           0 :     !nsLayoutUtils::CanScrollOriginClobberApz(mLastScrollOrigin) ||
    5995           0 :     mAllowScrollOriginDowngrade;
    5996             :   // Save mRestorePos instead of our actual current scroll position, if it's
    5997             :   // valid and we haven't moved since the last update of mLastPos (same check
    5998             :   // that ScrollToRestoredPosition uses). This ensures if a reframe occurs
    5999             :   // while we're in the process of loading content to scroll to a restored
    6000             :   // position, we'll keep trying after the reframe. Similarly, if we're in the
    6001             :   // middle of a smooth scroll, store the destination so that when we restore
    6002             :   // we'll jump straight to the end of the scroll animation, rather than
    6003             :   // effectively dropping it. Note that the mRestorePos will override the
    6004             :   // smooth scroll destination if both are present.
    6005           0 :   nsPoint pt = GetLogicalScrollPosition();
    6006           0 :   if (isInSmoothScroll) {
    6007           0 :     pt = mDestination;
    6008           0 :     allowScrollOriginDowngrade = false;
    6009             :   }
    6010           0 :   if (mRestorePos.y != -1 && pt == mLastPos) {
    6011           0 :     pt = mRestorePos;
    6012             :   }
    6013           0 :   state->SetScrollState(pt);
    6014           0 :   state->SetAllowScrollOriginDowngrade(allowScrollOriginDowngrade);
    6015           0 :   if (mIsRoot) {
    6016             :     // Only save resolution properties for root scroll frames
    6017           0 :     nsIPresShell* shell = mOuter->PresContext()->PresShell();
    6018           0 :     state->SetResolution(shell->GetResolution());
    6019           0 :     state->SetScaleToResolution(shell->ScaleToResolution());
    6020             :   }
    6021           0 :   return state;
    6022             : }
    6023             : 
    6024             : void
    6025           0 : ScrollFrameHelper::RestoreState(nsPresState* aState)
    6026             : {
    6027           0 :   mRestorePos = aState->GetScrollPosition();
    6028           0 :   MOZ_ASSERT(mLastScrollOrigin == nsGkAtoms::other);
    6029           0 :   mAllowScrollOriginDowngrade = aState->GetAllowScrollOriginDowngrade();
    6030           0 :   mDidHistoryRestore = true;
    6031           0 :   mLastPos = mScrolledFrame ? GetLogicalScrollPosition() : nsPoint(0,0);
    6032             : 
    6033             :   // Resolution properties should only exist on root scroll frames.
    6034           0 :   MOZ_ASSERT(mIsRoot || (!aState->GetScaleToResolution() &&
    6035             :                          aState->GetResolution() == 1.0));
    6036             : 
    6037           0 :   if (mIsRoot) {
    6038           0 :     nsIPresShell* presShell = mOuter->PresContext()->PresShell();
    6039           0 :     if (aState->GetScaleToResolution()) {
    6040           0 :       presShell->SetResolutionAndScaleTo(aState->GetResolution());
    6041             :     } else {
    6042           0 :       presShell->SetResolution(aState->GetResolution());
    6043             :     }
    6044             :   }
    6045           0 : }
    6046             : 
    6047             : void
    6048          20 : ScrollFrameHelper::PostScrolledAreaEvent()
    6049             : {
    6050          20 :   if (mScrolledAreaEvent.IsPending()) {
    6051           0 :     return;
    6052             :   }
    6053          20 :   mScrolledAreaEvent = new ScrolledAreaEvent(this);
    6054          20 :   nsContentUtils::AddScriptRunner(mScrolledAreaEvent.get());
    6055             : }
    6056             : 
    6057             : ////////////////////////////////////////////////////////////////////////////////
    6058             : // ScrolledArea change event dispatch
    6059             : 
    6060             : NS_IMETHODIMP
    6061          20 : ScrollFrameHelper::ScrolledAreaEvent::Run()
    6062             : {
    6063          20 :   if (mHelper) {
    6064          20 :     mHelper->FireScrolledAreaEvent();
    6065             :   }
    6066          20 :   return NS_OK;
    6067             : }
    6068             : 
    6069             : void
    6070          20 : ScrollFrameHelper::FireScrolledAreaEvent()
    6071             : {
    6072          20 :   mScrolledAreaEvent.Forget();
    6073             : 
    6074          40 :   InternalScrollAreaEvent event(true, eScrolledAreaChanged, nullptr);
    6075          20 :   nsPresContext *prescontext = mOuter->PresContext();
    6076          20 :   nsIContent* content = mOuter->GetContent();
    6077             : 
    6078          20 :   event.mArea = mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
    6079             : 
    6080          20 :   nsIDocument *doc = content->GetUncomposedDoc();
    6081          20 :   if (doc) {
    6082          20 :     EventDispatcher::Dispatch(doc, prescontext, &event, nullptr);
    6083             :   }
    6084          20 : }
    6085             : 
    6086             : uint32_t
    6087           1 : nsIScrollableFrame::GetPerceivedScrollingDirections() const
    6088             : {
    6089           1 :   nscoord oneDevPixel = GetScrolledFrame()->PresContext()->AppUnitsPerDevPixel();
    6090           1 :   uint32_t directions = GetScrollbarVisibility();
    6091           2 :   nsRect scrollRange = GetScrollRange();
    6092           1 :   if (scrollRange.width >= oneDevPixel) {
    6093           0 :     directions |= HORIZONTAL;
    6094             :   }
    6095           1 :   if (scrollRange.height >= oneDevPixel) {
    6096           0 :     directions |= VERTICAL;
    6097             :   }
    6098           2 :   return directions;
    6099             : }
    6100             : 
    6101             : /**
    6102             :  * Collect the scroll-snap-coordinates of frames in the subtree rooted at
    6103             :  * |aFrame|, relative to |aScrolledFrame|, into |aOutCoords|.
    6104             :  */
    6105             : void
    6106           0 : CollectScrollSnapCoordinates(nsIFrame* aFrame, nsIFrame* aScrolledFrame,
    6107             :                              nsTArray<nsPoint>& aOutCoords)
    6108             : {
    6109           0 :   nsIFrame::ChildListIterator childLists(aFrame);
    6110           0 :   for (; !childLists.IsDone(); childLists.Next()) {
    6111           0 :     nsFrameList::Enumerator childFrames(childLists.CurrentList());
    6112           0 :     for (; !childFrames.AtEnd(); childFrames.Next()) {
    6113           0 :       nsIFrame* f = childFrames.get();
    6114             : 
    6115           0 :       const nsStyleDisplay* styleDisplay = f->StyleDisplay();
    6116           0 :       size_t coordCount = styleDisplay->mScrollSnapCoordinate.Length();
    6117             : 
    6118           0 :       if (coordCount) {
    6119           0 :         nsRect frameRect = f->GetRect();
    6120           0 :         nsPoint offset = f->GetOffsetTo(aScrolledFrame);
    6121           0 :         nsRect edgesRect = nsRect(offset, frameRect.Size());
    6122           0 :         for (size_t coordNum = 0; coordNum < coordCount; coordNum++) {
    6123             :           const Position& coordPosition =
    6124           0 :             f->StyleDisplay()->mScrollSnapCoordinate[coordNum];
    6125           0 :           nsPoint coordPoint = edgesRect.TopLeft();
    6126           0 :           coordPoint += nsPoint(coordPosition.mXPosition.mLength,
    6127           0 :                                 coordPosition.mYPosition.mLength);
    6128           0 :           if (coordPosition.mXPosition.mHasPercent) {
    6129           0 :             coordPoint.x += NSToCoordRound(coordPosition.mXPosition.mPercent *
    6130           0 :                                            frameRect.width);
    6131             :           }
    6132           0 :           if (coordPosition.mYPosition.mHasPercent) {
    6133           0 :             coordPoint.y += NSToCoordRound(coordPosition.mYPosition.mPercent *
    6134           0 :                                            frameRect.height);
    6135             :           }
    6136             : 
    6137           0 :           aOutCoords.AppendElement(coordPoint);
    6138             :         }
    6139             :       }
    6140             : 
    6141           0 :       CollectScrollSnapCoordinates(f, aScrolledFrame, aOutCoords);
    6142             :     }
    6143             :   }
    6144           0 : }
    6145             : 
    6146             : layers::ScrollSnapInfo
    6147           3 : ComputeScrollSnapInfo(const ScrollFrameHelper& aScrollFrame)
    6148             : {
    6149           3 :   ScrollSnapInfo result;
    6150             : 
    6151           6 :   ScrollbarStyles styles = aScrollFrame.GetScrollbarStylesFromFrame();
    6152             : 
    6153           6 :   if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
    6154           3 :       styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
    6155             :     // We won't be snapping, short-circuit the computation.
    6156           3 :     return result;
    6157             :   }
    6158             : 
    6159           0 :   result.mScrollSnapTypeX = styles.mScrollSnapTypeX;
    6160           0 :   result.mScrollSnapTypeY = styles.mScrollSnapTypeY;
    6161             : 
    6162           0 :   nsSize scrollPortSize = aScrollFrame.GetScrollPortRect().Size();
    6163             : 
    6164           0 :   result.mScrollSnapDestination = nsPoint(styles.mScrollSnapDestinationX.mLength,
    6165             :                                           styles.mScrollSnapDestinationY.mLength);
    6166           0 :   if (styles.mScrollSnapDestinationX.mHasPercent) {
    6167           0 :     result.mScrollSnapDestination.x +=
    6168           0 :         NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent *
    6169           0 :                               scrollPortSize.width);
    6170             :   }
    6171           0 :   if (styles.mScrollSnapDestinationY.mHasPercent) {
    6172           0 :     result.mScrollSnapDestination.y +=
    6173           0 :         NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent *
    6174           0 :                               scrollPortSize.height);
    6175             :   }
    6176             : 
    6177           0 :   if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) {
    6178           0 :     result.mScrollSnapIntervalX = Some(nsRuleNode::ComputeCoordPercentCalc(
    6179           0 :         styles.mScrollSnapPointsX, scrollPortSize.width));
    6180             :   }
    6181           0 :   if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
    6182           0 :     result.mScrollSnapIntervalY = Some(nsRuleNode::ComputeCoordPercentCalc(
    6183           0 :         styles.mScrollSnapPointsY, scrollPortSize.height));
    6184             :   }
    6185             : 
    6186           0 :   CollectScrollSnapCoordinates(aScrollFrame.GetScrolledFrame(),
    6187             :                                aScrollFrame.GetScrolledFrame(),
    6188           0 :                                result.mScrollSnapCoordinates);
    6189             : 
    6190           0 :   return result;
    6191             : }
    6192             : 
    6193             : layers::ScrollSnapInfo
    6194           3 : ScrollFrameHelper::GetScrollSnapInfo() const
    6195             : {
    6196             :   // TODO(botond): Should we cache it?
    6197           3 :   return ComputeScrollSnapInfo(*this);
    6198             : }
    6199             : 
    6200             : bool
    6201           0 : ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUnit,
    6202             :                                               nsPoint aStartPos,
    6203             :                                               nsPoint &aDestination)
    6204             : {
    6205             :   Maybe<nsPoint> snapPoint = ScrollSnapUtils::GetSnapPointForDestination(
    6206           0 :       GetScrollSnapInfo(), aUnit, mScrollPort.Size(),
    6207           0 :       GetScrollRangeForClamping(), aStartPos, aDestination);
    6208           0 :   if (snapPoint) {
    6209           0 :     aDestination = snapPoint.ref();
    6210           0 :     return true;
    6211             :   }
    6212           0 :   return false;
    6213             : }
    6214             : 
    6215             : bool
    6216           3 : ScrollFrameHelper::UsesContainerScrolling() const
    6217             : {
    6218           3 :   if (gfxPrefs::LayoutUseContainersForRootFrames()) {
    6219           0 :     return mIsRoot;
    6220             :   }
    6221           3 :   return false;
    6222             : }
    6223             : 
    6224             : bool
    6225           0 : ScrollFrameHelper::DragScroll(WidgetEvent* aEvent)
    6226             : {
    6227             :   // Dragging is allowed while within a 20 pixel border. Note that device pixels
    6228             :   // are used so that the same margin is used even when zoomed in or out.
    6229           0 :   nscoord margin = 20 * mOuter->PresContext()->AppUnitsPerDevPixel();
    6230             : 
    6231             :   // Don't drag scroll for small scrollareas.
    6232           0 :   if (mScrollPort.width < margin * 2 || mScrollPort.height < margin * 2) {
    6233           0 :     return false;
    6234             :   }
    6235             : 
    6236             :   // If willScroll is computed as false, then the frame is already scrolled as
    6237             :   // far as it can go in both directions. Return false so that an ancestor
    6238             :   // scrollframe can scroll instead.
    6239           0 :   bool willScroll = false;
    6240           0 :   nsPoint pnt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mOuter);
    6241           0 :   nsPoint scrollPoint = GetScrollPosition();
    6242           0 :   nsRect rangeRect = GetScrollRangeForClamping();
    6243             : 
    6244             :   // Only drag scroll when a scrollbar is present.
    6245           0 :   nsPoint offset;
    6246           0 :   if (mHasHorizontalScrollbar) {
    6247           0 :     if (pnt.x >= mScrollPort.x && pnt.x <= mScrollPort.x + margin) {
    6248           0 :       offset.x = -margin;
    6249           0 :       if (scrollPoint.x > 0) {
    6250           0 :         willScroll = true;
    6251             :       }
    6252           0 :     } else if (pnt.x >= mScrollPort.XMost() - margin && pnt.x <= mScrollPort.XMost()) {
    6253           0 :       offset.x = margin;
    6254           0 :       if (scrollPoint.x < rangeRect.width) {
    6255           0 :         willScroll = true;
    6256             :       }
    6257             :     }
    6258             :   }
    6259             : 
    6260           0 :   if (mHasVerticalScrollbar) {
    6261           0 :     if (pnt.y >= mScrollPort.y && pnt.y <= mScrollPort.y + margin) {
    6262           0 :       offset.y = -margin;
    6263           0 :       if (scrollPoint.y > 0) {
    6264           0 :         willScroll = true;
    6265             :       }
    6266           0 :     } else if (pnt.y >= mScrollPort.YMost() - margin && pnt.y <= mScrollPort.YMost()) {
    6267           0 :       offset.y = margin;
    6268           0 :       if (scrollPoint.y < rangeRect.height) {
    6269           0 :         willScroll = true;
    6270             :       }
    6271             :     }
    6272             :   }
    6273             : 
    6274           0 :   if (offset.x || offset.y) {
    6275           0 :     ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL);
    6276             :   }
    6277             : 
    6278           0 :   return willScroll;
    6279             : }
    6280             : 
    6281             : static void
    6282           0 : AsyncScrollbarDragRejected(nsIFrame* aScrollbar)
    6283             : {
    6284           0 :   if (!aScrollbar) {
    6285           0 :     return;
    6286             :   }
    6287             : 
    6288           0 :   for (nsIFrame::ChildListIterator childLists(aScrollbar);
    6289           0 :        !childLists.IsDone();
    6290           0 :        childLists.Next()) {
    6291           0 :     for (nsIFrame* frame : childLists.CurrentList()) {
    6292           0 :       if (nsSliderFrame* sliderFrame = do_QueryFrame(frame)) {
    6293           0 :         sliderFrame->AsyncScrollbarDragRejected();
    6294             :       }
    6295             :     }
    6296             :   }
    6297             : }
    6298             : 
    6299             : void
    6300           0 : ScrollFrameHelper::AsyncScrollbarDragRejected()
    6301             : {
    6302             :   // We don't get told which scrollbar requested the async drag,
    6303             :   // so we notify both.
    6304           0 :   ::AsyncScrollbarDragRejected(mHScrollbarBox);
    6305           0 :   ::AsyncScrollbarDragRejected(mVScrollbarBox);
    6306           0 : }

Generated by: LCOV version 1.13