LCOV - code coverage report
Current view: top level - layout/generic - nsGfxScrollFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 581 2894 20.1 %
Date: 2018-08-07 16:42:27 Functions: 0 0 -
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13-14-ga5dd952