layout/generic/nsGfxScrollFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/generic/nsGfxScrollFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4599 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/* rendering object to wrap rendering objects that should be scrollable */
    1.10 +
    1.11 +#include "nsGfxScrollFrame.h"
    1.12 +
    1.13 +#include "base/compiler_specific.h"
    1.14 +#include "DisplayItemClip.h"
    1.15 +#include "nsCOMPtr.h"
    1.16 +#include "nsPresContext.h"
    1.17 +#include "nsView.h"
    1.18 +#include "nsIScrollable.h"
    1.19 +#include "nsContainerFrame.h"
    1.20 +#include "nsGkAtoms.h"
    1.21 +#include "nsNameSpaceManager.h"
    1.22 +#include "nsContentList.h"
    1.23 +#include "nsIDocumentInlines.h"
    1.24 +#include "nsFontMetrics.h"
    1.25 +#include "nsBoxLayoutState.h"
    1.26 +#include "nsINodeInfo.h"
    1.27 +#include "nsScrollbarFrame.h"
    1.28 +#include "nsIScrollbarMediator.h"
    1.29 +#include "nsITextControlFrame.h"
    1.30 +#include "nsIDOMHTMLTextAreaElement.h"
    1.31 +#include "nsNodeInfoManager.h"
    1.32 +#include "nsContentCreatorFunctions.h"
    1.33 +#include "nsAutoPtr.h"
    1.34 +#include "nsPresState.h"
    1.35 +#include "nsIHTMLDocument.h"
    1.36 +#include "nsContentUtils.h"
    1.37 +#include "nsLayoutUtils.h"
    1.38 +#include "nsBidiUtils.h"
    1.39 +#include "mozilla/ContentEvents.h"
    1.40 +#include "mozilla/EventDispatcher.h"
    1.41 +#include "mozilla/Preferences.h"
    1.42 +#include "mozilla/LookAndFeel.h"
    1.43 +#include "mozilla/dom/Element.h"
    1.44 +#include <stdint.h>
    1.45 +#include "mozilla/MathAlgorithms.h"
    1.46 +#include "FrameLayerBuilder.h"
    1.47 +#include "nsSMILKeySpline.h"
    1.48 +#include "nsSubDocumentFrame.h"
    1.49 +#include "nsSVGOuterSVGFrame.h"
    1.50 +#include "mozilla/Attributes.h"
    1.51 +#include "ScrollbarActivity.h"
    1.52 +#include "nsRefreshDriver.h"
    1.53 +#include "nsThemeConstants.h"
    1.54 +#include "nsSVGIntegrationUtils.h"
    1.55 +#include "nsIScrollPositionListener.h"
    1.56 +#include "StickyScrollContainer.h"
    1.57 +#include "nsIFrameInlines.h"
    1.58 +#include "gfxPrefs.h"
    1.59 +#include <algorithm>
    1.60 +#include <cstdlib> // for std::abs(int/long)
    1.61 +#include <cmath> // for std::abs(float/double)
    1.62 +
    1.63 +using namespace mozilla;
    1.64 +using namespace mozilla::dom;
    1.65 +using namespace mozilla::layout;
    1.66 +
    1.67 +//----------------------------------------------------------------------
    1.68 +
    1.69 +//----------nsHTMLScrollFrame-------------------------------------------
    1.70 +
    1.71 +nsIFrame*
    1.72 +NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
    1.73 +{
    1.74 +  return new (aPresShell) nsHTMLScrollFrame(aPresShell, aContext, aIsRoot);
    1.75 +}
    1.76 +
    1.77 +NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame)
    1.78 +
    1.79 +nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot)
    1.80 +  : nsContainerFrame(aContext),
    1.81 +    mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
    1.82 +{
    1.83 +}
    1.84 +
    1.85 +void
    1.86 +nsHTMLScrollFrame::ScrollbarActivityStarted() const
    1.87 +{
    1.88 +  if (mHelper.mScrollbarActivity) {
    1.89 +    mHelper.mScrollbarActivity->ActivityStarted();
    1.90 +  }
    1.91 +}
    1.92 +
    1.93 +void
    1.94 +nsHTMLScrollFrame::ScrollbarActivityStopped() const
    1.95 +{
    1.96 +  if (mHelper.mScrollbarActivity) {
    1.97 +    mHelper.mScrollbarActivity->ActivityStopped();
    1.98 +  }
    1.99 +}
   1.100 +
   1.101 +nsresult
   1.102 +nsHTMLScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
   1.103 +{
   1.104 +  return mHelper.CreateAnonymousContent(aElements);
   1.105 +}
   1.106 +
   1.107 +void
   1.108 +nsHTMLScrollFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
   1.109 +                                            uint32_t aFilter)
   1.110 +{
   1.111 +  mHelper.AppendAnonymousContentTo(aElements, aFilter);
   1.112 +}
   1.113 +
   1.114 +void
   1.115 +nsHTMLScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.116 +{
   1.117 +  DestroyAbsoluteFrames(aDestructRoot);
   1.118 +  mHelper.Destroy();
   1.119 +  nsContainerFrame::DestroyFrom(aDestructRoot);
   1.120 +}
   1.121 +
   1.122 +nsresult
   1.123 +nsHTMLScrollFrame::SetInitialChildList(ChildListID  aListID,
   1.124 +                                       nsFrameList& aChildList)
   1.125 +{
   1.126 +  nsresult rv = nsContainerFrame::SetInitialChildList(aListID, aChildList);
   1.127 +  mHelper.ReloadChildFrames();
   1.128 +  return rv;
   1.129 +}
   1.130 +
   1.131 +
   1.132 +nsresult
   1.133 +nsHTMLScrollFrame::AppendFrames(ChildListID  aListID,
   1.134 +                                nsFrameList& aFrameList)
   1.135 +{
   1.136 +  NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   1.137 +  mFrames.AppendFrames(nullptr, aFrameList);
   1.138 +  mHelper.ReloadChildFrames();
   1.139 +  return NS_OK;
   1.140 +}
   1.141 +
   1.142 +nsresult
   1.143 +nsHTMLScrollFrame::InsertFrames(ChildListID aListID,
   1.144 +                                nsIFrame* aPrevFrame,
   1.145 +                                nsFrameList& aFrameList)
   1.146 +{
   1.147 +  NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   1.148 +  NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
   1.149 +               "inserting after sibling frame with different parent");
   1.150 +  mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
   1.151 +  mHelper.ReloadChildFrames();
   1.152 +  return NS_OK;
   1.153 +}
   1.154 +
   1.155 +nsresult
   1.156 +nsHTMLScrollFrame::RemoveFrame(ChildListID aListID,
   1.157 +                               nsIFrame* aOldFrame)
   1.158 +{
   1.159 +  NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   1.160 +  mFrames.DestroyFrame(aOldFrame);
   1.161 +  mHelper.ReloadChildFrames();
   1.162 +  return NS_OK;
   1.163 +}
   1.164 +
   1.165 +nsSplittableType
   1.166 +nsHTMLScrollFrame::GetSplittableType() const
   1.167 +{
   1.168 +  return NS_FRAME_NOT_SPLITTABLE;
   1.169 +}
   1.170 +
   1.171 +nsIAtom*
   1.172 +nsHTMLScrollFrame::GetType() const
   1.173 +{
   1.174 +  return nsGkAtoms::scrollFrame;
   1.175 +}
   1.176 +
   1.177 +/**
   1.178 + HTML scrolling implementation
   1.179 +
   1.180 + All other things being equal, we prefer layouts with fewer scrollbars showing.
   1.181 +*/
   1.182 +
   1.183 +struct MOZ_STACK_CLASS ScrollReflowState {
   1.184 +  const nsHTMLReflowState& mReflowState;
   1.185 +  nsBoxLayoutState mBoxState;
   1.186 +  ScrollbarStyles mStyles;
   1.187 +  nsMargin mComputedBorder;
   1.188 +
   1.189 +  // === Filled in by ReflowScrolledFrame ===
   1.190 +  nsOverflowAreas mContentsOverflowAreas;
   1.191 +  bool mReflowedContentsWithHScrollbar;
   1.192 +  bool mReflowedContentsWithVScrollbar;
   1.193 +
   1.194 +  // === Filled in when TryLayout succeeds ===
   1.195 +  // The size of the inside-border area
   1.196 +  nsSize mInsideBorderSize;
   1.197 +  // Whether we decided to show the horizontal scrollbar
   1.198 +  bool mShowHScrollbar;
   1.199 +  // Whether we decided to show the vertical scrollbar
   1.200 +  bool mShowVScrollbar;
   1.201 +
   1.202 +  ScrollReflowState(nsIScrollableFrame* aFrame,
   1.203 +                    const nsHTMLReflowState& aState) :
   1.204 +    mReflowState(aState),
   1.205 +    // mBoxState is just used for scrollbars so we don't need to
   1.206 +    // worry about the reflow depth here
   1.207 +    mBoxState(aState.frame->PresContext(), aState.rendContext, 0),
   1.208 +    mStyles(aFrame->GetScrollbarStyles()) {
   1.209 +  }
   1.210 +};
   1.211 +
   1.212 +// XXXldb Can this go away?
   1.213 +static nsSize ComputeInsideBorderSize(ScrollReflowState* aState,
   1.214 +                                      const nsSize& aDesiredInsideBorderSize)
   1.215 +{
   1.216 +  // aDesiredInsideBorderSize is the frame size; i.e., it includes
   1.217 +  // borders and padding (but the scrolled child doesn't have
   1.218 +  // borders). The scrolled child has the same padding as us.
   1.219 +  nscoord contentWidth = aState->mReflowState.ComputedWidth();
   1.220 +  if (contentWidth == NS_UNCONSTRAINEDSIZE) {
   1.221 +    contentWidth = aDesiredInsideBorderSize.width -
   1.222 +      aState->mReflowState.ComputedPhysicalPadding().LeftRight();
   1.223 +  }
   1.224 +  nscoord contentHeight = aState->mReflowState.ComputedHeight();
   1.225 +  if (contentHeight == NS_UNCONSTRAINEDSIZE) {
   1.226 +    contentHeight = aDesiredInsideBorderSize.height -
   1.227 +      aState->mReflowState.ComputedPhysicalPadding().TopBottom();
   1.228 +  }
   1.229 +
   1.230 +  contentWidth  = aState->mReflowState.ApplyMinMaxWidth(contentWidth);
   1.231 +  contentHeight = aState->mReflowState.ApplyMinMaxHeight(contentHeight);
   1.232 +  return nsSize(contentWidth + aState->mReflowState.ComputedPhysicalPadding().LeftRight(),
   1.233 +                contentHeight + aState->mReflowState.ComputedPhysicalPadding().TopBottom());
   1.234 +}
   1.235 +
   1.236 +static void
   1.237 +GetScrollbarMetrics(nsBoxLayoutState& aState, nsIFrame* aBox, nsSize* aMin,
   1.238 +                    nsSize* aPref, bool aVertical)
   1.239 +{
   1.240 +  NS_ASSERTION(aState.GetRenderingContext(),
   1.241 +               "Must have rendering context in layout state for size "
   1.242 +               "computations");
   1.243 +
   1.244 +  if (aMin) {
   1.245 +    *aMin = aBox->GetMinSize(aState);
   1.246 +    nsBox::AddMargin(aBox, *aMin);
   1.247 +    if (aMin->width < 0) {
   1.248 +      aMin->width = 0;
   1.249 +    }
   1.250 +    if (aMin->height < 0) {
   1.251 +      aMin->height = 0;
   1.252 +    }
   1.253 +  }
   1.254 +
   1.255 +  if (aPref) {
   1.256 +    *aPref = aBox->GetPrefSize(aState);
   1.257 +    nsBox::AddMargin(aBox, *aPref);
   1.258 +    if (aPref->width < 0) {
   1.259 +      aPref->width = 0;
   1.260 +    }
   1.261 +    if (aPref->height < 0) {
   1.262 +      aPref->height = 0;
   1.263 +    }
   1.264 +  }
   1.265 +}
   1.266 +
   1.267 +/**
   1.268 + * Assuming that we know the metrics for our wrapped frame and
   1.269 + * whether the horizontal and/or vertical scrollbars are present,
   1.270 + * compute the resulting layout and return true if the layout is
   1.271 + * consistent. If the layout is consistent then we fill in the
   1.272 + * computed fields of the ScrollReflowState.
   1.273 + *
   1.274 + * The layout is consistent when both scrollbars are showing if and only
   1.275 + * if they should be showing. A horizontal scrollbar should be showing if all
   1.276 + * following conditions are met:
   1.277 + * 1) the style is not HIDDEN
   1.278 + * 2) our inside-border height is at least the scrollbar height (i.e., the
   1.279 + * scrollbar fits vertically)
   1.280 + * 3) our scrollport width (the inside-border width minus the width allocated for a
   1.281 + * vertical scrollbar, if showing) is at least the scrollbar's min-width
   1.282 + * (i.e., the scrollbar fits horizontally)
   1.283 + * 4) the style is SCROLL, or the kid's overflow-area XMost is
   1.284 + * greater than the scrollport width
   1.285 + *
   1.286 + * @param aForce if true, then we just assume the layout is consistent.
   1.287 + */
   1.288 +bool
   1.289 +nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState,
   1.290 +                             nsHTMLReflowMetrics* aKidMetrics,
   1.291 +                             bool aAssumeHScroll, bool aAssumeVScroll,
   1.292 +                             bool aForce, nsresult* aResult)
   1.293 +{
   1.294 +  *aResult = NS_OK;
   1.295 +
   1.296 +  if ((aState->mStyles.mVertical == NS_STYLE_OVERFLOW_HIDDEN && aAssumeVScroll) ||
   1.297 +      (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
   1.298 +    NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
   1.299 +    return false;
   1.300 +  }
   1.301 +
   1.302 +  if (aAssumeVScroll != aState->mReflowedContentsWithVScrollbar ||
   1.303 +      (aAssumeHScroll != aState->mReflowedContentsWithHScrollbar &&
   1.304 +       ScrolledContentDependsOnHeight(aState))) {
   1.305 +    nsresult rv = ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll,
   1.306 +                                      aKidMetrics, false);
   1.307 +    if (NS_FAILED(rv)) {
   1.308 +      *aResult = rv;
   1.309 +      return false;
   1.310 +    }
   1.311 +  }
   1.312 +
   1.313 +  nsSize vScrollbarMinSize(0, 0);
   1.314 +  nsSize vScrollbarPrefSize(0, 0);
   1.315 +  if (mHelper.mVScrollbarBox) {
   1.316 +    GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
   1.317 +                        &vScrollbarMinSize,
   1.318 +                        aAssumeVScroll ? &vScrollbarPrefSize : nullptr, true);
   1.319 +  }
   1.320 +  nscoord vScrollbarDesiredWidth = aAssumeVScroll ? vScrollbarPrefSize.width : 0;
   1.321 +  nscoord vScrollbarMinHeight = aAssumeVScroll ? vScrollbarMinSize.height : 0;
   1.322 +
   1.323 +  nsSize hScrollbarMinSize(0, 0);
   1.324 +  nsSize hScrollbarPrefSize(0, 0);
   1.325 +  if (mHelper.mHScrollbarBox) {
   1.326 +    GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
   1.327 +                        &hScrollbarMinSize,
   1.328 +                        aAssumeHScroll ? &hScrollbarPrefSize : nullptr, false);
   1.329 +  }
   1.330 +  nscoord hScrollbarDesiredHeight = aAssumeHScroll ? hScrollbarPrefSize.height : 0;
   1.331 +  nscoord hScrollbarMinWidth = aAssumeHScroll ? hScrollbarMinSize.width : 0;
   1.332 +
   1.333 +  // First, compute our inside-border size and scrollport size
   1.334 +  // XXXldb Can we depend more on ComputeSize here?
   1.335 +  nsSize desiredInsideBorderSize;
   1.336 +  desiredInsideBorderSize.width = vScrollbarDesiredWidth +
   1.337 +    std::max(aKidMetrics->Width(), hScrollbarMinWidth);
   1.338 +  desiredInsideBorderSize.height = hScrollbarDesiredHeight +
   1.339 +    std::max(aKidMetrics->Height(), vScrollbarMinHeight);
   1.340 +  aState->mInsideBorderSize =
   1.341 +    ComputeInsideBorderSize(aState, desiredInsideBorderSize);
   1.342 +  nsSize scrollPortSize = nsSize(std::max(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
   1.343 +                                 std::max(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
   1.344 +
   1.345 +  if (!aForce) {
   1.346 +    nsRect scrolledRect =
   1.347 +      mHelper.GetScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
   1.348 +                                     scrollPortSize);
   1.349 +    nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
   1.350 +
   1.351 +    // If the style is HIDDEN then we already know that aAssumeHScroll is false
   1.352 +    if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
   1.353 +      bool wantHScrollbar =
   1.354 +        aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
   1.355 +        scrolledRect.XMost() >= scrollPortSize.width + oneDevPixel ||
   1.356 +        scrolledRect.x <= -oneDevPixel;
   1.357 +      if (scrollPortSize.width < hScrollbarMinSize.width)
   1.358 +        wantHScrollbar = false;
   1.359 +      if (wantHScrollbar != aAssumeHScroll)
   1.360 +        return false;
   1.361 +    }
   1.362 +
   1.363 +    // If the style is HIDDEN then we already know that aAssumeVScroll is false
   1.364 +    if (aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
   1.365 +      bool wantVScrollbar =
   1.366 +        aState->mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL ||
   1.367 +        scrolledRect.YMost() >= scrollPortSize.height + oneDevPixel ||
   1.368 +        scrolledRect.y <= -oneDevPixel;
   1.369 +      if (scrollPortSize.height < vScrollbarMinSize.height)
   1.370 +        wantVScrollbar = false;
   1.371 +      if (wantVScrollbar != aAssumeVScroll)
   1.372 +        return false;
   1.373 +    }
   1.374 +  }
   1.375 +
   1.376 +  nscoord vScrollbarActualWidth = aState->mInsideBorderSize.width - scrollPortSize.width;
   1.377 +
   1.378 +  aState->mShowHScrollbar = aAssumeHScroll;
   1.379 +  aState->mShowVScrollbar = aAssumeVScroll;
   1.380 +  nsPoint scrollPortOrigin(aState->mComputedBorder.left,
   1.381 +                           aState->mComputedBorder.top);
   1.382 +  if (!mHelper.IsScrollbarOnRight()) {
   1.383 +    scrollPortOrigin.x += vScrollbarActualWidth;
   1.384 +  }
   1.385 +  mHelper.mScrollPort = nsRect(scrollPortOrigin, scrollPortSize);
   1.386 +  return true;
   1.387 +}
   1.388 +
   1.389 +bool
   1.390 +nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState* aState)
   1.391 +{
   1.392 +  // Return true if ReflowScrolledFrame is going to do something different
   1.393 +  // based on the presence of a horizontal scrollbar.
   1.394 +  return (mHelper.mScrolledFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) ||
   1.395 +    aState->mReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ||
   1.396 +    aState->mReflowState.ComputedMinHeight() > 0 ||
   1.397 +    aState->mReflowState.ComputedMaxHeight() != NS_UNCONSTRAINEDSIZE;
   1.398 +}
   1.399 +
   1.400 +nsresult
   1.401 +nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState,
   1.402 +                                       bool aAssumeHScroll,
   1.403 +                                       bool aAssumeVScroll,
   1.404 +                                       nsHTMLReflowMetrics* aMetrics,
   1.405 +                                       bool aFirstPass)
   1.406 +{
   1.407 +  // these could be NS_UNCONSTRAINEDSIZE ... std::min arithmetic should
   1.408 +  // be OK
   1.409 +  const nsMargin& padding = aState->mReflowState.ComputedPhysicalPadding();
   1.410 +  nscoord availWidth = aState->mReflowState.ComputedWidth() + padding.LeftRight();
   1.411 +
   1.412 +  nscoord computedHeight = aState->mReflowState.ComputedHeight();
   1.413 +  nscoord computedMinHeight = aState->mReflowState.ComputedMinHeight();
   1.414 +  nscoord computedMaxHeight = aState->mReflowState.ComputedMaxHeight();
   1.415 +  if (!ShouldPropagateComputedHeightToScrolledContent()) {
   1.416 +    computedHeight = NS_UNCONSTRAINEDSIZE;
   1.417 +    computedMinHeight = 0;
   1.418 +    computedMaxHeight = NS_UNCONSTRAINEDSIZE;
   1.419 +  }
   1.420 +  if (aAssumeHScroll) {
   1.421 +    nsSize hScrollbarPrefSize;
   1.422 +    GetScrollbarMetrics(aState->mBoxState, mHelper.mHScrollbarBox,
   1.423 +                        nullptr, &hScrollbarPrefSize, false);
   1.424 +    if (computedHeight != NS_UNCONSTRAINEDSIZE) {
   1.425 +      computedHeight = std::max(0, computedHeight - hScrollbarPrefSize.height);
   1.426 +    }
   1.427 +    computedMinHeight = std::max(0, computedMinHeight - hScrollbarPrefSize.height);
   1.428 +    if (computedMaxHeight != NS_UNCONSTRAINEDSIZE) {
   1.429 +      computedMaxHeight = std::max(0, computedMaxHeight - hScrollbarPrefSize.height);
   1.430 +    }
   1.431 +  }
   1.432 +
   1.433 +  if (aAssumeVScroll) {
   1.434 +    nsSize vScrollbarPrefSize;
   1.435 +    GetScrollbarMetrics(aState->mBoxState, mHelper.mVScrollbarBox,
   1.436 +                        nullptr, &vScrollbarPrefSize, true);
   1.437 +    availWidth = std::max(0, availWidth - vScrollbarPrefSize.width);
   1.438 +  }
   1.439 +
   1.440 +  nsPresContext* presContext = PresContext();
   1.441 +
   1.442 +  // Pass false for aInit so we can pass in the correct padding.
   1.443 +  nsHTMLReflowState kidReflowState(presContext, aState->mReflowState,
   1.444 +                                   mHelper.mScrolledFrame,
   1.445 +                                   nsSize(availWidth, NS_UNCONSTRAINEDSIZE),
   1.446 +                                   -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
   1.447 +  kidReflowState.Init(presContext, -1, -1, nullptr,
   1.448 +                      &padding);
   1.449 +  kidReflowState.mFlags.mAssumingHScrollbar = aAssumeHScroll;
   1.450 +  kidReflowState.mFlags.mAssumingVScrollbar = aAssumeVScroll;
   1.451 +  kidReflowState.SetComputedHeight(computedHeight);
   1.452 +  kidReflowState.ComputedMinHeight() = computedMinHeight;
   1.453 +  kidReflowState.ComputedMaxHeight() = computedMaxHeight;
   1.454 +
   1.455 +  // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to
   1.456 +  // reflect our assumptions while we reflow the child.
   1.457 +  bool didHaveHorizontalScrollbar = mHelper.mHasHorizontalScrollbar;
   1.458 +  bool didHaveVerticalScrollbar = mHelper.mHasVerticalScrollbar;
   1.459 +  mHelper.mHasHorizontalScrollbar = aAssumeHScroll;
   1.460 +  mHelper.mHasVerticalScrollbar = aAssumeVScroll;
   1.461 +
   1.462 +  nsReflowStatus status;
   1.463 +  nsresult rv = ReflowChild(mHelper.mScrolledFrame, presContext, *aMetrics,
   1.464 +                            kidReflowState, 0, 0,
   1.465 +                            NS_FRAME_NO_MOVE_FRAME, status);
   1.466 +
   1.467 +  mHelper.mHasHorizontalScrollbar = didHaveHorizontalScrollbar;
   1.468 +  mHelper.mHasVerticalScrollbar = didHaveVerticalScrollbar;
   1.469 +
   1.470 +  // Don't resize or position the view (if any) because we're going to resize
   1.471 +  // it to the correct size anyway in PlaceScrollArea. Allowing it to
   1.472 +  // resize here would size it to the natural height of the frame,
   1.473 +  // which will usually be different from the scrollport height;
   1.474 +  // invalidating the difference will cause unnecessary repainting.
   1.475 +  FinishReflowChild(mHelper.mScrolledFrame, presContext,
   1.476 +                    *aMetrics, &kidReflowState, 0, 0,
   1.477 +                    NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW);
   1.478 +
   1.479 +  // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
   1.480 +  // setting their mOverflowArea. This is wrong because every frame should
   1.481 +  // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
   1.482 +  // support the 'outline' property because of this. Rather than fix the world
   1.483 +  // right now, just fix up the overflow area if necessary. Note that we don't
   1.484 +  // check HasOverflowRect() because it could be set even though the
   1.485 +  // overflow area doesn't include the frame bounds.
   1.486 +  aMetrics->UnionOverflowAreasWithDesiredBounds();
   1.487 +
   1.488 +  if (MOZ_UNLIKELY(StyleDisplay()->mOverflowClipBox ==
   1.489 +                     NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
   1.490 +    nsOverflowAreas childOverflow;
   1.491 +    nsLayoutUtils::UnionChildOverflow(mHelper.mScrolledFrame, childOverflow);
   1.492 +    nsRect childScrollableOverflow = childOverflow.ScrollableOverflow();
   1.493 +    childScrollableOverflow.Inflate(padding);
   1.494 +    nsRect contentArea = nsRect(0, 0, availWidth, computedHeight);
   1.495 +    if (!contentArea.Contains(childScrollableOverflow)) {
   1.496 +      aMetrics->mOverflowAreas.ScrollableOverflow() = childScrollableOverflow;
   1.497 +    }
   1.498 +  }
   1.499 +
   1.500 +  aState->mContentsOverflowAreas = aMetrics->mOverflowAreas;
   1.501 +  aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
   1.502 +  aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
   1.503 +
   1.504 +  return rv;
   1.505 +}
   1.506 +
   1.507 +bool
   1.508 +nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowState& aState)
   1.509 +{
   1.510 +  if (aState.mStyles.mHorizontal != NS_STYLE_OVERFLOW_AUTO)
   1.511 +    // no guessing required
   1.512 +    return aState.mStyles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL;
   1.513 +
   1.514 +  return mHelper.mHasHorizontalScrollbar;
   1.515 +}
   1.516 +
   1.517 +bool
   1.518 +nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowState& aState)
   1.519 +{
   1.520 +  if (aState.mStyles.mVertical != NS_STYLE_OVERFLOW_AUTO)
   1.521 +    // no guessing required
   1.522 +    return aState.mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL;
   1.523 +
   1.524 +  // If we've had at least one non-initial reflow, then just assume
   1.525 +  // the state of the vertical scrollbar will be what we determined
   1.526 +  // last time.
   1.527 +  if (mHelper.mHadNonInitialReflow) {
   1.528 +    return mHelper.mHasVerticalScrollbar;
   1.529 +  }
   1.530 +
   1.531 +  // If this is the initial reflow, guess false because usually
   1.532 +  // we have very little content by then.
   1.533 +  if (InInitialReflow())
   1.534 +    return false;
   1.535 +
   1.536 +  if (mHelper.mIsRoot) {
   1.537 +    nsIFrame *f = mHelper.mScrolledFrame->GetFirstPrincipalChild();
   1.538 +    if (f && f->GetType() == nsGkAtoms::svgOuterSVGFrame &&
   1.539 +        static_cast<nsSVGOuterSVGFrame*>(f)->VerticalScrollbarNotNeeded()) {
   1.540 +      // Common SVG case - avoid a bad guess.
   1.541 +      return false;
   1.542 +    }
   1.543 +    // Assume that there will be a scrollbar; it seems to me
   1.544 +    // that 'most pages' do have a scrollbar, and anyway, it's cheaper
   1.545 +    // to do an extra reflow for the pages that *don't* need a
   1.546 +    // scrollbar (because on average they will have less content).
   1.547 +    return true;
   1.548 +  }
   1.549 +
   1.550 +  // For non-viewports, just guess that we don't need a scrollbar.
   1.551 +  // XXX I wonder if statistically this is the right idea; I'm
   1.552 +  // basically guessing that there are a lot of overflow:auto DIVs
   1.553 +  // that get their intrinsic size and don't overflow
   1.554 +  return false;
   1.555 +}
   1.556 +
   1.557 +bool
   1.558 +nsHTMLScrollFrame::InInitialReflow() const
   1.559 +{
   1.560 +  // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
   1.561 +  // root scrollframe.  In that case we want to skip this clause altogether.
   1.562 +  // The guess here is that there are lots of overflow:auto divs out there that
   1.563 +  // end up auto-sizing so they don't overflow, and that the root basically
   1.564 +  // always needs a scrollbar if it did last time we loaded this page (good
   1.565 +  // assumption, because our initial reflow is no longer synchronous).
   1.566 +  return !mHelper.mIsRoot && (GetStateBits() & NS_FRAME_FIRST_REFLOW);
   1.567 +}
   1.568 +
   1.569 +nsresult
   1.570 +nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
   1.571 +                                  const nsHTMLReflowMetrics& aDesiredSize)
   1.572 +{
   1.573 +  nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.GetWritingMode(), aDesiredSize.mFlags);
   1.574 +  nsresult rv = ReflowScrolledFrame(aState, GuessHScrollbarNeeded(*aState),
   1.575 +      GuessVScrollbarNeeded(*aState), &kidDesiredSize, true);
   1.576 +  NS_ENSURE_SUCCESS(rv, rv);
   1.577 +
   1.578 +  // There's an important special case ... if the child appears to fit
   1.579 +  // in the inside-border rect (but overflows the scrollport), we
   1.580 +  // should try laying it out without a vertical scrollbar. It will
   1.581 +  // usually fit because making the available-width wider will not
   1.582 +  // normally make the child taller. (The only situation I can think
   1.583 +  // of is when you have a line containing %-width inline replaced
   1.584 +  // elements whose percentages sum to more than 100%, so increasing
   1.585 +  // the available width makes the line break where it was fitting
   1.586 +  // before.) If we don't treat this case specially, then we will
   1.587 +  // decide that showing scrollbars is OK because the content
   1.588 +  // overflows when we're showing scrollbars and we won't try to
   1.589 +  // remove the vertical scrollbar.
   1.590 +
   1.591 +  // Detecting when we enter this special case is important for when
   1.592 +  // people design layouts that exactly fit the container "most of the
   1.593 +  // time".
   1.594 +
   1.595 +  // XXX Is this check really sufficient to catch all the incremental cases
   1.596 +  // where the ideal case doesn't have a scrollbar?
   1.597 +  if ((aState->mReflowedContentsWithHScrollbar || aState->mReflowedContentsWithVScrollbar) &&
   1.598 +      aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
   1.599 +      aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
   1.600 +    nsSize insideBorderSize =
   1.601 +      ComputeInsideBorderSize(aState,
   1.602 +                              nsSize(kidDesiredSize.Width(), kidDesiredSize.Height()));
   1.603 +    nsRect scrolledRect =
   1.604 +      mHelper.GetScrolledRectInternal(kidDesiredSize.ScrollableOverflow(),
   1.605 +                                     insideBorderSize);
   1.606 +    if (nsRect(nsPoint(0, 0), insideBorderSize).Contains(scrolledRect)) {
   1.607 +      // Let's pretend we had no scrollbars coming in here
   1.608 +      rv = ReflowScrolledFrame(aState, false, false,
   1.609 +                               &kidDesiredSize, false);
   1.610 +      NS_ENSURE_SUCCESS(rv, rv);
   1.611 +    }
   1.612 +  }
   1.613 +
   1.614 +  // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
   1.615 +  // Do this first because changing the vertical scrollbar setting is expensive,
   1.616 +  // forcing a reflow always.
   1.617 +
   1.618 +  // Try leaving the horizontal scrollbar unchanged first. This will be more
   1.619 +  // efficient.
   1.620 +  if (TryLayout(aState, &kidDesiredSize, aState->mReflowedContentsWithHScrollbar,
   1.621 +                aState->mReflowedContentsWithVScrollbar, false, &rv))
   1.622 +    return NS_OK;
   1.623 +  if (TryLayout(aState, &kidDesiredSize, !aState->mReflowedContentsWithHScrollbar,
   1.624 +                aState->mReflowedContentsWithVScrollbar, false, &rv))
   1.625 +    return NS_OK;
   1.626 +
   1.627 +  // OK, now try toggling the vertical scrollbar. The performance advantage
   1.628 +  // of trying the status-quo horizontal scrollbar state
   1.629 +  // does not exist here (we'll have to reflow due to the vertical scrollbar
   1.630 +  // change), so always try no horizontal scrollbar first.
   1.631 +  bool newVScrollbarState = !aState->mReflowedContentsWithVScrollbar;
   1.632 +  if (TryLayout(aState, &kidDesiredSize, false, newVScrollbarState, false, &rv))
   1.633 +    return NS_OK;
   1.634 +  if (TryLayout(aState, &kidDesiredSize, true, newVScrollbarState, false, &rv))
   1.635 +    return NS_OK;
   1.636 +
   1.637 +  // OK, we're out of ideas. Try again enabling whatever scrollbars we can
   1.638 +  // enable and force the layout to stick even if it's inconsistent.
   1.639 +  // This just happens sometimes.
   1.640 +  TryLayout(aState, &kidDesiredSize,
   1.641 +            aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN,
   1.642 +            aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN,
   1.643 +            true, &rv);
   1.644 +  return rv;
   1.645 +}
   1.646 +
   1.647 +void
   1.648 +nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState,
   1.649 +                                   const nsPoint& aScrollPosition)
   1.650 +{
   1.651 +  nsIFrame *scrolledFrame = mHelper.mScrolledFrame;
   1.652 +  // Set the x,y of the scrolled frame to the correct value
   1.653 +  scrolledFrame->SetPosition(mHelper.mScrollPort.TopLeft() - aScrollPosition);
   1.654 +
   1.655 +  nsRect scrolledArea;
   1.656 +  // Preserve the width or height of empty rects
   1.657 +  nsSize portSize = mHelper.mScrollPort.Size();
   1.658 +  nsRect scrolledRect =
   1.659 +    mHelper.GetScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
   1.660 +                                   portSize);
   1.661 +  scrolledArea.UnionRectEdges(scrolledRect,
   1.662 +                              nsRect(nsPoint(0,0), portSize));
   1.663 +
   1.664 +  // Store the new overflow area. Note that this changes where an outline
   1.665 +  // of the scrolled frame would be painted, but scrolled frames can't have
   1.666 +  // outlines (the outline would go on this scrollframe instead).
   1.667 +  // Using FinishAndStoreOverflow is needed so the overflow rect
   1.668 +  // gets set correctly.  It also messes with the overflow rect in the
   1.669 +  // -moz-hidden-unscrollable case, but scrolled frames can't have
   1.670 +  // 'overflow' either.
   1.671 +  // This needs to happen before SyncFrameViewAfterReflow so
   1.672 +  // HasOverflowRect() will return the correct value.
   1.673 +  nsOverflowAreas overflow(scrolledArea, scrolledArea);
   1.674 +  scrolledFrame->FinishAndStoreOverflow(overflow,
   1.675 +                                        scrolledFrame->GetSize());
   1.676 +
   1.677 +  // Note that making the view *exactly* the size of the scrolled area
   1.678 +  // is critical, since the view scrolling code uses the size of the
   1.679 +  // scrolled view to clamp scroll requests.
   1.680 +  // Normally the scrolledFrame won't have a view but in some cases it
   1.681 +  // might create its own.
   1.682 +  nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame->PresContext(),
   1.683 +                                             scrolledFrame,
   1.684 +                                             scrolledFrame->GetView(),
   1.685 +                                             scrolledArea,
   1.686 +                                             0);
   1.687 +}
   1.688 +
   1.689 +nscoord
   1.690 +nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsRenderingContext *aRenderingContext)
   1.691 +{
   1.692 +  ScrollbarStyles ss = GetScrollbarStyles();
   1.693 +  if (ss.mVertical != NS_STYLE_OVERFLOW_SCROLL || !mHelper.mVScrollbarBox)
   1.694 +    return 0;
   1.695 +
   1.696 +  // Don't need to worry about reflow depth here since it's
   1.697 +  // just for scrollbars
   1.698 +  nsBoxLayoutState bls(PresContext(), aRenderingContext, 0);
   1.699 +  nsSize vScrollbarPrefSize(0, 0);
   1.700 +  GetScrollbarMetrics(bls, mHelper.mVScrollbarBox,
   1.701 +                      nullptr, &vScrollbarPrefSize, true);
   1.702 +  return vScrollbarPrefSize.width;
   1.703 +}
   1.704 +
   1.705 +/* virtual */ nscoord
   1.706 +nsHTMLScrollFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
   1.707 +{
   1.708 +  nscoord result = mHelper.mScrolledFrame->GetMinWidth(aRenderingContext);
   1.709 +  DISPLAY_MIN_WIDTH(this, result);
   1.710 +  return result + GetIntrinsicVScrollbarWidth(aRenderingContext);
   1.711 +}
   1.712 +
   1.713 +/* virtual */ nscoord
   1.714 +nsHTMLScrollFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
   1.715 +{
   1.716 +  nscoord result = mHelper.mScrolledFrame->GetPrefWidth(aRenderingContext);
   1.717 +  DISPLAY_PREF_WIDTH(this, result);
   1.718 +  return NSCoordSaturatingAdd(result, GetIntrinsicVScrollbarWidth(aRenderingContext));
   1.719 +}
   1.720 +
   1.721 +nsresult
   1.722 +nsHTMLScrollFrame::GetPadding(nsMargin& aMargin)
   1.723 +{
   1.724 +  // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
   1.725 +  // reaize that.  If we're stuck inside a XUL box, we need to claim no
   1.726 +  // padding.
   1.727 +  // @see also nsXULScrollFrame::GetPadding.
   1.728 +  aMargin.SizeTo(0,0,0,0);
   1.729 +  return NS_OK;
   1.730 +}
   1.731 +
   1.732 +bool
   1.733 +nsHTMLScrollFrame::IsCollapsed()
   1.734 +{
   1.735 +  // We're never collapsed in the box sense.
   1.736 +  return false;
   1.737 +}
   1.738 +
   1.739 +// Return the <browser> if the scrollframe is for the root frame directly
   1.740 +// inside a <browser>.
   1.741 +static nsIContent*
   1.742 +GetBrowserRoot(nsIContent* aContent)
   1.743 +{
   1.744 +  if (aContent) {
   1.745 +    nsIDocument* doc = aContent->GetCurrentDoc();
   1.746 +    nsPIDOMWindow* win = doc->GetWindow();
   1.747 +    if (win) {
   1.748 +      nsCOMPtr<nsIContent> frameContent =
   1.749 +        do_QueryInterface(win->GetFrameElementInternal());
   1.750 +      if (frameContent &&
   1.751 +          frameContent->NodeInfo()->Equals(nsGkAtoms::browser, kNameSpaceID_XUL))
   1.752 +        return frameContent;
   1.753 +    }
   1.754 +  }
   1.755 +
   1.756 +  return nullptr;
   1.757 +}
   1.758 +
   1.759 +nsresult
   1.760 +nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
   1.761 +                          nsHTMLReflowMetrics&     aDesiredSize,
   1.762 +                          const nsHTMLReflowState& aReflowState,
   1.763 +                          nsReflowStatus&          aStatus)
   1.764 +{
   1.765 +  DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
   1.766 +  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   1.767 +
   1.768 +  mHelper.HandleScrollbarStyleSwitching();
   1.769 +
   1.770 +  ScrollReflowState state(this, aReflowState);
   1.771 +  // sanity check: ensure that if we have no scrollbar, we treat it
   1.772 +  // as hidden.
   1.773 +  if (!mHelper.mVScrollbarBox || mHelper.mNeverHasVerticalScrollbar)
   1.774 +    state.mStyles.mVertical = NS_STYLE_OVERFLOW_HIDDEN;
   1.775 +  if (!mHelper.mHScrollbarBox || mHelper.mNeverHasHorizontalScrollbar)
   1.776 +    state.mStyles.mHorizontal = NS_STYLE_OVERFLOW_HIDDEN;
   1.777 +
   1.778 +  //------------ Handle Incremental Reflow -----------------
   1.779 +  bool reflowHScrollbar = true;
   1.780 +  bool reflowVScrollbar = true;
   1.781 +  bool reflowScrollCorner = true;
   1.782 +  if (!aReflowState.ShouldReflowAllKids()) {
   1.783 +    #define NEEDS_REFLOW(frame_) \
   1.784 +      ((frame_) && NS_SUBTREE_DIRTY(frame_))
   1.785 +
   1.786 +    reflowHScrollbar = NEEDS_REFLOW(mHelper.mHScrollbarBox);
   1.787 +    reflowVScrollbar = NEEDS_REFLOW(mHelper.mVScrollbarBox);
   1.788 +    reflowScrollCorner = NEEDS_REFLOW(mHelper.mScrollCornerBox) ||
   1.789 +                         NEEDS_REFLOW(mHelper.mResizerBox);
   1.790 +
   1.791 +    #undef NEEDS_REFLOW
   1.792 +  }
   1.793 +
   1.794 +  if (mHelper.mIsRoot) {
   1.795 +    mHelper.mCollapsedResizer = true;
   1.796 +
   1.797 +    nsIContent* browserRoot = GetBrowserRoot(mContent);
   1.798 +    if (browserRoot) {
   1.799 +      bool showResizer = browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer);
   1.800 +      reflowScrollCorner = showResizer == mHelper.mCollapsedResizer;
   1.801 +      mHelper.mCollapsedResizer = !showResizer;
   1.802 +    }
   1.803 +  }
   1.804 +
   1.805 +  nsRect oldScrollAreaBounds = mHelper.mScrollPort;
   1.806 +  nsRect oldScrolledAreaBounds =
   1.807 +    mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
   1.808 +  nsPoint oldScrollPosition = mHelper.GetScrollPosition();
   1.809 +
   1.810 +  state.mComputedBorder = aReflowState.ComputedPhysicalBorderPadding() -
   1.811 +    aReflowState.ComputedPhysicalPadding();
   1.812 +
   1.813 +  nsresult rv = ReflowContents(&state, aDesiredSize);
   1.814 +  if (NS_FAILED(rv))
   1.815 +    return rv;
   1.816 +
   1.817 +  // Restore the old scroll position, for now, even if that's not valid anymore
   1.818 +  // because we changed size. We'll fix it up in a post-reflow callback, because
   1.819 +  // our current size may only be temporary (e.g. we're compute XUL desired sizes).
   1.820 +  PlaceScrollArea(state, oldScrollPosition);
   1.821 +  if (!mHelper.mPostedReflowCallback) {
   1.822 +    // Make sure we'll try scrolling to restored position
   1.823 +    PresContext()->PresShell()->PostReflowCallback(&mHelper);
   1.824 +    mHelper.mPostedReflowCallback = true;
   1.825 +  }
   1.826 +
   1.827 +  bool didHaveHScrollbar = mHelper.mHasHorizontalScrollbar;
   1.828 +  bool didHaveVScrollbar = mHelper.mHasVerticalScrollbar;
   1.829 +  mHelper.mHasHorizontalScrollbar = state.mShowHScrollbar;
   1.830 +  mHelper.mHasVerticalScrollbar = state.mShowVScrollbar;
   1.831 +  nsRect newScrollAreaBounds = mHelper.mScrollPort;
   1.832 +  nsRect newScrolledAreaBounds =
   1.833 +    mHelper.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
   1.834 +  if (mHelper.mSkippedScrollbarLayout ||
   1.835 +      reflowHScrollbar || reflowVScrollbar || reflowScrollCorner ||
   1.836 +      (GetStateBits() & NS_FRAME_IS_DIRTY) ||
   1.837 +      didHaveHScrollbar != state.mShowHScrollbar ||
   1.838 +      didHaveVScrollbar != state.mShowVScrollbar ||
   1.839 +      !oldScrollAreaBounds.IsEqualEdges(newScrollAreaBounds) ||
   1.840 +      !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
   1.841 +    if (!mHelper.mSupppressScrollbarUpdate) {
   1.842 +      mHelper.mSkippedScrollbarLayout = false;
   1.843 +      mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, state.mShowHScrollbar);
   1.844 +      mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, state.mShowVScrollbar);
   1.845 +      // place and reflow scrollbars
   1.846 +      nsRect insideBorderArea =
   1.847 +        nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
   1.848 +               state.mInsideBorderSize);
   1.849 +      mHelper.LayoutScrollbars(state.mBoxState, insideBorderArea,
   1.850 +                              oldScrollAreaBounds);
   1.851 +    } else {
   1.852 +      mHelper.mSkippedScrollbarLayout = true;
   1.853 +    }
   1.854 +  }
   1.855 +
   1.856 +  aDesiredSize.Width() = state.mInsideBorderSize.width +
   1.857 +    state.mComputedBorder.LeftRight();
   1.858 +  aDesiredSize.Height() = state.mInsideBorderSize.height +
   1.859 +    state.mComputedBorder.TopBottom();
   1.860 +
   1.861 +  aDesiredSize.SetOverflowAreasToDesiredBounds();
   1.862 +  if (mHelper.IsIgnoringViewportClipping()) {
   1.863 +    aDesiredSize.mOverflowAreas.UnionWith(
   1.864 +      state.mContentsOverflowAreas + mHelper.mScrolledFrame->GetPosition());
   1.865 +  }
   1.866 +
   1.867 +  mHelper.UpdateSticky();
   1.868 +  FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
   1.869 +
   1.870 +  if (!InInitialReflow() && !mHelper.mHadNonInitialReflow) {
   1.871 +    mHelper.mHadNonInitialReflow = true;
   1.872 +  }
   1.873 +
   1.874 +  if (mHelper.mIsRoot && !oldScrolledAreaBounds.IsEqualEdges(newScrolledAreaBounds)) {
   1.875 +    mHelper.PostScrolledAreaEvent();
   1.876 +  }
   1.877 +
   1.878 +  aStatus = NS_FRAME_COMPLETE;
   1.879 +  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   1.880 +  mHelper.PostOverflowEvent();
   1.881 +  return rv;
   1.882 +}
   1.883 +
   1.884 +
   1.885 +////////////////////////////////////////////////////////////////////////////////
   1.886 +
   1.887 +#ifdef DEBUG_FRAME_DUMP
   1.888 +nsresult
   1.889 +nsHTMLScrollFrame::GetFrameName(nsAString& aResult) const
   1.890 +{
   1.891 +  return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult);
   1.892 +}
   1.893 +#endif
   1.894 +
   1.895 +#ifdef ACCESSIBILITY
   1.896 +a11y::AccType
   1.897 +nsHTMLScrollFrame::AccessibleType()
   1.898 +{
   1.899 +  // Create an accessible regardless of focusable state because the state can be
   1.900 +  // changed during frame life cycle without any notifications to accessibility.
   1.901 +  if (mContent->IsRootOfNativeAnonymousSubtree() ||
   1.902 +      GetScrollbarStyles() ==
   1.903 +        ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN) ) {
   1.904 +    return a11y::eNoType;
   1.905 +  }
   1.906 +
   1.907 +  return a11y::eHyperTextType;
   1.908 +}
   1.909 +#endif
   1.910 +
   1.911 +NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
   1.912 +  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
   1.913 +  NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
   1.914 +  NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
   1.915 +  NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
   1.916 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
   1.917 +
   1.918 +//----------nsXULScrollFrame-------------------------------------------
   1.919 +
   1.920 +nsIFrame*
   1.921 +NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
   1.922 +                     bool aIsRoot, bool aClipAllDescendants)
   1.923 +{
   1.924 +  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot,
   1.925 +                                           aClipAllDescendants);
   1.926 +}
   1.927 +
   1.928 +NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
   1.929 +
   1.930 +nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext,
   1.931 +                                   bool aIsRoot, bool aClipAllDescendants)
   1.932 +  : nsBoxFrame(aShell, aContext, aIsRoot),
   1.933 +    mHelper(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
   1.934 +{
   1.935 +  SetLayoutManager(nullptr);
   1.936 +  mHelper.mClipAllDescendants = aClipAllDescendants;
   1.937 +}
   1.938 +
   1.939 +void
   1.940 +nsXULScrollFrame::ScrollbarActivityStarted() const
   1.941 +{
   1.942 +  if (mHelper.mScrollbarActivity) {
   1.943 +    mHelper.mScrollbarActivity->ActivityStarted();
   1.944 +  }
   1.945 +}
   1.946 +
   1.947 +void
   1.948 +nsXULScrollFrame::ScrollbarActivityStopped() const
   1.949 +{
   1.950 +  if (mHelper.mScrollbarActivity) {
   1.951 +    mHelper.mScrollbarActivity->ActivityStopped();
   1.952 +  }
   1.953 +}
   1.954 +
   1.955 +nsMargin
   1.956 +ScrollFrameHelper::GetDesiredScrollbarSizes(nsBoxLayoutState* aState)
   1.957 +{
   1.958 +  NS_ASSERTION(aState && aState->GetRenderingContext(),
   1.959 +               "Must have rendering context in layout state for size "
   1.960 +               "computations");
   1.961 +
   1.962 +  nsMargin result(0, 0, 0, 0);
   1.963 +
   1.964 +  if (mVScrollbarBox) {
   1.965 +    nsSize size = mVScrollbarBox->GetPrefSize(*aState);
   1.966 +    nsBox::AddMargin(mVScrollbarBox, size);
   1.967 +    if (IsScrollbarOnRight())
   1.968 +      result.left = size.width;
   1.969 +    else
   1.970 +      result.right = size.width;
   1.971 +  }
   1.972 +
   1.973 +  if (mHScrollbarBox) {
   1.974 +    nsSize size = mHScrollbarBox->GetPrefSize(*aState);
   1.975 +    nsBox::AddMargin(mHScrollbarBox, size);
   1.976 +    // We don't currently support any scripts that would require a scrollbar
   1.977 +    // at the top. (Are there any?)
   1.978 +    result.bottom = size.height;
   1.979 +  }
   1.980 +
   1.981 +  return result;
   1.982 +}
   1.983 +
   1.984 +nscoord
   1.985 +ScrollFrameHelper::GetNondisappearingScrollbarWidth(nsBoxLayoutState* aState)
   1.986 +{
   1.987 +  NS_ASSERTION(aState && aState->GetRenderingContext(),
   1.988 +               "Must have rendering context in layout state for size "
   1.989 +               "computations");
   1.990 +
   1.991 +  if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
   1.992 +    // We're using overlay scrollbars, so we need to get the width that
   1.993 +    // non-disappearing scrollbars would have.
   1.994 +    nsITheme* theme = aState->PresContext()->GetTheme();
   1.995 +    if (theme &&
   1.996 +        theme->ThemeSupportsWidget(aState->PresContext(),
   1.997 +                                   mVScrollbarBox,
   1.998 +                                   NS_THEME_SCROLLBAR_NON_DISAPPEARING)) {
   1.999 +      nsIntSize size;
  1.1000 +      nsRenderingContext* rendContext = aState->GetRenderingContext();
  1.1001 +      if (rendContext) {
  1.1002 +        bool canOverride = true;
  1.1003 +        theme->GetMinimumWidgetSize(rendContext,
  1.1004 +                                    mVScrollbarBox,
  1.1005 +                                    NS_THEME_SCROLLBAR_NON_DISAPPEARING,
  1.1006 +                                    &size,
  1.1007 +                                    &canOverride);
  1.1008 +        if (size.width) {
  1.1009 +          return aState->PresContext()->DevPixelsToAppUnits(size.width);
  1.1010 +        }
  1.1011 +      }
  1.1012 +    }
  1.1013 +  }
  1.1014 +
  1.1015 +  return GetDesiredScrollbarSizes(aState).LeftRight();
  1.1016 +}
  1.1017 +
  1.1018 +void
  1.1019 +ScrollFrameHelper::HandleScrollbarStyleSwitching()
  1.1020 +{
  1.1021 +  // Check if we switched between scrollbar styles.
  1.1022 +  if (mScrollbarActivity &&
  1.1023 +      LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) == 0) {
  1.1024 +    mScrollbarActivity->Destroy();
  1.1025 +    mScrollbarActivity = nullptr;
  1.1026 +    mOuter->PresContext()->ThemeChanged();
  1.1027 +  }
  1.1028 +  else if (!mScrollbarActivity &&
  1.1029 +           LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
  1.1030 +    mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(mOuter));
  1.1031 +    mOuter->PresContext()->ThemeChanged();
  1.1032 +  }
  1.1033 +}
  1.1034 +
  1.1035 +static bool IsFocused(nsIContent* aContent)
  1.1036 +{
  1.1037 +  // Some content elements, like the GetContent() of a scroll frame
  1.1038 +  // for a text input field, are inside anonymous subtrees, but the focus
  1.1039 +  // manager always reports a non-anonymous element as the focused one, so
  1.1040 +  // walk up the tree until we reach a non-anonymous element.
  1.1041 +  while (aContent && aContent->IsInAnonymousSubtree()) {
  1.1042 +    aContent = aContent->GetParent();
  1.1043 +  }
  1.1044 +
  1.1045 +  return aContent ? nsContentUtils::IsFocusedContent(aContent) : false;
  1.1046 +}
  1.1047 +
  1.1048 +bool
  1.1049 +ScrollFrameHelper::WantAsyncScroll() const
  1.1050 +{
  1.1051 +  nsRect scrollRange = GetScrollRange();
  1.1052 +  ScrollbarStyles styles = GetScrollbarStylesFromFrame();
  1.1053 +  bool isFocused = IsFocused(mOuter->GetContent());
  1.1054 +  bool isVScrollable = (scrollRange.height > 0)
  1.1055 +                    && (styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
  1.1056 +  bool isHScrollable = (scrollRange.width > 0)
  1.1057 +                    && (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN);
  1.1058 +  // The check for scroll bars was added in bug 825692 to prevent layerization
  1.1059 +  // of text inputs for performance reasons. However, if a text input is
  1.1060 +  // focused we want to layerize it so we can async scroll it (bug 946408).
  1.1061 +  bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || isFocused);
  1.1062 +  bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || isFocused);
  1.1063 +  return isVAsyncScrollable || isHAsyncScrollable;
  1.1064 +}
  1.1065 +
  1.1066 +nsresult
  1.1067 +nsXULScrollFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
  1.1068 +{
  1.1069 +  return mHelper.CreateAnonymousContent(aElements);
  1.1070 +}
  1.1071 +
  1.1072 +void
  1.1073 +nsXULScrollFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
  1.1074 +                                           uint32_t aFilter)
  1.1075 +{
  1.1076 +  mHelper.AppendAnonymousContentTo(aElements, aFilter);
  1.1077 +}
  1.1078 +
  1.1079 +void
  1.1080 +nsXULScrollFrame::DestroyFrom(nsIFrame* aDestructRoot)
  1.1081 +{
  1.1082 +  mHelper.Destroy();
  1.1083 +  nsBoxFrame::DestroyFrom(aDestructRoot);
  1.1084 +}
  1.1085 +
  1.1086 +nsresult
  1.1087 +nsXULScrollFrame::SetInitialChildList(ChildListID     aListID,
  1.1088 +                                      nsFrameList&    aChildList)
  1.1089 +{
  1.1090 +  nsresult rv = nsBoxFrame::SetInitialChildList(aListID, aChildList);
  1.1091 +  mHelper.ReloadChildFrames();
  1.1092 +  return rv;
  1.1093 +}
  1.1094 +
  1.1095 +
  1.1096 +nsresult
  1.1097 +nsXULScrollFrame::AppendFrames(ChildListID     aListID,
  1.1098 +                               nsFrameList&    aFrameList)
  1.1099 +{
  1.1100 +  nsresult rv = nsBoxFrame::AppendFrames(aListID, aFrameList);
  1.1101 +  mHelper.ReloadChildFrames();
  1.1102 +  return rv;
  1.1103 +}
  1.1104 +
  1.1105 +nsresult
  1.1106 +nsXULScrollFrame::InsertFrames(ChildListID     aListID,
  1.1107 +                               nsIFrame*       aPrevFrame,
  1.1108 +                               nsFrameList&    aFrameList)
  1.1109 +{
  1.1110 +  nsresult rv = nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
  1.1111 +  mHelper.ReloadChildFrames();
  1.1112 +  return rv;
  1.1113 +}
  1.1114 +
  1.1115 +nsresult
  1.1116 +nsXULScrollFrame::RemoveFrame(ChildListID     aListID,
  1.1117 +                              nsIFrame*       aOldFrame)
  1.1118 +{
  1.1119 +  nsresult rv = nsBoxFrame::RemoveFrame(aListID, aOldFrame);
  1.1120 +  mHelper.ReloadChildFrames();
  1.1121 +  return rv;
  1.1122 +}
  1.1123 +
  1.1124 +nsSplittableType
  1.1125 +nsXULScrollFrame::GetSplittableType() const
  1.1126 +{
  1.1127 +  return NS_FRAME_NOT_SPLITTABLE;
  1.1128 +}
  1.1129 +
  1.1130 +nsresult
  1.1131 +nsXULScrollFrame::GetPadding(nsMargin& aMargin)
  1.1132 +{
  1.1133 +  aMargin.SizeTo(0,0,0,0);
  1.1134 +  return NS_OK;
  1.1135 +}
  1.1136 +
  1.1137 +nsIAtom*
  1.1138 +nsXULScrollFrame::GetType() const
  1.1139 +{
  1.1140 +  return nsGkAtoms::scrollFrame;
  1.1141 +}
  1.1142 +
  1.1143 +nscoord
  1.1144 +nsXULScrollFrame::GetBoxAscent(nsBoxLayoutState& aState)
  1.1145 +{
  1.1146 +  if (!mHelper.mScrolledFrame)
  1.1147 +    return 0;
  1.1148 +
  1.1149 +  nscoord ascent = mHelper.mScrolledFrame->GetBoxAscent(aState);
  1.1150 +  nsMargin m(0,0,0,0);
  1.1151 +  GetBorderAndPadding(m);
  1.1152 +  ascent += m.top;
  1.1153 +  GetMargin(m);
  1.1154 +  ascent += m.top;
  1.1155 +
  1.1156 +  return ascent;
  1.1157 +}
  1.1158 +
  1.1159 +nsSize
  1.1160 +nsXULScrollFrame::GetPrefSize(nsBoxLayoutState& aState)
  1.1161 +{
  1.1162 +#ifdef DEBUG_LAYOUT
  1.1163 +  PropagateDebug(aState);
  1.1164 +#endif
  1.1165 +
  1.1166 +  nsSize pref = mHelper.mScrolledFrame->GetPrefSize(aState);
  1.1167 +
  1.1168 +  ScrollbarStyles styles = GetScrollbarStyles();
  1.1169 +
  1.1170 +  // scrolled frames don't have their own margins
  1.1171 +
  1.1172 +  if (mHelper.mVScrollbarBox &&
  1.1173 +      styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
  1.1174 +    nsSize vSize = mHelper.mVScrollbarBox->GetPrefSize(aState);
  1.1175 +    nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
  1.1176 +    pref.width += vSize.width;
  1.1177 +  }
  1.1178 +
  1.1179 +  if (mHelper.mHScrollbarBox &&
  1.1180 +      styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
  1.1181 +    nsSize hSize = mHelper.mHScrollbarBox->GetPrefSize(aState);
  1.1182 +    nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
  1.1183 +    pref.height += hSize.height;
  1.1184 +  }
  1.1185 +
  1.1186 +  AddBorderAndPadding(pref);
  1.1187 +  bool widthSet, heightSet;
  1.1188 +  nsIFrame::AddCSSPrefSize(this, pref, widthSet, heightSet);
  1.1189 +  return pref;
  1.1190 +}
  1.1191 +
  1.1192 +nsSize
  1.1193 +nsXULScrollFrame::GetMinSize(nsBoxLayoutState& aState)
  1.1194 +{
  1.1195 +#ifdef DEBUG_LAYOUT
  1.1196 +  PropagateDebug(aState);
  1.1197 +#endif
  1.1198 +
  1.1199 +  nsSize min = mHelper.mScrolledFrame->GetMinSizeForScrollArea(aState);
  1.1200 +
  1.1201 +  ScrollbarStyles styles = GetScrollbarStyles();
  1.1202 +
  1.1203 +  if (mHelper.mVScrollbarBox &&
  1.1204 +      styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
  1.1205 +     nsSize vSize = mHelper.mVScrollbarBox->GetMinSize(aState);
  1.1206 +     AddMargin(mHelper.mVScrollbarBox, vSize);
  1.1207 +     min.width += vSize.width;
  1.1208 +     if (min.height < vSize.height)
  1.1209 +        min.height = vSize.height;
  1.1210 +  }
  1.1211 +
  1.1212 +  if (mHelper.mHScrollbarBox &&
  1.1213 +      styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
  1.1214 +     nsSize hSize = mHelper.mHScrollbarBox->GetMinSize(aState);
  1.1215 +     AddMargin(mHelper.mHScrollbarBox, hSize);
  1.1216 +     min.height += hSize.height;
  1.1217 +     if (min.width < hSize.width)
  1.1218 +        min.width = hSize.width;
  1.1219 +  }
  1.1220 +
  1.1221 +  AddBorderAndPadding(min);
  1.1222 +  bool widthSet, heightSet;
  1.1223 +  nsIFrame::AddCSSMinSize(aState, this, min, widthSet, heightSet);
  1.1224 +  return min;
  1.1225 +}
  1.1226 +
  1.1227 +nsSize
  1.1228 +nsXULScrollFrame::GetMaxSize(nsBoxLayoutState& aState)
  1.1229 +{
  1.1230 +#ifdef DEBUG_LAYOUT
  1.1231 +  PropagateDebug(aState);
  1.1232 +#endif
  1.1233 +
  1.1234 +  nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
  1.1235 +
  1.1236 +  AddBorderAndPadding(maxSize);
  1.1237 +  bool widthSet, heightSet;
  1.1238 +  nsIFrame::AddCSSMaxSize(this, maxSize, widthSet, heightSet);
  1.1239 +  return maxSize;
  1.1240 +}
  1.1241 +
  1.1242 +#ifdef DEBUG_FRAME_DUMP
  1.1243 +nsresult
  1.1244 +nsXULScrollFrame::GetFrameName(nsAString& aResult) const
  1.1245 +{
  1.1246 +  return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult);
  1.1247 +}
  1.1248 +#endif
  1.1249 +
  1.1250 +NS_IMETHODIMP
  1.1251 +nsXULScrollFrame::DoLayout(nsBoxLayoutState& aState)
  1.1252 +{
  1.1253 +  uint32_t flags = aState.LayoutFlags();
  1.1254 +  nsresult rv = Layout(aState);
  1.1255 +  aState.SetLayoutFlags(flags);
  1.1256 +
  1.1257 +  nsBox::DoLayout(aState);
  1.1258 +  return rv;
  1.1259 +}
  1.1260 +
  1.1261 +NS_QUERYFRAME_HEAD(nsXULScrollFrame)
  1.1262 +  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
  1.1263 +  NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
  1.1264 +  NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
  1.1265 +  NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
  1.1266 +NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
  1.1267 +
  1.1268 +//-------------------- Helper ----------------------
  1.1269 +
  1.1270 +#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
  1.1271 +
  1.1272 +const double kCurrentVelocityWeighting = 0.25;
  1.1273 +const double kStopDecelerationWeighting = 0.4;
  1.1274 +
  1.1275 +// AsyncScroll has ref counting.
  1.1276 +class ScrollFrameHelper::AsyncScroll MOZ_FINAL : public nsARefreshObserver {
  1.1277 +public:
  1.1278 +  typedef mozilla::TimeStamp TimeStamp;
  1.1279 +  typedef mozilla::TimeDuration TimeDuration;
  1.1280 +
  1.1281 +  AsyncScroll(nsPoint aStartPos)
  1.1282 +    : mIsFirstIteration(true)
  1.1283 +    , mStartPos(aStartPos)
  1.1284 +    , mCallee(nullptr)
  1.1285 +  {}
  1.1286 +
  1.1287 +private:
  1.1288 +  // Private destructor, to discourage deletion outside of Release():
  1.1289 +  ~AsyncScroll() {
  1.1290 +    RemoveObserver();
  1.1291 +  }
  1.1292 +
  1.1293 +public:
  1.1294 +  nsPoint PositionAt(TimeStamp aTime);
  1.1295 +  nsSize VelocityAt(TimeStamp aTime); // In nscoords per second
  1.1296 +
  1.1297 +  void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
  1.1298 +                        nsIAtom *aOrigin, const nsRect& aRange);
  1.1299 +  void Init(const nsRect& aRange) {
  1.1300 +    mRange = aRange;
  1.1301 +  }
  1.1302 +
  1.1303 +  bool IsFinished(TimeStamp aTime) {
  1.1304 +    return aTime > mStartTime + mDuration; // XXX or if we've hit the wall
  1.1305 +  }
  1.1306 +
  1.1307 +  TimeStamp mStartTime;
  1.1308 +
  1.1309 +  // mPrevEventTime holds previous 3 timestamps for intervals averaging (to
  1.1310 +  // reduce duration fluctuations). When AsyncScroll is constructed and no
  1.1311 +  // previous timestamps are available (indicated with mIsFirstIteration),
  1.1312 +  // initialize mPrevEventTime using imaginary previous timestamps with maximum
  1.1313 +  // relevant intervals between them.
  1.1314 +  TimeStamp mPrevEventTime[3];
  1.1315 +  bool mIsFirstIteration;
  1.1316 +
  1.1317 +  // Cached Preferences values to avoid re-reading them when extending an existing
  1.1318 +  // animation for the same event origin (can be as frequent as every 10(!)ms for
  1.1319 +  // a quick roll of the mouse wheel).
  1.1320 +  // These values are minimum and maximum animation duration per event origin,
  1.1321 +  // and a global ratio which defines how longer is the animation's duration
  1.1322 +  // compared to the average recent events intervals (such that for a relatively
  1.1323 +  // consistent events rate, the next event arrives before current animation ends)
  1.1324 +  nsCOMPtr<nsIAtom> mOrigin;
  1.1325 +  int32_t mOriginMinMS;
  1.1326 +  int32_t mOriginMaxMS;
  1.1327 +  double  mIntervalRatio;
  1.1328 +
  1.1329 +  TimeDuration mDuration;
  1.1330 +  nsPoint mStartPos;
  1.1331 +  nsPoint mDestination;
  1.1332 +  // Allowed destination positions around mDestination
  1.1333 +  nsRect mRange;
  1.1334 +  nsSMILKeySpline mTimingFunctionX;
  1.1335 +  nsSMILKeySpline mTimingFunctionY;
  1.1336 +  bool mIsSmoothScroll;
  1.1337 +
  1.1338 +protected:
  1.1339 +  double ProgressAt(TimeStamp aTime) {
  1.1340 +    return clamped((aTime - mStartTime) / mDuration, 0.0, 1.0);
  1.1341 +  }
  1.1342 +
  1.1343 +  nscoord VelocityComponent(double aTimeProgress,
  1.1344 +                            nsSMILKeySpline& aTimingFunction,
  1.1345 +                            nscoord aStart, nscoord aDestination);
  1.1346 +
  1.1347 +  // Initializes the timing function in such a way that the current velocity is
  1.1348 +  // preserved.
  1.1349 +  void InitTimingFunction(nsSMILKeySpline& aTimingFunction,
  1.1350 +                          nscoord aCurrentPos, nscoord aCurrentVelocity,
  1.1351 +                          nscoord aDestination);
  1.1352 +
  1.1353 +  TimeDuration CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin);
  1.1354 +
  1.1355 +// The next section is observer/callback management
  1.1356 +// Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
  1.1357 +public:
  1.1358 +  NS_INLINE_DECL_REFCOUNTING(AsyncScroll)
  1.1359 +
  1.1360 +  /*
  1.1361 +   * Set a refresh observer for smooth scroll iterations (and start observing).
  1.1362 +   * Should be used at most once during the lifetime of this object.
  1.1363 +   * Return value: true on success, false otherwise.
  1.1364 +   */
  1.1365 +  bool SetRefreshObserver(ScrollFrameHelper *aCallee) {
  1.1366 +    NS_ASSERTION(aCallee && !mCallee, "AsyncScroll::SetRefreshObserver - Invalid usage.");
  1.1367 +
  1.1368 +    if (!RefreshDriver(aCallee)->AddRefreshObserver(this, Flush_Style)) {
  1.1369 +      return false;
  1.1370 +    }
  1.1371 +
  1.1372 +    mCallee = aCallee;
  1.1373 +    return true;
  1.1374 +  }
  1.1375 +
  1.1376 +  virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE {
  1.1377 +    // The callback may release "this".
  1.1378 +    // We don't access members after returning, so no need for KungFuDeathGrip.
  1.1379 +    ScrollFrameHelper::AsyncScrollCallback(mCallee, aTime);
  1.1380 +  }
  1.1381 +
  1.1382 +private:
  1.1383 +  ScrollFrameHelper *mCallee;
  1.1384 +
  1.1385 +  nsRefreshDriver* RefreshDriver(ScrollFrameHelper* aCallee) {
  1.1386 +    return aCallee->mOuter->PresContext()->RefreshDriver();
  1.1387 +  }
  1.1388 +
  1.1389 +  /*
  1.1390 +   * The refresh driver doesn't hold a reference to its observers,
  1.1391 +   *   so releasing this object can (and is) used to remove the observer on DTOR.
  1.1392 +   * Currently, this object is released once the scrolling ends.
  1.1393 +   */
  1.1394 +  void RemoveObserver() {
  1.1395 +    if (mCallee) {
  1.1396 +      RefreshDriver(mCallee)->RemoveRefreshObserver(this, Flush_Style);
  1.1397 +    }
  1.1398 +  }
  1.1399 +};
  1.1400 +
  1.1401 +nsPoint
  1.1402 +ScrollFrameHelper::AsyncScroll::PositionAt(TimeStamp aTime) {
  1.1403 +  double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
  1.1404 +  double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
  1.1405 +  return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x + progressX * mDestination.x),
  1.1406 +                 NSToCoordRound((1 - progressY) * mStartPos.y + progressY * mDestination.y));
  1.1407 +}
  1.1408 +
  1.1409 +nsSize
  1.1410 +ScrollFrameHelper::AsyncScroll::VelocityAt(TimeStamp aTime) {
  1.1411 +  double timeProgress = ProgressAt(aTime);
  1.1412 +  return nsSize(VelocityComponent(timeProgress, mTimingFunctionX,
  1.1413 +                                  mStartPos.x, mDestination.x),
  1.1414 +                VelocityComponent(timeProgress, mTimingFunctionY,
  1.1415 +                                  mStartPos.y, mDestination.y));
  1.1416 +}
  1.1417 +
  1.1418 +/*
  1.1419 + * Calculate duration, possibly dynamically according to events rate and event origin.
  1.1420 + * (also maintain previous timestamps - which are only used here).
  1.1421 + */
  1.1422 +TimeDuration
  1.1423 +ScrollFrameHelper::
  1.1424 +AsyncScroll::CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin) {
  1.1425 +  if (!aOrigin){
  1.1426 +    aOrigin = nsGkAtoms::other;
  1.1427 +  }
  1.1428 +
  1.1429 +  // Read preferences only on first iteration or for a different event origin.
  1.1430 +  if (mIsFirstIteration || aOrigin != mOrigin) {
  1.1431 +    mOrigin = aOrigin;
  1.1432 +    mOriginMinMS = mOriginMaxMS = 0;
  1.1433 +    bool isOriginSmoothnessEnabled = false;
  1.1434 +    mIntervalRatio = 1;
  1.1435 +
  1.1436 +    // Default values for all preferences are defined in all.js
  1.1437 +    static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
  1.1438 +    static const bool kDefaultIsSmoothEnabled = true;
  1.1439 +
  1.1440 +    nsAutoCString originName;
  1.1441 +    aOrigin->ToUTF8String(originName);
  1.1442 +    nsAutoCString prefBase = NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
  1.1443 +
  1.1444 +    isOriginSmoothnessEnabled = Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
  1.1445 +    if (isOriginSmoothnessEnabled) {
  1.1446 +      nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
  1.1447 +      nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
  1.1448 +      mOriginMinMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
  1.1449 +      mOriginMaxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
  1.1450 +
  1.1451 +      static const int32_t kSmoothScrollMaxAllowedAnimationDurationMS = 10000;
  1.1452 +      mOriginMaxMS = clamped(mOriginMaxMS, 0, kSmoothScrollMaxAllowedAnimationDurationMS);
  1.1453 +      mOriginMinMS = clamped(mOriginMinMS, 0, mOriginMaxMS);
  1.1454 +    }
  1.1455 +
  1.1456 +    // Keep the animation duration longer than the average event intervals
  1.1457 +    //   (to "connect" consecutive scroll animations before the scroll comes to a stop).
  1.1458 +    static const double kDefaultDurationToIntervalRatio = 2; // Duplicated at all.js
  1.1459 +    mIntervalRatio = Preferences::GetInt("general.smoothScroll.durationToIntervalRatio",
  1.1460 +                                         kDefaultDurationToIntervalRatio * 100) / 100.0;
  1.1461 +
  1.1462 +    // Duration should be at least as long as the intervals -> ratio is at least 1
  1.1463 +    mIntervalRatio = std::max(1.0, mIntervalRatio);
  1.1464 +
  1.1465 +    if (mIsFirstIteration) {
  1.1466 +      // Starting a new scroll (i.e. not when extending an existing scroll animation),
  1.1467 +      //   create imaginary prev timestamps with maximum relevant intervals between them.
  1.1468 +
  1.1469 +      // Longest relevant interval (which results in maximum duration)
  1.1470 +      TimeDuration maxDelta = TimeDuration::FromMilliseconds(mOriginMaxMS / mIntervalRatio);
  1.1471 +      mPrevEventTime[0] = aTime              - maxDelta;
  1.1472 +      mPrevEventTime[1] = mPrevEventTime[0]  - maxDelta;
  1.1473 +      mPrevEventTime[2] = mPrevEventTime[1]  - maxDelta;
  1.1474 +    }
  1.1475 +  }
  1.1476 +
  1.1477 +  // Average last 3 delta durations (rounding errors up to 2ms are negligible for us)
  1.1478 +  int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
  1.1479 +  mPrevEventTime[2] = mPrevEventTime[1];
  1.1480 +  mPrevEventTime[1] = mPrevEventTime[0];
  1.1481 +  mPrevEventTime[0] = aTime;
  1.1482 +
  1.1483 +  // Modulate duration according to events rate (quicker events -> shorter durations).
  1.1484 +  // The desired effect is to use longer duration when scrolling slowly, such that
  1.1485 +  // it's easier to follow, but reduce the duration to make it feel more snappy when
  1.1486 +  // scrolling quickly. To reduce fluctuations of the duration, we average event
  1.1487 +  // intervals using the recent 4 timestamps (now + three prev -> 3 intervals).
  1.1488 +  int32_t durationMS = clamped<int32_t>(eventsDeltaMs * mIntervalRatio, mOriginMinMS, mOriginMaxMS);
  1.1489 +
  1.1490 +  return TimeDuration::FromMilliseconds(durationMS);
  1.1491 +}
  1.1492 +
  1.1493 +void
  1.1494 +ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
  1.1495 +                                                     nsPoint aDestination,
  1.1496 +                                                     nsIAtom *aOrigin,
  1.1497 +                                                     const nsRect& aRange) {
  1.1498 +  mRange = aRange;
  1.1499 +  TimeDuration duration = CalcDurationForEventTime(aTime, aOrigin);
  1.1500 +  nsSize currentVelocity(0, 0);
  1.1501 +  if (!mIsFirstIteration) {
  1.1502 +    // If an additional event has not changed the destination, then do not let
  1.1503 +    // another minimum duration reset slow things down.  If it would then
  1.1504 +    // instead continue with the existing timing function.
  1.1505 +    if (aDestination == mDestination &&
  1.1506 +        aTime + duration > mStartTime + mDuration)
  1.1507 +      return;
  1.1508 +
  1.1509 +    currentVelocity = VelocityAt(aTime);
  1.1510 +    mStartPos = PositionAt(aTime);
  1.1511 +  }
  1.1512 +  mStartTime = aTime;
  1.1513 +  mDuration = duration;
  1.1514 +  mDestination = aDestination;
  1.1515 +  InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
  1.1516 +                     aDestination.x);
  1.1517 +  InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
  1.1518 +                     aDestination.y);
  1.1519 +  mIsFirstIteration = false;
  1.1520 +}
  1.1521 +
  1.1522 +
  1.1523 +nscoord
  1.1524 +ScrollFrameHelper::AsyncScroll::VelocityComponent(double aTimeProgress,
  1.1525 +                                                      nsSMILKeySpline& aTimingFunction,
  1.1526 +                                                      nscoord aStart,
  1.1527 +                                                      nscoord aDestination)
  1.1528 +{
  1.1529 +  double dt, dxy;
  1.1530 +  aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
  1.1531 +  if (dt == 0)
  1.1532 +    return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
  1.1533 +
  1.1534 +  const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
  1.1535 +  double slope = dxy / dt;
  1.1536 +  return NSToCoordRound(slope * (aDestination - aStart) / (mDuration / oneSecond));
  1.1537 +}
  1.1538 +
  1.1539 +void
  1.1540 +ScrollFrameHelper::AsyncScroll::InitTimingFunction(nsSMILKeySpline& aTimingFunction,
  1.1541 +                                                       nscoord aCurrentPos,
  1.1542 +                                                       nscoord aCurrentVelocity,
  1.1543 +                                                       nscoord aDestination)
  1.1544 +{
  1.1545 +  if (aDestination == aCurrentPos || kCurrentVelocityWeighting == 0) {
  1.1546 +    aTimingFunction.Init(0, 0, 1 - kStopDecelerationWeighting, 1);
  1.1547 +    return;
  1.1548 +  }
  1.1549 +
  1.1550 +  const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
  1.1551 +  double slope = aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
  1.1552 +  double normalization = sqrt(1.0 + slope * slope);
  1.1553 +  double dt = 1.0 / normalization * kCurrentVelocityWeighting;
  1.1554 +  double dxy = slope / normalization * kCurrentVelocityWeighting;
  1.1555 +  aTimingFunction.Init(dt, dxy, 1 - kStopDecelerationWeighting, 1);
  1.1556 +}
  1.1557 +
  1.1558 +static bool
  1.1559 +IsSmoothScrollingEnabled()
  1.1560 +{
  1.1561 +  return Preferences::GetBool(SMOOTH_SCROLL_PREF_NAME, false);
  1.1562 +}
  1.1563 +
  1.1564 +class ScrollFrameActivityTracker MOZ_FINAL : public nsExpirationTracker<ScrollFrameHelper,4> {
  1.1565 +public:
  1.1566 +  // Wait for 3-4s between scrolls before we remove our layers.
  1.1567 +  // That's 4 generations of 1s each.
  1.1568 +  enum { TIMEOUT_MS = 1000 };
  1.1569 +  ScrollFrameActivityTracker()
  1.1570 +    : nsExpirationTracker<ScrollFrameHelper,4>(TIMEOUT_MS) {}
  1.1571 +  ~ScrollFrameActivityTracker() {
  1.1572 +    AgeAllGenerations();
  1.1573 +  }
  1.1574 +
  1.1575 +  virtual void NotifyExpired(ScrollFrameHelper *aObject) {
  1.1576 +    RemoveObject(aObject);
  1.1577 +    aObject->MarkInactive();
  1.1578 +  }
  1.1579 +};
  1.1580 +
  1.1581 +static ScrollFrameActivityTracker *gScrollFrameActivityTracker = nullptr;
  1.1582 +
  1.1583 +// There are situations when a scroll frame is destroyed and then re-created
  1.1584 +// for the same content element. In this case we want to increment the scroll
  1.1585 +// generation between the old and new scrollframes. If the new one knew about
  1.1586 +// the old one then it could steal the old generation counter and increment it
  1.1587 +// but it doesn't have that reference so instead we use a static global to
  1.1588 +// ensure the new one gets a fresh value.
  1.1589 +static uint32_t sScrollGenerationCounter = 0;
  1.1590 +
  1.1591 +ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
  1.1592 +                                             bool aIsRoot)
  1.1593 +  : mHScrollbarBox(nullptr)
  1.1594 +  , mVScrollbarBox(nullptr)
  1.1595 +  , mScrolledFrame(nullptr)
  1.1596 +  , mScrollCornerBox(nullptr)
  1.1597 +  , mResizerBox(nullptr)
  1.1598 +  , mOuter(aOuter)
  1.1599 +  , mAsyncScroll(nullptr)
  1.1600 +  , mOriginOfLastScroll(nsGkAtoms::other)
  1.1601 +  , mScrollGeneration(++sScrollGenerationCounter)
  1.1602 +  , mDestination(0, 0)
  1.1603 +  , mScrollPosAtLastPaint(0, 0)
  1.1604 +  , mRestorePos(-1, -1)
  1.1605 +  , mLastPos(-1, -1)
  1.1606 +  , mResolution(1.0, 1.0)
  1.1607 +  , mScrollPosForLayerPixelAlignment(-1, -1)
  1.1608 +  , mLastUpdateImagesPos(-1, -1)
  1.1609 +  , mNeverHasVerticalScrollbar(false)
  1.1610 +  , mNeverHasHorizontalScrollbar(false)
  1.1611 +  , mHasVerticalScrollbar(false)
  1.1612 +  , mHasHorizontalScrollbar(false)
  1.1613 +  , mFrameIsUpdatingScrollbar(false)
  1.1614 +  , mDidHistoryRestore(false)
  1.1615 +  , mIsRoot(aIsRoot)
  1.1616 +  , mClipAllDescendants(aIsRoot)
  1.1617 +  , mSupppressScrollbarUpdate(false)
  1.1618 +  , mSkippedScrollbarLayout(false)
  1.1619 +  , mHadNonInitialReflow(false)
  1.1620 +  , mHorizontalOverflow(false)
  1.1621 +  , mVerticalOverflow(false)
  1.1622 +  , mPostedReflowCallback(false)
  1.1623 +  , mMayHaveDirtyFixedChildren(false)
  1.1624 +  , mUpdateScrollbarAttributes(false)
  1.1625 +  , mCollapsedResizer(false)
  1.1626 +  , mShouldBuildScrollableLayer(false)
  1.1627 +  , mHasBeenScrolled(false)
  1.1628 +  , mIsResolutionSet(false)
  1.1629 +{
  1.1630 +  mScrollingActive = IsAlwaysActive();
  1.1631 +
  1.1632 +  if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
  1.1633 +    mScrollbarActivity = new ScrollbarActivity(do_QueryFrame(aOuter));
  1.1634 +  }
  1.1635 +
  1.1636 +  EnsureImageVisPrefsCached();
  1.1637 +}
  1.1638 +
  1.1639 +ScrollFrameHelper::~ScrollFrameHelper()
  1.1640 +{
  1.1641 +  if (mActivityExpirationState.IsTracked()) {
  1.1642 +    gScrollFrameActivityTracker->RemoveObject(this);
  1.1643 +  }
  1.1644 +  if (gScrollFrameActivityTracker &&
  1.1645 +      gScrollFrameActivityTracker->IsEmpty()) {
  1.1646 +    delete gScrollFrameActivityTracker;
  1.1647 +    gScrollFrameActivityTracker = nullptr;
  1.1648 +  }
  1.1649 +
  1.1650 +  if (mScrollActivityTimer) {
  1.1651 +    mScrollActivityTimer->Cancel();
  1.1652 +    mScrollActivityTimer = nullptr;
  1.1653 +  }
  1.1654 +}
  1.1655 +
  1.1656 +/*
  1.1657 + * Callback function from AsyncScroll, used in ScrollFrameHelper::ScrollTo
  1.1658 + */
  1.1659 +void
  1.1660 +ScrollFrameHelper::AsyncScrollCallback(void* anInstance, mozilla::TimeStamp aTime)
  1.1661 +{
  1.1662 +  ScrollFrameHelper* self = static_cast<ScrollFrameHelper*>(anInstance);
  1.1663 +  if (!self || !self->mAsyncScroll)
  1.1664 +    return;
  1.1665 +
  1.1666 +  nsRect range = self->mAsyncScroll->mRange;
  1.1667 +  if (self->mAsyncScroll->mIsSmoothScroll) {
  1.1668 +    if (!self->mAsyncScroll->IsFinished(aTime)) {
  1.1669 +      nsPoint destination = self->mAsyncScroll->PositionAt(aTime);
  1.1670 +      // Allow this scroll operation to land on any pixel boundary between the
  1.1671 +      // current position and the final allowed range.  (We don't want
  1.1672 +      // intermediate steps to be more constrained than the final step!)
  1.1673 +      nsRect intermediateRange =
  1.1674 +        nsRect(self->GetScrollPosition(), nsSize()).UnionEdges(range);
  1.1675 +      self->ScrollToImpl(destination, intermediateRange);
  1.1676 +      // 'self' might be destroyed here
  1.1677 +      return;
  1.1678 +    }
  1.1679 +  }
  1.1680 +
  1.1681 +  // Apply desired destination range since this is the last step of scrolling.
  1.1682 +  self->mAsyncScroll = nullptr;
  1.1683 +  nsWeakFrame weakFrame(self->mOuter);
  1.1684 +  self->ScrollToImpl(self->mDestination, range);
  1.1685 +  if (!weakFrame.IsAlive()) {
  1.1686 +    return;
  1.1687 +  }
  1.1688 +  // We are done scrolling, set our destination to wherever we actually ended
  1.1689 +  // up scrolling to.
  1.1690 +  self->mDestination = self->GetScrollPosition();
  1.1691 +}
  1.1692 +
  1.1693 +void
  1.1694 +ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition)
  1.1695 +{
  1.1696 +  nsPoint current = GetScrollPosition();
  1.1697 +  CSSIntPoint currentCSSPixels = GetScrollPositionCSSPixels();
  1.1698 +  nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
  1.1699 +  nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
  1.1700 +  nsRect range(pt.x - halfPixel, pt.y - halfPixel, 2*halfPixel - 1, 2*halfPixel - 1);
  1.1701 +  // XXX I don't think the following blocks are needed anymore, now that
  1.1702 +  // ScrollToImpl simply tries to scroll an integer number of layer
  1.1703 +  // pixels from the current position
  1.1704 +  if (currentCSSPixels.x == aScrollPosition.x) {
  1.1705 +    pt.x = current.x;
  1.1706 +    range.x = pt.x;
  1.1707 +    range.width = 0;
  1.1708 +  }
  1.1709 +  if (currentCSSPixels.y == aScrollPosition.y) {
  1.1710 +    pt.y = current.y;
  1.1711 +    range.y = pt.y;
  1.1712 +    range.height = 0;
  1.1713 +  }
  1.1714 +  ScrollTo(pt, nsIScrollableFrame::INSTANT, &range);
  1.1715 +  // 'this' might be destroyed here
  1.1716 +}
  1.1717 +
  1.1718 +void
  1.1719 +ScrollFrameHelper::ScrollToCSSPixelsApproximate(const CSSPoint& aScrollPosition,
  1.1720 +                                                nsIAtom *aOrigin)
  1.1721 +{
  1.1722 +  nsPoint pt = CSSPoint::ToAppUnits(aScrollPosition);
  1.1723 +  nscoord halfRange = nsPresContext::CSSPixelsToAppUnits(1000);
  1.1724 +  nsRect range(pt.x - halfRange, pt.y - halfRange, 2*halfRange - 1, 2*halfRange - 1);
  1.1725 +  ScrollToWithOrigin(pt, nsIScrollableFrame::INSTANT, aOrigin, &range);
  1.1726 +  // 'this' might be destroyed here
  1.1727 +}
  1.1728 +
  1.1729 +CSSIntPoint
  1.1730 +ScrollFrameHelper::GetScrollPositionCSSPixels()
  1.1731 +{
  1.1732 +  return CSSIntPoint::FromAppUnitsRounded(GetScrollPosition());
  1.1733 +}
  1.1734 +
  1.1735 +/*
  1.1736 + * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
  1.1737 + *  based on the setting of the smoothness scroll pref
  1.1738 + */
  1.1739 +void
  1.1740 +ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
  1.1741 +                                          nsIScrollableFrame::ScrollMode aMode,
  1.1742 +                                          nsIAtom *aOrigin,
  1.1743 +                                          const nsRect* aRange)
  1.1744 +{
  1.1745 +  nsRect scrollRange = GetScrollRangeForClamping();
  1.1746 +  mDestination = scrollRange.ClampPoint(aScrollPosition);
  1.1747 +
  1.1748 +  nsRect range = aRange ? *aRange : nsRect(aScrollPosition, nsSize(0, 0));
  1.1749 +
  1.1750 +  if (aMode == nsIScrollableFrame::INSTANT) {
  1.1751 +    // Asynchronous scrolling is not allowed, so we'll kill any existing
  1.1752 +    // async-scrolling process and do an instant scroll.
  1.1753 +    mAsyncScroll = nullptr;
  1.1754 +    nsWeakFrame weakFrame(mOuter);
  1.1755 +    ScrollToImpl(mDestination, range, aOrigin);
  1.1756 +    if (!weakFrame.IsAlive()) {
  1.1757 +      return;
  1.1758 +    }
  1.1759 +    // We are done scrolling, set our destination to wherever we actually ended
  1.1760 +    // up scrolling to.
  1.1761 +    mDestination = GetScrollPosition();
  1.1762 +    return;
  1.1763 +  }
  1.1764 +
  1.1765 +  TimeStamp now = TimeStamp::Now();
  1.1766 +  bool isSmoothScroll = (aMode == nsIScrollableFrame::SMOOTH) &&
  1.1767 +                          IsSmoothScrollingEnabled();
  1.1768 +
  1.1769 +  if (!mAsyncScroll) {
  1.1770 +    mAsyncScroll = new AsyncScroll(GetScrollPosition());
  1.1771 +    if (!mAsyncScroll->SetRefreshObserver(this)) {
  1.1772 +      mAsyncScroll = nullptr;
  1.1773 +      // Observer setup failed. Scroll the normal way.
  1.1774 +      nsWeakFrame weakFrame(mOuter);
  1.1775 +      ScrollToImpl(mDestination, range, aOrigin);
  1.1776 +      if (!weakFrame.IsAlive()) {
  1.1777 +        return;
  1.1778 +      }
  1.1779 +      // We are done scrolling, set our destination to wherever we actually
  1.1780 +      // ended up scrolling to.
  1.1781 +      mDestination = GetScrollPosition();
  1.1782 +      return;
  1.1783 +    }
  1.1784 +  }
  1.1785 +
  1.1786 +  mAsyncScroll->mIsSmoothScroll = isSmoothScroll;
  1.1787 +
  1.1788 +  if (isSmoothScroll) {
  1.1789 +    mAsyncScroll->InitSmoothScroll(now, mDestination, aOrigin, range);
  1.1790 +  } else {
  1.1791 +    mAsyncScroll->Init(range);
  1.1792 +  }
  1.1793 +}
  1.1794 +
  1.1795 +// We can't use nsContainerFrame::PositionChildViews here because
  1.1796 +// we don't want to invalidate views that have moved.
  1.1797 +static void AdjustViews(nsIFrame* aFrame)
  1.1798 +{
  1.1799 +  nsView* view = aFrame->GetView();
  1.1800 +  if (view) {
  1.1801 +    nsPoint pt;
  1.1802 +    aFrame->GetParent()->GetClosestView(&pt);
  1.1803 +    pt += aFrame->GetPosition();
  1.1804 +    view->SetPosition(pt.x, pt.y);
  1.1805 +
  1.1806 +    return;
  1.1807 +  }
  1.1808 +
  1.1809 +  if (!(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
  1.1810 +    return;
  1.1811 +  }
  1.1812 +
  1.1813 +  // Call AdjustViews recursively for all child frames except the popup list as
  1.1814 +  // the views for popups are not scrolled.
  1.1815 +  nsIFrame::ChildListIterator lists(aFrame);
  1.1816 +  for (; !lists.IsDone(); lists.Next()) {
  1.1817 +    if (lists.CurrentID() == nsIFrame::kPopupList) {
  1.1818 +      continue;
  1.1819 +    }
  1.1820 +    nsFrameList::Enumerator childFrames(lists.CurrentList());
  1.1821 +    for (; !childFrames.AtEnd(); childFrames.Next()) {
  1.1822 +      AdjustViews(childFrames.get());
  1.1823 +    }
  1.1824 +  }
  1.1825 +}
  1.1826 +
  1.1827 +static bool
  1.1828 +CanScrollWithBlitting(nsIFrame* aFrame)
  1.1829 +{
  1.1830 +  if (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL)
  1.1831 +    return false;
  1.1832 +
  1.1833 +  for (nsIFrame* f = aFrame; f;
  1.1834 +       f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
  1.1835 +    if (nsSVGIntegrationUtils::UsingEffectsForFrame(f) ||
  1.1836 +        f->IsFrameOfType(nsIFrame::eSVG) ||
  1.1837 +        f->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) {
  1.1838 +      return false;
  1.1839 +    }
  1.1840 +    if (nsLayoutUtils::IsPopup(f))
  1.1841 +      break;
  1.1842 +  }
  1.1843 +  return true;
  1.1844 +}
  1.1845 +
  1.1846 +bool ScrollFrameHelper::IsIgnoringViewportClipping() const
  1.1847 +{
  1.1848 +  if (!mIsRoot)
  1.1849 +    return false;
  1.1850 +  nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
  1.1851 +    (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
  1.1852 +  return subdocFrame && !subdocFrame->ShouldClipSubdocument();
  1.1853 +}
  1.1854 +
  1.1855 +bool ScrollFrameHelper::ShouldClampScrollPosition() const
  1.1856 +{
  1.1857 +  if (!mIsRoot)
  1.1858 +    return true;
  1.1859 +  nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
  1.1860 +    (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
  1.1861 +  return !subdocFrame || subdocFrame->ShouldClampScrollPosition();
  1.1862 +}
  1.1863 +
  1.1864 +bool ScrollFrameHelper::IsAlwaysActive() const
  1.1865 +{
  1.1866 +  if (nsDisplayItem::ForceActiveLayers()) {
  1.1867 +    return true;
  1.1868 +  }
  1.1869 +
  1.1870 +  const nsStyleDisplay* disp = mOuter->StyleDisplay();
  1.1871 +  if (disp && (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_SCROLL)) {
  1.1872 +    return true;
  1.1873 +  }
  1.1874 +
  1.1875 +  // Unless this is the root scrollframe for a non-chrome document
  1.1876 +  // which is the direct child of a chrome document, we default to not
  1.1877 +  // being "active".
  1.1878 +  if (!(mIsRoot && mOuter->PresContext()->IsRootContentDocument())) {
  1.1879 +     return false;
  1.1880 +  }
  1.1881 +
  1.1882 +  // If we have scrolled before, then we should stay active.
  1.1883 +  if (mHasBeenScrolled) {
  1.1884 +    return true;
  1.1885 +  }
  1.1886 +
  1.1887 +  // If we're overflow:hidden, then start as inactive until
  1.1888 +  // we get scrolled manually.
  1.1889 +  ScrollbarStyles styles = GetScrollbarStylesFromFrame();
  1.1890 +  return (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN &&
  1.1891 +          styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
  1.1892 +}
  1.1893 +
  1.1894 +void ScrollFrameHelper::MarkInactive()
  1.1895 +{
  1.1896 +  if (IsAlwaysActive() || !mScrollingActive)
  1.1897 +    return;
  1.1898 +
  1.1899 +  mScrollingActive = false;
  1.1900 +  mOuter->InvalidateFrameSubtree();
  1.1901 +}
  1.1902 +
  1.1903 +void ScrollFrameHelper::MarkActive()
  1.1904 +{
  1.1905 +  mScrollingActive = true;
  1.1906 +  if (IsAlwaysActive())
  1.1907 +    return;
  1.1908 +
  1.1909 +  if (mActivityExpirationState.IsTracked()) {
  1.1910 +    gScrollFrameActivityTracker->MarkUsed(this);
  1.1911 +  } else {
  1.1912 +    if (!gScrollFrameActivityTracker) {
  1.1913 +      gScrollFrameActivityTracker = new ScrollFrameActivityTracker();
  1.1914 +    }
  1.1915 +    gScrollFrameActivityTracker->AddObject(this);
  1.1916 +  }
  1.1917 +}
  1.1918 +
  1.1919 +void ScrollFrameHelper::ScrollVisual(nsPoint aOldScrolledFramePos)
  1.1920 +{
  1.1921 +  // Mark this frame as having been scrolled. If this is the root
  1.1922 +  // scroll frame of a content document, then IsAlwaysActive()
  1.1923 +  // will return true from now on and MarkInactive() won't
  1.1924 +  // have any effect.
  1.1925 +  mHasBeenScrolled = true;
  1.1926 +
  1.1927 +  AdjustViews(mScrolledFrame);
  1.1928 +  // We need to call this after fixing up the view positions
  1.1929 +  // to be consistent with the frame hierarchy.
  1.1930 +  bool canScrollWithBlitting = CanScrollWithBlitting(mOuter);
  1.1931 +  mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
  1.1932 +  if (IsScrollingActive()) {
  1.1933 +    if (!canScrollWithBlitting) {
  1.1934 +      MarkInactive();
  1.1935 +    }
  1.1936 +  }
  1.1937 +  if (canScrollWithBlitting) {
  1.1938 +    MarkActive();
  1.1939 +  }
  1.1940 +
  1.1941 +  mOuter->SchedulePaint();
  1.1942 +}
  1.1943 +
  1.1944 +/**
  1.1945 + * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
  1.1946 + * to [aBoundLower, aBoundUpper] and then select the appunit value from among
  1.1947 + * aBoundLower, aBoundUpper and those such that (aDesired - aCurrent) *
  1.1948 + * aRes/aAppUnitsPerPixel is an integer (or as close as we can get
  1.1949 + * modulo rounding to appunits) that is in [aDestLower, aDestUpper] and
  1.1950 + * closest to aDesired.  If no such value exists, return the nearest in
  1.1951 + * [aDestLower, aDestUpper].
  1.1952 + */
  1.1953 +static nscoord
  1.1954 +ClampAndAlignWithPixels(nscoord aDesired,
  1.1955 +                        nscoord aBoundLower, nscoord aBoundUpper,
  1.1956 +                        nscoord aDestLower, nscoord aDestUpper,
  1.1957 +                        nscoord aAppUnitsPerPixel, double aRes,
  1.1958 +                        nscoord aCurrent)
  1.1959 +{
  1.1960 +  // Intersect scroll range with allowed range, by clamping the ends
  1.1961 +  // of aRange to be within bounds
  1.1962 +  nscoord destLower = clamped(aDestLower, aBoundLower, aBoundUpper);
  1.1963 +  nscoord destUpper = clamped(aDestUpper, aBoundLower, aBoundUpper);
  1.1964 +
  1.1965 +  nscoord desired = clamped(aDesired, destLower, destUpper);
  1.1966 +
  1.1967 +  double currentLayerVal = (aRes*aCurrent)/aAppUnitsPerPixel;
  1.1968 +  double desiredLayerVal = (aRes*desired)/aAppUnitsPerPixel;
  1.1969 +  double delta = desiredLayerVal - currentLayerVal;
  1.1970 +  double nearestLayerVal = NS_round(delta) + currentLayerVal;
  1.1971 +
  1.1972 +  // Convert back from ThebesLayer space to appunits relative to the top-left
  1.1973 +  // of the scrolled frame.
  1.1974 +  nscoord aligned =
  1.1975 +    NSToCoordRoundWithClamp(nearestLayerVal*aAppUnitsPerPixel/aRes);
  1.1976 +
  1.1977 +  // Use a bound if it is within the allowed range and closer to desired than
  1.1978 +  // the nearest pixel-aligned value.
  1.1979 +  if (aBoundUpper == destUpper &&
  1.1980 +      static_cast<decltype(Abs(desired))>(aBoundUpper - desired) <
  1.1981 +      Abs(desired - aligned))
  1.1982 +    return aBoundUpper;
  1.1983 +
  1.1984 +  if (aBoundLower == destLower &&
  1.1985 +      static_cast<decltype(Abs(desired))>(desired - aBoundLower) <
  1.1986 +      Abs(aligned - desired))
  1.1987 +    return aBoundLower;
  1.1988 +
  1.1989 +  // Accept the nearest pixel-aligned value if it is within the allowed range. 
  1.1990 +  if (aligned >= destLower && aligned <= destUpper)
  1.1991 +    return aligned;
  1.1992 +
  1.1993 +  // Check if opposite pixel boundary fits into allowed range.
  1.1994 +  double oppositeLayerVal =
  1.1995 +    nearestLayerVal + ((nearestLayerVal < desiredLayerVal) ? 1.0 : -1.0);
  1.1996 +  nscoord opposite =
  1.1997 +    NSToCoordRoundWithClamp(oppositeLayerVal*aAppUnitsPerPixel/aRes);
  1.1998 +  if (opposite >= destLower && opposite <= destUpper) {
  1.1999 +    return opposite;
  1.2000 +  }
  1.2001 +
  1.2002 +  // No alignment available.
  1.2003 +  return desired;
  1.2004 +}
  1.2005 +
  1.2006 +/**
  1.2007 + * Clamp desired scroll position aPt to aBounds and then snap
  1.2008 + * it to the same layer pixel edges as aCurrent, keeping it within aRange
  1.2009 + * during snapping. aCurrent is the current scroll position.
  1.2010 + */
  1.2011 +static nsPoint
  1.2012 +ClampAndAlignWithLayerPixels(const nsPoint& aPt,
  1.2013 +                             const nsRect& aBounds,
  1.2014 +                             const nsRect& aRange,
  1.2015 +                             const nsPoint& aCurrent,
  1.2016 +                             nscoord aAppUnitsPerPixel,
  1.2017 +                             const gfxSize& aScale)
  1.2018 +{
  1.2019 +  return nsPoint(ClampAndAlignWithPixels(aPt.x, aBounds.x, aBounds.XMost(),
  1.2020 +                                         aRange.x, aRange.XMost(),
  1.2021 +                                         aAppUnitsPerPixel, aScale.width,
  1.2022 +                                         aCurrent.x),
  1.2023 +                 ClampAndAlignWithPixels(aPt.y, aBounds.y, aBounds.YMost(),
  1.2024 +                                         aRange.y, aRange.YMost(),
  1.2025 +                                         aAppUnitsPerPixel, aScale.height,
  1.2026 +                                         aCurrent.y));
  1.2027 +}
  1.2028 +
  1.2029 +/* static */ void
  1.2030 +ScrollFrameHelper::ScrollActivityCallback(nsITimer *aTimer, void* anInstance)
  1.2031 +{
  1.2032 +  ScrollFrameHelper* self = static_cast<ScrollFrameHelper*>(anInstance);
  1.2033 +
  1.2034 +  // Fire the synth mouse move.
  1.2035 +  self->mScrollActivityTimer->Cancel();
  1.2036 +  self->mScrollActivityTimer = nullptr;
  1.2037 +  self->mOuter->PresContext()->PresShell()->SynthesizeMouseMove(true);
  1.2038 +}
  1.2039 +
  1.2040 +
  1.2041 +void
  1.2042 +ScrollFrameHelper::ScheduleSyntheticMouseMove()
  1.2043 +{
  1.2044 +  if (!mScrollActivityTimer) {
  1.2045 +    mScrollActivityTimer = do_CreateInstance("@mozilla.org/timer;1");
  1.2046 +    if (!mScrollActivityTimer)
  1.2047 +      return;
  1.2048 +  }
  1.2049 +
  1.2050 +  mScrollActivityTimer->InitWithFuncCallback(
  1.2051 +        ScrollActivityCallback, this, 100, nsITimer::TYPE_ONE_SHOT);
  1.2052 +}
  1.2053 +
  1.2054 +void
  1.2055 +ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOrigin)
  1.2056 +{
  1.2057 +  if (aOrigin == nullptr) {
  1.2058 +    // If no origin was specified, we still want to set it to something that's
  1.2059 +    // non-null, so that we can use nullness to distinguish if the frame was scrolled
  1.2060 +    // at all. Default it to some generic placeholder.
  1.2061 +    aOrigin = nsGkAtoms::other;
  1.2062 +  }
  1.2063 +
  1.2064 +  nsPresContext* presContext = mOuter->PresContext();
  1.2065 +  nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
  1.2066 +  // 'scale' is our estimate of the scale factor that will be applied
  1.2067 +  // when rendering the scrolled content to its own ThebesLayer.
  1.2068 +  gfxSize scale = FrameLayerBuilder::GetThebesLayerScaleForFrame(mScrolledFrame);
  1.2069 +  nsPoint curPos = GetScrollPosition();
  1.2070 +  nsPoint alignWithPos = mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)
  1.2071 +      ? curPos : mScrollPosForLayerPixelAlignment;
  1.2072 +  // Try to align aPt with curPos so they have an integer number of layer
  1.2073 +  // pixels between them. This gives us the best chance of scrolling without
  1.2074 +  // having to invalidate due to changes in subpixel rendering.
  1.2075 +  // Note that when we actually draw into a ThebesLayer, the coordinates
  1.2076 +  // that get mapped onto the layer buffer pixels are from the display list,
  1.2077 +  // which are relative to the display root frame's top-left increasing down,
  1.2078 +  // whereas here our coordinates are scroll positions which increase upward
  1.2079 +  // and are relative to the scrollport top-left. This difference doesn't actually
  1.2080 +  // matter since all we are about is that there be an integer number of
  1.2081 +  // layer pixels between pt and curPos.
  1.2082 +  nsPoint pt =
  1.2083 +    ClampAndAlignWithLayerPixels(aPt,
  1.2084 +                                 GetScrollRangeForClamping(),
  1.2085 +                                 aRange,
  1.2086 +                                 alignWithPos,
  1.2087 +                                 appUnitsPerDevPixel,
  1.2088 +                                 scale);
  1.2089 +  if (pt == curPos) {
  1.2090 +    return;
  1.2091 +  }
  1.2092 +
  1.2093 +  bool needImageVisibilityUpdate = (mLastUpdateImagesPos == nsPoint(-1,-1));
  1.2094 +
  1.2095 +  nsPoint dist(std::abs(pt.x - mLastUpdateImagesPos.x),
  1.2096 +               std::abs(pt.y - mLastUpdateImagesPos.y));
  1.2097 +  nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
  1.2098 +  nscoord horzAllowance = std::max(scrollPortSize.width / std::max(sHorzScrollFraction, 1),
  1.2099 +                                   nsPresContext::AppUnitsPerCSSPixel());
  1.2100 +  nscoord vertAllowance = std::max(scrollPortSize.height / std::max(sVertScrollFraction, 1),
  1.2101 +                                   nsPresContext::AppUnitsPerCSSPixel());
  1.2102 +  if (dist.x >= horzAllowance || dist.y >= vertAllowance) {
  1.2103 +    needImageVisibilityUpdate = true;
  1.2104 +  }
  1.2105 +
  1.2106 +  if (needImageVisibilityUpdate) {
  1.2107 +    presContext->PresShell()->ScheduleImageVisibilityUpdate();
  1.2108 +  }
  1.2109 +
  1.2110 +  // notify the listeners.
  1.2111 +  for (uint32_t i = 0; i < mListeners.Length(); i++) {
  1.2112 +    mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
  1.2113 +  }
  1.2114 +
  1.2115 +  nsPoint oldScrollFramePos = mScrolledFrame->GetPosition();
  1.2116 +  // Update frame position for scrolling
  1.2117 +  mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
  1.2118 +  mOriginOfLastScroll = aOrigin;
  1.2119 +  mScrollGeneration = ++sScrollGenerationCounter;
  1.2120 +
  1.2121 +  // We pass in the amount to move visually
  1.2122 +  ScrollVisual(oldScrollFramePos);
  1.2123 +
  1.2124 +  ScheduleSyntheticMouseMove();
  1.2125 +  nsWeakFrame weakFrame(mOuter);
  1.2126 +  UpdateScrollbarPosition();
  1.2127 +  if (!weakFrame.IsAlive()) {
  1.2128 +    return;
  1.2129 +  }
  1.2130 +  PostScrollEvent();
  1.2131 +
  1.2132 +  // notify the listeners.
  1.2133 +  for (uint32_t i = 0; i < mListeners.Length(); i++) {
  1.2134 +    mListeners[i]->ScrollPositionDidChange(pt.x, pt.y);
  1.2135 +  }
  1.2136 +
  1.2137 +  nsCOMPtr<nsIDocShell> docShell = presContext->GetDocShell();
  1.2138 +  if (docShell) {
  1.2139 +    docShell->NotifyScrollObservers();
  1.2140 +  }
  1.2141 +}
  1.2142 +
  1.2143 +static int32_t
  1.2144 +MaxZIndexInList(nsDisplayList* aList, nsDisplayListBuilder* aBuilder)
  1.2145 +{
  1.2146 +  int32_t maxZIndex = 0;
  1.2147 +  for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
  1.2148 +    maxZIndex = std::max(maxZIndex, item->ZIndex());
  1.2149 +  }
  1.2150 +  return maxZIndex;
  1.2151 +}
  1.2152 +
  1.2153 +static void
  1.2154 +AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
  1.2155 +            nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
  1.2156 +            uint32_t aFlags, mozilla::layers::FrameMetrics::ViewID aScrollTargetId,
  1.2157 +            bool aPositioned)
  1.2158 +{
  1.2159 +  if (aSource->IsEmpty())
  1.2160 +    return;
  1.2161 +
  1.2162 +  nsDisplayWrapList* newItem = aOwnLayer?
  1.2163 +    new (aBuilder) nsDisplayOwnLayer(aBuilder, aSourceFrame, aSource,
  1.2164 +                                     aFlags, aScrollTargetId) :
  1.2165 +    new (aBuilder) nsDisplayWrapList(aBuilder, aSourceFrame, aSource);
  1.2166 +
  1.2167 +  if (aPositioned) {
  1.2168 +    // We want overlay scrollbars to always be on top of the scrolled content,
  1.2169 +    // but we don't want them to unnecessarily cover overlapping elements from
  1.2170 +    // outside our scroll frame.
  1.2171 +    nsDisplayList* positionedDescendants = aLists.PositionedDescendants();
  1.2172 +    if (!positionedDescendants->IsEmpty()) {
  1.2173 +      newItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
  1.2174 +      positionedDescendants->AppendNewToTop(newItem);
  1.2175 +    } else {
  1.2176 +      aLists.Outlines()->AppendNewToTop(newItem);
  1.2177 +    }
  1.2178 +  } else {
  1.2179 +    aLists.BorderBackground()->AppendNewToTop(newItem);
  1.2180 +  }
  1.2181 +}
  1.2182 +
  1.2183 +struct HoveredStateComparator
  1.2184 +{
  1.2185 +  bool Equals(nsIFrame* A, nsIFrame* B) const {
  1.2186 +    bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
  1.2187 +                                             nsGkAtoms::hover);
  1.2188 +    bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
  1.2189 +                                             nsGkAtoms::hover);
  1.2190 +    return aHovered == bHovered;
  1.2191 +  }
  1.2192 +  bool LessThan(nsIFrame* A, nsIFrame* B) const {
  1.2193 +    bool aHovered = A->GetContent()->HasAttr(kNameSpaceID_None,
  1.2194 +                                             nsGkAtoms::hover);
  1.2195 +    bool bHovered = B->GetContent()->HasAttr(kNameSpaceID_None,
  1.2196 +                                             nsGkAtoms::hover);
  1.2197 +    return !aHovered && bHovered;
  1.2198 +  }
  1.2199 +};
  1.2200 +
  1.2201 +void
  1.2202 +ScrollFrameHelper::AppendScrollPartsTo(nsDisplayListBuilder*   aBuilder,
  1.2203 +                                           const nsRect&           aDirtyRect,
  1.2204 +                                           const nsDisplayListSet& aLists,
  1.2205 +                                           bool&                   aCreateLayer,
  1.2206 +                                           bool                    aPositioned)
  1.2207 +{
  1.2208 +  nsITheme* theme = mOuter->PresContext()->GetTheme();
  1.2209 +  if (theme &&
  1.2210 +      theme->ShouldHideScrollbars()) {
  1.2211 +    return;
  1.2212 +  }
  1.2213 +
  1.2214 +  bool overlayScrollbars =
  1.2215 +    LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0;
  1.2216 +
  1.2217 +  nsAutoTArray<nsIFrame*, 3> scrollParts;
  1.2218 +  for (nsIFrame* kid = mOuter->GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
  1.2219 +    if (kid == mScrolledFrame ||
  1.2220 +        (kid->IsPositioned() || overlayScrollbars) != aPositioned)
  1.2221 +      continue;
  1.2222 +
  1.2223 +    scrollParts.AppendElement(kid);
  1.2224 +  }
  1.2225 +
  1.2226 +  mozilla::layers::FrameMetrics::ViewID scrollTargetId = aCreateLayer
  1.2227 +    ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
  1.2228 +    : mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
  1.2229 +
  1.2230 +  scrollParts.Sort(HoveredStateComparator());
  1.2231 +
  1.2232 +  for (uint32_t i = 0; i < scrollParts.Length(); ++i) {
  1.2233 +    nsDisplayListCollection partList;
  1.2234 +    mOuter->BuildDisplayListForChild(
  1.2235 +      aBuilder, scrollParts[i], aDirtyRect, partList,
  1.2236 +      nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
  1.2237 +
  1.2238 +    uint32_t flags = 0;
  1.2239 +    if (scrollParts[i] == mVScrollbarBox) {
  1.2240 +      flags |= nsDisplayOwnLayer::VERTICAL_SCROLLBAR;
  1.2241 +    }
  1.2242 +    if (scrollParts[i] == mHScrollbarBox) {
  1.2243 +      flags |= nsDisplayOwnLayer::HORIZONTAL_SCROLLBAR;
  1.2244 +    }
  1.2245 +
  1.2246 +    // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into
  1.2247 +    // partList.PositionedDescendants().
  1.2248 +    ::AppendToTop(aBuilder, aLists,
  1.2249 +                  partList.PositionedDescendants(), scrollParts[i],
  1.2250 +                  aCreateLayer, flags, scrollTargetId, aPositioned);
  1.2251 +  }
  1.2252 +}
  1.2253 +
  1.2254 +class ScrollLayerWrapper : public nsDisplayWrapper
  1.2255 +{
  1.2256 +public:
  1.2257 +  ScrollLayerWrapper(nsIFrame* aScrollFrame, nsIFrame* aScrolledFrame)
  1.2258 +    : mCount(0)
  1.2259 +    , mProps(aScrolledFrame->Properties())
  1.2260 +    , mScrollFrame(aScrollFrame)
  1.2261 +    , mScrolledFrame(aScrolledFrame)
  1.2262 +  {
  1.2263 +    SetCount(0);
  1.2264 +  }
  1.2265 +
  1.2266 +  virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
  1.2267 +                                  nsIFrame* aFrame,
  1.2268 +                                  nsDisplayList* aList) MOZ_OVERRIDE {
  1.2269 +    SetCount(++mCount);
  1.2270 +    return new (aBuilder) nsDisplayScrollLayer(aBuilder, aList, mScrolledFrame, mScrolledFrame, mScrollFrame);
  1.2271 +  }
  1.2272 +
  1.2273 +  virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
  1.2274 +                                  nsDisplayItem* aItem) MOZ_OVERRIDE {
  1.2275 +
  1.2276 +    SetCount(++mCount);
  1.2277 +    return new (aBuilder) nsDisplayScrollLayer(aBuilder, aItem, aItem->Frame(), mScrolledFrame, mScrollFrame);
  1.2278 +  }
  1.2279 +
  1.2280 +protected:
  1.2281 +  void SetCount(intptr_t aCount) {
  1.2282 +    mProps.Set(nsIFrame::ScrollLayerCount(), reinterpret_cast<void*>(aCount));
  1.2283 +  }
  1.2284 +
  1.2285 +  intptr_t mCount;
  1.2286 +  FrameProperties mProps;
  1.2287 +  nsIFrame* mScrollFrame;
  1.2288 +  nsIFrame* mScrolledFrame;
  1.2289 +};
  1.2290 +
  1.2291 +/* static */ bool ScrollFrameHelper::sImageVisPrefsCached = false;
  1.2292 +/* static */ uint32_t ScrollFrameHelper::sHorzExpandScrollPort = 0;
  1.2293 +/* static */ uint32_t ScrollFrameHelper::sVertExpandScrollPort = 1;
  1.2294 +/* static */ int32_t ScrollFrameHelper::sHorzScrollFraction = 2;
  1.2295 +/* static */ int32_t ScrollFrameHelper::sVertScrollFraction = 2;
  1.2296 +
  1.2297 +/* static */ void
  1.2298 +ScrollFrameHelper::EnsureImageVisPrefsCached()
  1.2299 +{
  1.2300 +  if (!sImageVisPrefsCached) {
  1.2301 +    Preferences::AddUintVarCache(&sHorzExpandScrollPort,
  1.2302 +      "layout.imagevisibility.numscrollportwidths", (uint32_t)0);
  1.2303 +    Preferences::AddUintVarCache(&sVertExpandScrollPort,
  1.2304 +      "layout.imagevisibility.numscrollportheights", 1);
  1.2305 +
  1.2306 +    Preferences::AddIntVarCache(&sHorzScrollFraction,
  1.2307 +      "layout.imagevisibility.amountscrollbeforeupdatehorizontal", 2);
  1.2308 +    Preferences::AddIntVarCache(&sVertScrollFraction,
  1.2309 +      "layout.imagevisibility.amountscrollbeforeupdatevertical", 2);
  1.2310 +
  1.2311 +    sImageVisPrefsCached = true;
  1.2312 +  }
  1.2313 +}
  1.2314 +
  1.2315 +nsRect
  1.2316 +ScrollFrameHelper::ExpandRect(const nsRect& aRect) const
  1.2317 +{
  1.2318 +  // We don't want to expand a rect in a direction that we can't scroll, so we
  1.2319 +  // check the scroll range.
  1.2320 +  nsRect scrollRange = GetScrollRangeForClamping();
  1.2321 +  nsPoint scrollPos = GetScrollPosition();
  1.2322 +  nsMargin expand(0, 0, 0, 0);
  1.2323 +
  1.2324 +  nscoord vertShift = sVertExpandScrollPort * aRect.height;
  1.2325 +  if (scrollRange.y < scrollPos.y) {
  1.2326 +    expand.top = vertShift;
  1.2327 +  }
  1.2328 +  if (scrollPos.y < scrollRange.YMost()) {
  1.2329 +    expand.bottom = vertShift;
  1.2330 +  }
  1.2331 +
  1.2332 +  nscoord horzShift = sHorzExpandScrollPort * aRect.width;
  1.2333 +  if (scrollRange.x < scrollPos.x) {
  1.2334 +    expand.left = horzShift;
  1.2335 +  }
  1.2336 +  if (scrollPos.x < scrollRange.XMost()) {
  1.2337 +    expand.right = horzShift;
  1.2338 +  }
  1.2339 +
  1.2340 +  nsRect rect = aRect;
  1.2341 +  rect.Inflate(expand);
  1.2342 +  return rect;
  1.2343 +}
  1.2344 +
  1.2345 +static bool
  1.2346 +ShouldBeClippedByFrame(nsIFrame* aClipFrame, nsIFrame* aClippedFrame)
  1.2347 +{
  1.2348 +  return nsLayoutUtils::IsProperAncestorFrame(aClipFrame, aClippedFrame);
  1.2349 +}
  1.2350 +
  1.2351 +static void
  1.2352 +ClipItemsExceptCaret(nsDisplayList* aList, nsDisplayListBuilder* aBuilder,
  1.2353 +                     nsIFrame* aClipFrame, const DisplayItemClip& aClip)
  1.2354 +{
  1.2355 +  nsDisplayItem* i = aList->GetBottom();
  1.2356 +  for (; i; i = i->GetAbove()) {
  1.2357 +    if (!::ShouldBeClippedByFrame(aClipFrame, i->Frame())) {
  1.2358 +      continue;
  1.2359 +    }
  1.2360 +
  1.2361 +    bool unused;
  1.2362 +    nsRect bounds = i->GetBounds(aBuilder, &unused);
  1.2363 +    bool isAffectedByClip = aClip.IsRectAffectedByClip(bounds);
  1.2364 +    if (isAffectedByClip && nsDisplayItem::TYPE_CARET == i->GetType()) {
  1.2365 +      // Don't clip the caret if it overflows vertically only, and by half
  1.2366 +      // its height at most.  This is to avoid clipping it when the line-height
  1.2367 +      // is small.
  1.2368 +      auto half = bounds.height / 2;
  1.2369 +      bounds.y += half;
  1.2370 +      bounds.height -= half;
  1.2371 +      isAffectedByClip = aClip.IsRectAffectedByClip(bounds);
  1.2372 +      if (isAffectedByClip) {
  1.2373 +        // Don't clip the caret if it's just outside on the right side.
  1.2374 +        nsRect rightSide(bounds.x - 1, bounds.y, 1, bounds.height);
  1.2375 +        isAffectedByClip = aClip.IsRectAffectedByClip(rightSide);
  1.2376 +        // Also, avoid clipping it in a zero-height line box (heuristic only).
  1.2377 +        if (isAffectedByClip) {
  1.2378 +          isAffectedByClip = i->Frame()->GetRect().height != 0;
  1.2379 +        }
  1.2380 +      }
  1.2381 +    }
  1.2382 +    if (isAffectedByClip) {
  1.2383 +      DisplayItemClip newClip;
  1.2384 +      newClip.IntersectWith(i->GetClip());
  1.2385 +      newClip.IntersectWith(aClip);
  1.2386 +      i->SetClip(aBuilder, newClip);
  1.2387 +    }
  1.2388 +    nsDisplayList* children = i->GetSameCoordinateSystemChildren();
  1.2389 +    if (children) {
  1.2390 +      ClipItemsExceptCaret(children, aBuilder, aClipFrame, aClip);
  1.2391 +    }
  1.2392 +  }
  1.2393 +}
  1.2394 +
  1.2395 +static void
  1.2396 +ClipListsExceptCaret(nsDisplayListCollection* aLists,
  1.2397 +                     nsDisplayListBuilder* aBuilder,
  1.2398 +                     nsIFrame* aClipFrame,
  1.2399 +                     const DisplayItemClip& aClip)
  1.2400 +{
  1.2401 +  ::ClipItemsExceptCaret(aLists->BorderBackground(), aBuilder, aClipFrame, aClip);
  1.2402 +  ::ClipItemsExceptCaret(aLists->BlockBorderBackgrounds(), aBuilder, aClipFrame, aClip);
  1.2403 +  ::ClipItemsExceptCaret(aLists->Floats(), aBuilder, aClipFrame, aClip);
  1.2404 +  ::ClipItemsExceptCaret(aLists->PositionedDescendants(), aBuilder, aClipFrame, aClip);
  1.2405 +  ::ClipItemsExceptCaret(aLists->Outlines(), aBuilder, aClipFrame, aClip);
  1.2406 +  ::ClipItemsExceptCaret(aLists->Content(), aBuilder, aClipFrame, aClip);
  1.2407 +}
  1.2408 +
  1.2409 +static bool
  1.2410 +DisplayportExceedsMaxTextureSize(nsPresContext* aPresContext, const nsRect& aDisplayPort)
  1.2411 +{
  1.2412 +#ifdef MOZ_WIDGET_GONK
  1.2413 +  // On B2G we actively run into max texture size limits because the displayport-sizing code
  1.2414 +  // in the AsyncPanZoomController code is slightly busted (bug 957668 will fix it properly).
  1.2415 +  // We sometimes end up requesting displayports for which the corresponding layer will be
  1.2416 +  // larger than the max GL texture size (which we assume to be 4096 here).
  1.2417 +  // If we run into this case, we should just not use the displayport at all and fall back to
  1.2418 +  // just making a ScrollInfoLayer so that we use the APZC's synchronous scrolling fallback
  1.2419 +  // mechanism.
  1.2420 +  // Note also that if we don't do this here, it is quite likely that the parent B2G process
  1.2421 +  // will kill this child process to prevent OOMs (see the patch that landed as part of bug
  1.2422 +  // 965945 for details).
  1.2423 +  gfxSize resolution = aPresContext->PresShell()->GetCumulativeResolution();
  1.2424 +  return (aPresContext->AppUnitsToDevPixels(aDisplayPort.width) * resolution.width > 4096) ||
  1.2425 +         (aPresContext->AppUnitsToDevPixels(aDisplayPort.height) * resolution.height > 4096);
  1.2426 +#else
  1.2427 +  return false;
  1.2428 +#endif
  1.2429 +}
  1.2430 +
  1.2431 +void
  1.2432 +ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1.2433 +                                    const nsRect&           aDirtyRect,
  1.2434 +                                    const nsDisplayListSet& aLists)
  1.2435 +{
  1.2436 +  if (aBuilder->IsForImageVisibility()) {
  1.2437 +    mLastUpdateImagesPos = GetScrollPosition();
  1.2438 +  }
  1.2439 +
  1.2440 +  mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
  1.2441 +
  1.2442 +  if (aBuilder->IsPaintingToWindow()) {
  1.2443 +    mScrollPosAtLastPaint = GetScrollPosition();
  1.2444 +    if (IsScrollingActive() && !CanScrollWithBlitting(mOuter)) {
  1.2445 +      MarkInactive();
  1.2446 +    }
  1.2447 +    if (IsScrollingActive()) {
  1.2448 +      if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
  1.2449 +        mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
  1.2450 +      }
  1.2451 +    } else {
  1.2452 +      mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
  1.2453 +    }
  1.2454 +  }
  1.2455 +
  1.2456 +  // We put scrollbars in their own layers when this is the root scroll
  1.2457 +  // frame and we are a toplevel content document. In this situation, the
  1.2458 +  // scrollbar(s) would normally be assigned their own layer anyway, since
  1.2459 +  // they're not scrolled with the rest of the document. But when both
  1.2460 +  // scrollbars are visible, the layer's visible rectangle would be the size
  1.2461 +  // of the viewport, so most layer implementations would create a layer buffer
  1.2462 +  // that's much larger than necessary. Creating independent layers for each
  1.2463 +  // scrollbar works around the problem.
  1.2464 +  bool createLayersForScrollbars = mIsRoot &&
  1.2465 +    mOuter->PresContext()->IsRootContentDocument();
  1.2466 +
  1.2467 +  if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
  1.2468 +
  1.2469 +    // If we are a root scroll frame that has a display port we want to add
  1.2470 +    // scrollbars, they will be children of the scrollable layer, but they get
  1.2471 +    // adjusted by the APZC automatically.
  1.2472 +    bool addScrollBars = mIsRoot &&
  1.2473 +      nsLayoutUtils::GetDisplayPort(mOuter->GetContent()) &&
  1.2474 +      !aBuilder->IsForEventDelivery();
  1.2475 +    // For now, don't add them for display root documents, cause we've never
  1.2476 +    // had them there.
  1.2477 +    if (aBuilder->RootReferenceFrame()->PresContext() == mOuter->PresContext()) {
  1.2478 +      addScrollBars = false;
  1.2479 +    }
  1.2480 +
  1.2481 +    if (addScrollBars) {
  1.2482 +      // Add classic scrollbars.
  1.2483 +      AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, createLayersForScrollbars,
  1.2484 +                          false);
  1.2485 +    }
  1.2486 +
  1.2487 +    // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
  1.2488 +    // The scrolled frame shouldn't have its own background/border, so we
  1.2489 +    // can just pass aLists directly.
  1.2490 +    mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame,
  1.2491 +                                     aDirtyRect, aLists);
  1.2492 +
  1.2493 +#ifdef MOZ_WIDGET_GONK
  1.2494 +    // TODO: only layerize the overlay scrollbars if this scrollframe can be
  1.2495 +    // panned asynchronously. For now just always layerize on B2G because.
  1.2496 +    // that's where we want the layerized scrollbars
  1.2497 +    createLayersForScrollbars = true;
  1.2498 +#endif
  1.2499 +    if (addScrollBars) {
  1.2500 +      // Add overlay scrollbars.
  1.2501 +      AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, createLayersForScrollbars,
  1.2502 +                          true);
  1.2503 +    }
  1.2504 +
  1.2505 +    return;
  1.2506 +  }
  1.2507 +
  1.2508 +  // Now display the scrollbars and scrollcorner. These parts are drawn
  1.2509 +  // in the border-background layer, on top of our own background and
  1.2510 +  // borders and underneath borders and backgrounds of later elements
  1.2511 +  // in the tree.
  1.2512 +  // Note that this does not apply for overlay scrollbars; those are drawn
  1.2513 +  // in the positioned-elements layer on top of everything else by the call
  1.2514 +  // to AppendScrollPartsTo(..., true) further down.
  1.2515 +  AppendScrollPartsTo(aBuilder, aDirtyRect, aLists, createLayersForScrollbars,
  1.2516 +                      false);
  1.2517 +
  1.2518 +  // Overflow clipping can never clip frames outside our subtree, so there
  1.2519 +  // is no need to worry about whether we are a moving frame that might clip
  1.2520 +  // non-moving frames.
  1.2521 +  // Not all our descendants will be clipped by overflow clipping, but all
  1.2522 +  // the ones that aren't clipped will be out of flow frames that have already
  1.2523 +  // had dirty rects saved for them by their parent frames calling
  1.2524 +  // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
  1.2525 +  // dirty rect here.
  1.2526 +  nsRect dirtyRect = aDirtyRect.Intersect(mScrollPort);
  1.2527 +
  1.2528 +  nsRect displayPort;
  1.2529 +  bool usingDisplayport = false;
  1.2530 +  if (!aBuilder->IsForEventDelivery()) {
  1.2531 +    if (!mIsRoot) {
  1.2532 +      // For a non-root scroll frame, override the value of the display port
  1.2533 +      // base rect, and possibly create a display port if there isn't one
  1.2534 +      // already. For root scroll frame, nsLayoutUtils::PaintFrame or
  1.2535 +      // nsSubDocumentFrame::BuildDisplayList takes care of this.
  1.2536 +      nsRect displayportBase = dirtyRect;
  1.2537 +      usingDisplayport = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
  1.2538 +          *aBuilder, mOuter, displayportBase, &displayPort);
  1.2539 +    } else {
  1.2540 +      // For a root frmae, just get the value of the existing of the display
  1.2541 +      // port, if any.
  1.2542 +      usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
  1.2543 +    }
  1.2544 +
  1.2545 +    if (usingDisplayport && DisplayportExceedsMaxTextureSize(mOuter->PresContext(), displayPort)) {
  1.2546 +      usingDisplayport = false;
  1.2547 +    }
  1.2548 +
  1.2549 +    // Override the dirty rectangle if the displayport has been set.
  1.2550 +    if (usingDisplayport) {
  1.2551 +      dirtyRect = displayPort;
  1.2552 +    }
  1.2553 +  }
  1.2554 +
  1.2555 +  if (aBuilder->IsForImageVisibility()) {
  1.2556 +    // We expand the dirty rect to catch images just outside of the scroll port.
  1.2557 +    // We use the dirty rect instead of the whole scroll port to prevent
  1.2558 +    // too much expansion in the presence of very large (bigger than the
  1.2559 +    // viewport) scroll ports.
  1.2560 +    dirtyRect = ExpandRect(dirtyRect);
  1.2561 +  }
  1.2562 +
  1.2563 +  // Since making new layers is expensive, only use nsDisplayScrollLayer
  1.2564 +  // if the area is scrollable and we're the content process (unless we're on
  1.2565 +  // B2G, where we support async scrolling for scrollable elements in the
  1.2566 +  // parent process as well).
  1.2567 +  // When a displayport is being used, force building of a layer so that
  1.2568 +  // CompositorParent can always find the scrollable layer for the root content
  1.2569 +  // document.
  1.2570 +  // If the element is marked 'scrollgrab', also force building of a layer
  1.2571 +  // so that APZ can implement scroll grabbing.
  1.2572 +  mShouldBuildScrollableLayer = usingDisplayport || nsContentUtils::HasScrollgrab(mOuter->GetContent());
  1.2573 +  bool shouldBuildLayer = false;
  1.2574 +  if (mShouldBuildScrollableLayer) {
  1.2575 +    shouldBuildLayer = true;
  1.2576 +  } else {
  1.2577 +    shouldBuildLayer =
  1.2578 +      nsLayoutUtils::WantSubAPZC() &&
  1.2579 +      WantAsyncScroll() &&
  1.2580 +      // If we are the root scroll frame for the display root then we don't need a scroll
  1.2581 +      // info layer to make a RecordFrameMetrics call for us as
  1.2582 +      // nsDisplayList::PaintForFrame already calls RecordFrameMetrics for us.
  1.2583 +      (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
  1.2584 +  }
  1.2585 +
  1.2586 +  nsDisplayListCollection scrolledContent;
  1.2587 +  {
  1.2588 +    // Note that setting the current scroll parent id here means that positioned children
  1.2589 +    // of this scroll info layer will pick up the scroll info layer as their scroll handoff
  1.2590 +    // parent. This is intentional because that is what happens for positioned children
  1.2591 +    // of scroll layers, and we want to maintain consistent behaviour between scroll layers
  1.2592 +    // and scroll info layers.
  1.2593 +    nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(
  1.2594 +        aBuilder,
  1.2595 +        shouldBuildLayer && mScrolledFrame->GetContent()
  1.2596 +            ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent())
  1.2597 +            : aBuilder->GetCurrentScrollParentId());
  1.2598 +    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
  1.2599 +
  1.2600 +    if (usingDisplayport) {
  1.2601 +      nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter);
  1.2602 +
  1.2603 +      // If we are using a display port, then ignore any pre-existing clip
  1.2604 +      // passed down from our parents, and use only the clip computed here
  1.2605 +      // based on the display port. The pre-existing clip would just defeat
  1.2606 +      // the purpose of a display port which is to paint regions that are not
  1.2607 +      // currently visible so that they can be brought into view asynchronously.
  1.2608 +      // Notes:
  1.2609 +      //   - The pre-existing clip state will be restored when the
  1.2610 +      //     AutoSaveRestore goes out of scope, so there is no permanent change
  1.2611 +      //     to this clip state.
  1.2612 +      //   - We still set a clip to the scroll port further below where we
  1.2613 +      //     build the scroll wrapper. This doesn't prevent us from painting
  1.2614 +      //     the entire displayport, but it lets the compositor know to
  1.2615 +      //     clip to the scroll port after compositing.
  1.2616 +      clipState.Clear();
  1.2617 +
  1.2618 +      if (mClipAllDescendants) {
  1.2619 +        clipState.ClipContentDescendants(clip);
  1.2620 +      } else {
  1.2621 +        clipState.ClipContainingBlockDescendants(clip, nullptr);
  1.2622 +      }
  1.2623 +    } else {
  1.2624 +      nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
  1.2625 +      nscoord radii[8];
  1.2626 +      bool haveRadii = mOuter->GetPaddingBoxBorderRadii(radii);
  1.2627 +      // Our override of GetBorderRadii ensures we never have a radius at
  1.2628 +      // the corners where we have a scrollbar.
  1.2629 +      if (mClipAllDescendants) {
  1.2630 +        clipState.ClipContentDescendants(clip, haveRadii ? radii : nullptr);
  1.2631 +      } else {
  1.2632 +        clipState.ClipContainingBlockDescendants(clip, haveRadii ? radii : nullptr);
  1.2633 +      }
  1.2634 +    }
  1.2635 +
  1.2636 +    mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
  1.2637 +  }
  1.2638 +
  1.2639 +  if (MOZ_UNLIKELY(mOuter->StyleDisplay()->mOverflowClipBox ==
  1.2640 +                     NS_STYLE_OVERFLOW_CLIP_BOX_CONTENT_BOX)) {
  1.2641 +    // We only clip if there is *scrollable* overflow, to avoid clipping
  1.2642 +    // *visual* overflow unnecessarily.
  1.2643 +    nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
  1.2644 +    nsRect so = mScrolledFrame->GetScrollableOverflowRect();
  1.2645 +    if (clipRect.width != so.width || clipRect.height != so.height ||
  1.2646 +        so.x < 0 || so.y < 0) {
  1.2647 +      // The 'scrolledContent' items are clipped to the padding-box at this point.
  1.2648 +      // Now clip them again to the content-box, except the nsDisplayCaret item
  1.2649 +      // which we allow to overflow the content-box in various situations --
  1.2650 +      // see ::ClipItemsExceptCaret.
  1.2651 +      clipRect.Deflate(mOuter->GetUsedPadding());
  1.2652 +      DisplayItemClip clip;
  1.2653 +      clip.SetTo(clipRect);
  1.2654 +      ::ClipListsExceptCaret(&scrolledContent, aBuilder, mScrolledFrame, clip);
  1.2655 +    }
  1.2656 +  }
  1.2657 +
  1.2658 +  if (shouldBuildLayer) {
  1.2659 +    // ScrollLayerWrapper must always be created because it initializes the
  1.2660 +    // scroll layer count. The display lists depend on this.
  1.2661 +    ScrollLayerWrapper wrapper(mOuter, mScrolledFrame);
  1.2662 +
  1.2663 +    if (mShouldBuildScrollableLayer) {
  1.2664 +      DisplayListClipState::AutoSaveRestore clipState(aBuilder);
  1.2665 +
  1.2666 +      // For root scrollframes in documents where the CSS viewport has been
  1.2667 +      // modified, the CSS viewport no longer corresponds to what is visible,
  1.2668 +      // so we don't want to clip the content to it. For root scrollframes
  1.2669 +      // in documents where the CSS viewport is NOT modified, the mScrollPort
  1.2670 +      // is the same as the CSS viewport, modulo scrollbars.
  1.2671 +      if (!(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden())) {
  1.2672 +        nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
  1.2673 +        if (mClipAllDescendants) {
  1.2674 +          clipState.ClipContentDescendants(clip);
  1.2675 +        } else {
  1.2676 +          clipState.ClipContainingBlockDescendants(clip);
  1.2677 +        }
  1.2678 +      }
  1.2679 +
  1.2680 +      // Once a displayport is set, assume that scrolling needs to be fast
  1.2681 +      // so create a layer with all the content inside. The compositor
  1.2682 +      // process will be able to scroll the content asynchronously.
  1.2683 +      wrapper.WrapListsInPlace(aBuilder, mOuter, scrolledContent);
  1.2684 +    }
  1.2685 +
  1.2686 +    // In case we are not using displayport or the nsDisplayScrollLayers are
  1.2687 +    // flattened during visibility computation, we still need to export the
  1.2688 +    // metadata about this scroll box to the compositor process.
  1.2689 +    nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
  1.2690 +      aBuilder, mScrolledFrame, mOuter);
  1.2691 +    scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
  1.2692 +  }
  1.2693 +  // Now display overlay scrollbars and the resizer, if we have one.
  1.2694 +#ifdef MOZ_WIDGET_GONK
  1.2695 +  // TODO: only layerize the overlay scrollbars if this scrollframe can be
  1.2696 +  // panned asynchronously. For now just always layerize on B2G because.
  1.2697 +  // that's where we want the layerized scrollbars
  1.2698 +  createLayersForScrollbars = true;
  1.2699 +#endif
  1.2700 +  AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent,
  1.2701 +                      createLayersForScrollbars, true);
  1.2702 +  scrolledContent.MoveTo(aLists);
  1.2703 +}
  1.2704 +
  1.2705 +bool
  1.2706 +ScrollFrameHelper::IsRectNearlyVisible(const nsRect& aRect) const
  1.2707 +{
  1.2708 +  // Use the right rect depending on if a display port is set.
  1.2709 +  nsRect displayPort;
  1.2710 +  bool usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
  1.2711 +  return aRect.Intersects(ExpandRect(usingDisplayport ? displayPort : mScrollPort));
  1.2712 +}
  1.2713 +
  1.2714 +static void HandleScrollPref(nsIScrollable *aScrollable, int32_t aOrientation,
  1.2715 +                             uint8_t& aValue)
  1.2716 +{
  1.2717 +  int32_t pref;
  1.2718 +  aScrollable->GetDefaultScrollbarPreferences(aOrientation, &pref);
  1.2719 +  switch (pref) {
  1.2720 +    case nsIScrollable::Scrollbar_Auto:
  1.2721 +      // leave |aValue| untouched
  1.2722 +      break;
  1.2723 +    case nsIScrollable::Scrollbar_Never:
  1.2724 +      aValue = NS_STYLE_OVERFLOW_HIDDEN;
  1.2725 +      break;
  1.2726 +    case nsIScrollable::Scrollbar_Always:
  1.2727 +      aValue = NS_STYLE_OVERFLOW_SCROLL;
  1.2728 +      break;
  1.2729 +  }
  1.2730 +}
  1.2731 +
  1.2732 +ScrollbarStyles
  1.2733 +ScrollFrameHelper::GetScrollbarStylesFromFrame() const
  1.2734 +{
  1.2735 +  nsPresContext* presContext = mOuter->PresContext();
  1.2736 +  if (!presContext->IsDynamic() &&
  1.2737 +      !(mIsRoot && presContext->HasPaginatedScrolling())) {
  1.2738 +    return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN, NS_STYLE_OVERFLOW_HIDDEN);
  1.2739 +  }
  1.2740 +
  1.2741 +  if (!mIsRoot) {
  1.2742 +    const nsStyleDisplay* disp = mOuter->StyleDisplay();
  1.2743 +    return ScrollbarStyles(disp->mOverflowX, disp->mOverflowY);
  1.2744 +  }
  1.2745 +
  1.2746 +  ScrollbarStyles result = presContext->GetViewportOverflowOverride();
  1.2747 +  nsCOMPtr<nsISupports> container = presContext->GetContainerWeak();
  1.2748 +  nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(container);
  1.2749 +  if (scrollable) {
  1.2750 +    HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_X,
  1.2751 +                     result.mHorizontal);
  1.2752 +    HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_Y,
  1.2753 +                     result.mVertical);
  1.2754 +  }
  1.2755 +  return result;
  1.2756 +}
  1.2757 +
  1.2758 +nsRect
  1.2759 +ScrollFrameHelper::GetScrollRange() const
  1.2760 +{
  1.2761 +  return GetScrollRange(mScrollPort.width, mScrollPort.height);
  1.2762 +}
  1.2763 +
  1.2764 +nsRect
  1.2765 +ScrollFrameHelper::GetScrollRange(nscoord aWidth, nscoord aHeight) const
  1.2766 +{
  1.2767 +  nsRect range = GetScrolledRect();
  1.2768 +  range.width = std::max(range.width - aWidth, 0);
  1.2769 +  range.height = std::max(range.height - aHeight, 0);
  1.2770 +  return range;
  1.2771 +}
  1.2772 +
  1.2773 +nsRect
  1.2774 +ScrollFrameHelper::GetScrollRangeForClamping() const
  1.2775 +{
  1.2776 +  if (!ShouldClampScrollPosition()) {
  1.2777 +    return nsRect(nscoord_MIN/2, nscoord_MIN/2,
  1.2778 +                  nscoord_MAX - nscoord_MIN/2, nscoord_MAX - nscoord_MIN/2);
  1.2779 +  }
  1.2780 +  nsSize scrollPortSize = GetScrollPositionClampingScrollPortSize();
  1.2781 +  return GetScrollRange(scrollPortSize.width, scrollPortSize.height);
  1.2782 +}
  1.2783 +
  1.2784 +nsSize
  1.2785 +ScrollFrameHelper::GetScrollPositionClampingScrollPortSize() const
  1.2786 +{
  1.2787 +  nsIPresShell* presShell = mOuter->PresContext()->PresShell();
  1.2788 +  if (mIsRoot && presShell->IsScrollPositionClampingScrollPortSizeSet()) {
  1.2789 +    return presShell->GetScrollPositionClampingScrollPortSize();
  1.2790 +  }
  1.2791 +  return mScrollPort.Size();
  1.2792 +}
  1.2793 +
  1.2794 +gfxSize
  1.2795 +ScrollFrameHelper::GetResolution() const
  1.2796 +{
  1.2797 +  return mResolution;
  1.2798 +}
  1.2799 +
  1.2800 +void
  1.2801 +ScrollFrameHelper::SetResolution(const gfxSize& aResolution)
  1.2802 +{
  1.2803 +  mResolution = aResolution;
  1.2804 +  mIsResolutionSet = true;
  1.2805 +}
  1.2806 +
  1.2807 +static void
  1.2808 +AdjustForWholeDelta(int32_t aDelta, nscoord* aCoord)
  1.2809 +{
  1.2810 +  if (aDelta < 0) {
  1.2811 +    *aCoord = nscoord_MIN;
  1.2812 +  } else if (aDelta > 0) {
  1.2813 +    *aCoord = nscoord_MAX;
  1.2814 +  }
  1.2815 +}
  1.2816 +
  1.2817 +/**
  1.2818 + * Calculate lower/upper scrollBy range in given direction.
  1.2819 + * @param aDelta specifies scrollBy direction, if 0 then range will be 0 size
  1.2820 + * @param aPos desired destination in AppUnits
  1.2821 + * @param aNeg/PosTolerance defines relative range distance
  1.2822 + *   below and above of aPos point
  1.2823 + * @param aMultiplier used for conversion of tolerance into appUnis
  1.2824 + */
  1.2825 +static void
  1.2826 +CalcRangeForScrollBy(int32_t aDelta, nscoord aPos,
  1.2827 +                     float aNegTolerance,
  1.2828 +                     float aPosTolerance,
  1.2829 +                     nscoord aMultiplier,
  1.2830 +                     nscoord* aLower, nscoord* aUpper)
  1.2831 +{
  1.2832 +  if (!aDelta) {
  1.2833 +    *aLower = *aUpper = aPos;
  1.2834 +    return;
  1.2835 +  }
  1.2836 +  *aLower = aPos - NSToCoordRound(aMultiplier * (aDelta > 0 ? aNegTolerance : aPosTolerance));
  1.2837 +  *aUpper = aPos + NSToCoordRound(aMultiplier * (aDelta > 0 ? aPosTolerance : aNegTolerance));
  1.2838 +}
  1.2839 +
  1.2840 +void
  1.2841 +ScrollFrameHelper::ScrollBy(nsIntPoint aDelta,
  1.2842 +                                nsIScrollableFrame::ScrollUnit aUnit,
  1.2843 +                                nsIScrollableFrame::ScrollMode aMode,
  1.2844 +                                nsIntPoint* aOverflow,
  1.2845 +                                nsIAtom *aOrigin)
  1.2846 +{
  1.2847 +  nsSize deltaMultiplier;
  1.2848 +  float negativeTolerance;
  1.2849 +  float positiveTolerance;
  1.2850 +  if (!aOrigin){
  1.2851 +    aOrigin = nsGkAtoms::other;
  1.2852 +  }
  1.2853 +  bool isGenericOrigin = (aOrigin == nsGkAtoms::other);
  1.2854 +  switch (aUnit) {
  1.2855 +  case nsIScrollableFrame::DEVICE_PIXELS: {
  1.2856 +    nscoord appUnitsPerDevPixel =
  1.2857 +      mOuter->PresContext()->AppUnitsPerDevPixel();
  1.2858 +    deltaMultiplier = nsSize(appUnitsPerDevPixel, appUnitsPerDevPixel);
  1.2859 +    if (isGenericOrigin){
  1.2860 +      aOrigin = nsGkAtoms::pixels;
  1.2861 +    }
  1.2862 +    negativeTolerance = positiveTolerance = 0.5f;
  1.2863 +    break;
  1.2864 +  }
  1.2865 +  case nsIScrollableFrame::LINES: {
  1.2866 +    deltaMultiplier = GetLineScrollAmount();
  1.2867 +    if (isGenericOrigin){
  1.2868 +      aOrigin = nsGkAtoms::lines;
  1.2869 +    }
  1.2870 +    negativeTolerance = positiveTolerance = 0.1f;
  1.2871 +    break;
  1.2872 +  }
  1.2873 +  case nsIScrollableFrame::PAGES: {
  1.2874 +    deltaMultiplier = GetPageScrollAmount();
  1.2875 +    if (isGenericOrigin){
  1.2876 +      aOrigin = nsGkAtoms::pages;
  1.2877 +    }
  1.2878 +    negativeTolerance = 0.05f;
  1.2879 +    positiveTolerance = 0;
  1.2880 +    break;
  1.2881 +  }
  1.2882 +  case nsIScrollableFrame::WHOLE: {
  1.2883 +    nsPoint pos = GetScrollPosition();
  1.2884 +    AdjustForWholeDelta(aDelta.x, &pos.x);
  1.2885 +    AdjustForWholeDelta(aDelta.y, &pos.y);
  1.2886 +    ScrollTo(pos, aMode);
  1.2887 +    // 'this' might be destroyed here
  1.2888 +    if (aOverflow) {
  1.2889 +      *aOverflow = nsIntPoint(0, 0);
  1.2890 +    }
  1.2891 +    return;
  1.2892 +  }
  1.2893 +  default:
  1.2894 +    NS_ERROR("Invalid scroll mode");
  1.2895 +    return;
  1.2896 +  }
  1.2897 +
  1.2898 +  nsPoint newPos = mDestination +
  1.2899 +    nsPoint(aDelta.x*deltaMultiplier.width, aDelta.y*deltaMultiplier.height);
  1.2900 +  // Calculate desired range values.
  1.2901 +  nscoord rangeLowerX, rangeUpperX, rangeLowerY, rangeUpperY;
  1.2902 +  CalcRangeForScrollBy(aDelta.x, newPos.x, negativeTolerance, positiveTolerance,
  1.2903 +                       deltaMultiplier.width, &rangeLowerX, &rangeUpperX);
  1.2904 +  CalcRangeForScrollBy(aDelta.y, newPos.y, negativeTolerance, positiveTolerance,
  1.2905 +                       deltaMultiplier.height, &rangeLowerY, &rangeUpperY);
  1.2906 +  nsRect range(rangeLowerX,
  1.2907 +               rangeLowerY,
  1.2908 +               rangeUpperX - rangeLowerX,
  1.2909 +               rangeUpperY - rangeLowerY);
  1.2910 +  nsWeakFrame weakFrame(mOuter);
  1.2911 +  ScrollToWithOrigin(newPos, aMode, aOrigin, &range);
  1.2912 +  if (!weakFrame.IsAlive()) {
  1.2913 +    return;
  1.2914 +  }
  1.2915 +
  1.2916 +  if (aOverflow) {
  1.2917 +    nsPoint clampAmount = newPos - mDestination;
  1.2918 +    float appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
  1.2919 +    *aOverflow = nsIntPoint(
  1.2920 +        NSAppUnitsToIntPixels(clampAmount.x, appUnitsPerDevPixel),
  1.2921 +        NSAppUnitsToIntPixels(clampAmount.y, appUnitsPerDevPixel));
  1.2922 +  }
  1.2923 +}
  1.2924 +
  1.2925 +nsSize
  1.2926 +ScrollFrameHelper::GetLineScrollAmount() const
  1.2927 +{
  1.2928 +  nsRefPtr<nsFontMetrics> fm;
  1.2929 +  nsLayoutUtils::GetFontMetricsForFrame(mOuter, getter_AddRefs(fm),
  1.2930 +    nsLayoutUtils::FontSizeInflationFor(mOuter));
  1.2931 +  NS_ASSERTION(fm, "FontMetrics is null, assuming fontHeight == 1 appunit");
  1.2932 +  static nscoord sMinLineScrollAmountInPixels = -1;
  1.2933 +  if (sMinLineScrollAmountInPixels < 0) {
  1.2934 +    Preferences::AddIntVarCache(&sMinLineScrollAmountInPixels,
  1.2935 +                                "mousewheel.min_line_scroll_amount", 1);
  1.2936 +  }
  1.2937 +  int32_t appUnitsPerDevPixel = mOuter->PresContext()->AppUnitsPerDevPixel();
  1.2938 +  nscoord minScrollAmountInAppUnits =
  1.2939 +    std::max(1, sMinLineScrollAmountInPixels) * appUnitsPerDevPixel;
  1.2940 +  nscoord horizontalAmount = fm ? fm->AveCharWidth() : 0;
  1.2941 +  nscoord verticalAmount = fm ? fm->MaxHeight() : 0;
  1.2942 +  return nsSize(std::max(horizontalAmount, minScrollAmountInAppUnits),
  1.2943 +                std::max(verticalAmount, minScrollAmountInAppUnits));
  1.2944 +}
  1.2945 +
  1.2946 +/**
  1.2947 + * Compute the scrollport size excluding any fixed-pos headers and
  1.2948 + * footers. A header or footer is an box that spans that entire width
  1.2949 + * of the viewport and touches the top (or bottom, respectively) of the
  1.2950 + * viewport. We also want to consider fixed elements that stack or overlap
  1.2951 + * to effectively create a larger header or footer. Headers and footers that
  1.2952 + * cover more than a third of the the viewport are ignored since they
  1.2953 + * probably aren't true headers and footers and we don't want to restrict
  1.2954 + * scrolling too much in such cases. This is a bit conservative --- some
  1.2955 + * pages use elements as headers or footers that don't span the entire width
  1.2956 + * of the viewport --- but it should be a good start.
  1.2957 + */
  1.2958 +struct TopAndBottom
  1.2959 +{
  1.2960 +  TopAndBottom(nscoord aTop, nscoord aBottom) : top(aTop), bottom(aBottom) {}
  1.2961 +
  1.2962 +  nscoord top, bottom;
  1.2963 +};
  1.2964 +struct TopComparator
  1.2965 +{
  1.2966 +  bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
  1.2967 +    return A.top == B.top;
  1.2968 +  }
  1.2969 +  bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
  1.2970 +    return A.top < B.top;
  1.2971 +  }
  1.2972 +};
  1.2973 +struct ReverseBottomComparator
  1.2974 +{
  1.2975 +  bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
  1.2976 +    return A.bottom == B.bottom;
  1.2977 +  }
  1.2978 +  bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
  1.2979 +    return A.bottom > B.bottom;
  1.2980 +  }
  1.2981 +};
  1.2982 +static nsSize
  1.2983 +GetScrollPortSizeExcludingHeadersAndFooters(nsIFrame* aViewportFrame,
  1.2984 +                                            const nsRect& aScrollPort)
  1.2985 +{
  1.2986 +  nsTArray<TopAndBottom> list;
  1.2987 +  nsFrameList fixedFrames = aViewportFrame->GetChildList(nsIFrame::kFixedList);
  1.2988 +  for (nsFrameList::Enumerator iterator(fixedFrames); !iterator.AtEnd();
  1.2989 +       iterator.Next()) {
  1.2990 +    nsIFrame* f = iterator.get();
  1.2991 +    nsRect r = f->GetRect().Intersect(aScrollPort);
  1.2992 +    if (r.x == 0 && r.width == aScrollPort.width &&
  1.2993 +        r.height <= aScrollPort.height/3) {
  1.2994 +      list.AppendElement(TopAndBottom(r.y, r.YMost()));
  1.2995 +    }
  1.2996 +  }
  1.2997 +
  1.2998 +  list.Sort(TopComparator());
  1.2999 +  nscoord headerBottom = 0;
  1.3000 +  for (uint32_t i = 0; i < list.Length(); ++i) {
  1.3001 +    if (list[i].top <= headerBottom) {
  1.3002 +      headerBottom = std::max(headerBottom, list[i].bottom);
  1.3003 +    }
  1.3004 +  }
  1.3005 +
  1.3006 +  list.Sort(ReverseBottomComparator());
  1.3007 +  nscoord footerTop = aScrollPort.height;
  1.3008 +  for (uint32_t i = 0; i < list.Length(); ++i) {
  1.3009 +    if (list[i].bottom >= footerTop) {
  1.3010 +      footerTop = std::min(footerTop, list[i].top);
  1.3011 +    }
  1.3012 +  }
  1.3013 +
  1.3014 +  headerBottom = std::min(aScrollPort.height/3, headerBottom);
  1.3015 +  footerTop = std::max(aScrollPort.height - aScrollPort.height/3, footerTop);
  1.3016 +
  1.3017 +  return nsSize(aScrollPort.width, footerTop - headerBottom);
  1.3018 +}
  1.3019 +
  1.3020 +nsSize
  1.3021 +ScrollFrameHelper::GetPageScrollAmount() const
  1.3022 +{
  1.3023 +  nsSize lineScrollAmount = GetLineScrollAmount();
  1.3024 +  nsSize effectiveScrollPortSize;
  1.3025 +  if (mIsRoot) {
  1.3026 +    // Reduce effective scrollport height by the height of any fixed-pos
  1.3027 +    // headers or footers
  1.3028 +    nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
  1.3029 +    effectiveScrollPortSize =
  1.3030 +      GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
  1.3031 +  } else {
  1.3032 +    effectiveScrollPortSize = mScrollPort.Size();
  1.3033 +  }
  1.3034 +  // The page increment is the size of the page, minus the smaller of
  1.3035 +  // 10% of the size or 2 lines.
  1.3036 +  return nsSize(
  1.3037 +    effectiveScrollPortSize.width -
  1.3038 +      std::min(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
  1.3039 +    effectiveScrollPortSize.height -
  1.3040 +      std::min(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
  1.3041 +}
  1.3042 +
  1.3043 +  /**
  1.3044 +   * this code is resposible for restoring the scroll position back to some
  1.3045 +   * saved position. if the user has not moved the scroll position manually
  1.3046 +   * we keep scrolling down until we get to our original position. keep in
  1.3047 +   * mind that content could incrementally be coming in. we only want to stop
  1.3048 +   * when we reach our new position.
  1.3049 +   */
  1.3050 +void
  1.3051 +ScrollFrameHelper::ScrollToRestoredPosition()
  1.3052 +{
  1.3053 +  if (mRestorePos.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
  1.3054 +    return;
  1.3055 +  }
  1.3056 +  // make sure our scroll position did not change for where we last put
  1.3057 +  // it. if it does then the user must have moved it, and we no longer
  1.3058 +  // need to restore.
  1.3059 +  //
  1.3060 +  // In the RTL case, we check whether the scroll position changed using the
  1.3061 +  // logical scroll position, but we scroll to the physical scroll position in
  1.3062 +  // all cases
  1.3063 +
  1.3064 +  // if we didn't move, we still need to restore
  1.3065 +  if (GetLogicalScrollPosition() == mLastPos) {
  1.3066 +    // if our desired position is different to the scroll position, scroll.
  1.3067 +    // remember that we could be incrementally loading so we may enter
  1.3068 +    // and scroll many times.
  1.3069 +    if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
  1.3070 +      nsPoint scrollToPos = mRestorePos;
  1.3071 +      if (!IsLTR())
  1.3072 +        // convert from logical to physical scroll position
  1.3073 +        scrollToPos.x = mScrollPort.x -
  1.3074 +          (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
  1.3075 +      nsWeakFrame weakFrame(mOuter);
  1.3076 +      ScrollTo(scrollToPos, nsIScrollableFrame::INSTANT);
  1.3077 +      if (!weakFrame.IsAlive()) {
  1.3078 +        return;
  1.3079 +      }
  1.3080 +      // Re-get the scroll position, it might not be exactly equal to
  1.3081 +      // mRestorePos due to rounding and clamping.
  1.3082 +      mLastPos = GetLogicalScrollPosition();
  1.3083 +    } else {
  1.3084 +      // if we reached the position then stop
  1.3085 +      mRestorePos.y = -1;
  1.3086 +      mLastPos.x = -1;
  1.3087 +      mLastPos.y = -1;
  1.3088 +    }
  1.3089 +  } else {
  1.3090 +    // user moved the position, so we won't need to restore
  1.3091 +    mLastPos.x = -1;
  1.3092 +    mLastPos.y = -1;
  1.3093 +  }
  1.3094 +}
  1.3095 +
  1.3096 +nsresult
  1.3097 +ScrollFrameHelper::FireScrollPortEvent()
  1.3098 +{
  1.3099 +  mAsyncScrollPortEvent.Forget();
  1.3100 +
  1.3101 +  // Keep this in sync with PostOverflowEvent().
  1.3102 +  nsSize scrollportSize = mScrollPort.Size();
  1.3103 +  nsSize childSize = GetScrolledRect().Size();
  1.3104 +
  1.3105 +  bool newVerticalOverflow = childSize.height > scrollportSize.height;
  1.3106 +  bool vertChanged = mVerticalOverflow != newVerticalOverflow;
  1.3107 +
  1.3108 +  bool newHorizontalOverflow = childSize.width > scrollportSize.width;
  1.3109 +  bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
  1.3110 +
  1.3111 +  if (!vertChanged && !horizChanged) {
  1.3112 +    return NS_OK;
  1.3113 +  }
  1.3114 +
  1.3115 +  // If both either overflowed or underflowed then we dispatch only one
  1.3116 +  // DOM event.
  1.3117 +  bool both = vertChanged && horizChanged &&
  1.3118 +                newVerticalOverflow == newHorizontalOverflow;
  1.3119 +  InternalScrollPortEvent::orientType orient;
  1.3120 +  if (both) {
  1.3121 +    orient = InternalScrollPortEvent::both;
  1.3122 +    mHorizontalOverflow = newHorizontalOverflow;
  1.3123 +    mVerticalOverflow = newVerticalOverflow;
  1.3124 +  }
  1.3125 +  else if (vertChanged) {
  1.3126 +    orient = InternalScrollPortEvent::vertical;
  1.3127 +    mVerticalOverflow = newVerticalOverflow;
  1.3128 +    if (horizChanged) {
  1.3129 +      // We need to dispatch a separate horizontal DOM event. Do that the next
  1.3130 +      // time around since dispatching the vertical DOM event might destroy
  1.3131 +      // the frame.
  1.3132 +      PostOverflowEvent();
  1.3133 +    }
  1.3134 +  }
  1.3135 +  else {
  1.3136 +    orient = InternalScrollPortEvent::horizontal;
  1.3137 +    mHorizontalOverflow = newHorizontalOverflow;
  1.3138 +  }
  1.3139 +
  1.3140 +  InternalScrollPortEvent event(true,
  1.3141 +    (orient == InternalScrollPortEvent::horizontal ? mHorizontalOverflow :
  1.3142 +                                                     mVerticalOverflow) ?
  1.3143 +    NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW, nullptr);
  1.3144 +  event.orient = orient;
  1.3145 +  return EventDispatcher::Dispatch(mOuter->GetContent(),
  1.3146 +                                   mOuter->PresContext(), &event);
  1.3147 +}
  1.3148 +
  1.3149 +void
  1.3150 +ScrollFrameHelper::ReloadChildFrames()
  1.3151 +{
  1.3152 +  mScrolledFrame = nullptr;
  1.3153 +  mHScrollbarBox = nullptr;
  1.3154 +  mVScrollbarBox = nullptr;
  1.3155 +  mScrollCornerBox = nullptr;
  1.3156 +  mResizerBox = nullptr;
  1.3157 +
  1.3158 +  nsIFrame* frame = mOuter->GetFirstPrincipalChild();
  1.3159 +  while (frame) {
  1.3160 +    nsIContent* content = frame->GetContent();
  1.3161 +    if (content == mOuter->GetContent()) {
  1.3162 +      NS_ASSERTION(!mScrolledFrame, "Already found the scrolled frame");
  1.3163 +      mScrolledFrame = frame;
  1.3164 +    } else {
  1.3165 +      nsAutoString value;
  1.3166 +      content->GetAttr(kNameSpaceID_None, nsGkAtoms::orient, value);
  1.3167 +      if (!value.IsEmpty()) {
  1.3168 +        // probably a scrollbar then
  1.3169 +        if (value.LowerCaseEqualsLiteral("horizontal")) {
  1.3170 +          NS_ASSERTION(!mHScrollbarBox, "Found multiple horizontal scrollbars?");
  1.3171 +          mHScrollbarBox = frame;
  1.3172 +        } else {
  1.3173 +          NS_ASSERTION(!mVScrollbarBox, "Found multiple vertical scrollbars?");
  1.3174 +          mVScrollbarBox = frame;
  1.3175 +        }
  1.3176 +      } else if (content->Tag() == nsGkAtoms::resizer) {
  1.3177 +        NS_ASSERTION(!mResizerBox, "Found multiple resizers");
  1.3178 +        mResizerBox = frame;
  1.3179 +      } else if (content->Tag() == nsGkAtoms::scrollcorner) {
  1.3180 +        // probably a scrollcorner
  1.3181 +        NS_ASSERTION(!mScrollCornerBox, "Found multiple scrollcorners");
  1.3182 +        mScrollCornerBox = frame;
  1.3183 +      }
  1.3184 +    }
  1.3185 +
  1.3186 +    frame = frame->GetNextSibling();
  1.3187 +  }
  1.3188 +}
  1.3189 +
  1.3190 +nsresult
  1.3191 +ScrollFrameHelper::CreateAnonymousContent(
  1.3192 +  nsTArray<nsIAnonymousContentCreator::ContentInfo>& aElements)
  1.3193 +{
  1.3194 +  nsPresContext* presContext = mOuter->PresContext();
  1.3195 +  nsIFrame* parent = mOuter->GetParent();
  1.3196 +
  1.3197 +  // Don't create scrollbars if we're an SVG document being used as an image,
  1.3198 +  // or if we're printing/print previewing.
  1.3199 +  // (In the printing case, we allow scrollbars if this is the child of the
  1.3200 +  // viewport & paginated scrolling is enabled, because then we must be the
  1.3201 +  // scroll frame for the print preview window, & that does need scrollbars.)
  1.3202 +  if (presContext->Document()->IsBeingUsedAsImage() ||
  1.3203 +      (!presContext->IsDynamic() &&
  1.3204 +       !(mIsRoot && presContext->HasPaginatedScrolling()))) {
  1.3205 +    mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
  1.3206 +    return NS_OK;
  1.3207 +  }
  1.3208 +
  1.3209 +  // Check if the frame is resizable.
  1.3210 +  int8_t resizeStyle = mOuter->StyleDisplay()->mResize;
  1.3211 +  bool isResizable = resizeStyle != NS_STYLE_RESIZE_NONE;
  1.3212 +
  1.3213 +  nsIScrollableFrame *scrollable = do_QueryFrame(mOuter);
  1.3214 +
  1.3215 +  // If we're the scrollframe for the root, then we want to construct
  1.3216 +  // our scrollbar frames no matter what.  That way later dynamic
  1.3217 +  // changes to propagated overflow styles will show or hide
  1.3218 +  // scrollbars on the viewport without requiring frame reconstruction
  1.3219 +  // of the viewport (good!).
  1.3220 +  bool canHaveHorizontal;
  1.3221 +  bool canHaveVertical;
  1.3222 +  if (!mIsRoot) {
  1.3223 +    ScrollbarStyles styles = scrollable->GetScrollbarStyles();
  1.3224 +    canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
  1.3225 +    canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
  1.3226 +    if (!canHaveHorizontal && !canHaveVertical && !isResizable) {
  1.3227 +      // Nothing to do.
  1.3228 +      return NS_OK;
  1.3229 +    }
  1.3230 +  } else {
  1.3231 +    canHaveHorizontal = true;
  1.3232 +    canHaveVertical = true;
  1.3233 +  }
  1.3234 +
  1.3235 +  // The anonymous <div> used by <inputs> never gets scrollbars.
  1.3236 +  nsITextControlFrame* textFrame = do_QueryFrame(parent);
  1.3237 +  if (textFrame) {
  1.3238 +    // Make sure we are not a text area.
  1.3239 +    nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
  1.3240 +    if (!textAreaElement) {
  1.3241 +      mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = true;
  1.3242 +      return NS_OK;
  1.3243 +    }
  1.3244 +  }
  1.3245 +
  1.3246 +  nsNodeInfoManager *nodeInfoManager =
  1.3247 +    presContext->Document()->NodeInfoManager();
  1.3248 +  nsCOMPtr<nsINodeInfo> nodeInfo;
  1.3249 +  nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbar, nullptr,
  1.3250 +                                          kNameSpaceID_XUL,
  1.3251 +                                          nsIDOMNode::ELEMENT_NODE);
  1.3252 +  NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
  1.3253 +
  1.3254 +  if (canHaveHorizontal) {
  1.3255 +    nsCOMPtr<nsINodeInfo> ni = nodeInfo;
  1.3256 +    NS_TrustedNewXULElement(getter_AddRefs(mHScrollbarContent), ni.forget());
  1.3257 +    mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
  1.3258 +                                NS_LITERAL_STRING("horizontal"), false);
  1.3259 +    mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
  1.3260 +                                NS_LITERAL_STRING("always"), false);
  1.3261 +    if (mIsRoot) {
  1.3262 +      mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
  1.3263 +                                  NS_LITERAL_STRING("true"), false);
  1.3264 +    }
  1.3265 +    if (!aElements.AppendElement(mHScrollbarContent))
  1.3266 +      return NS_ERROR_OUT_OF_MEMORY;
  1.3267 +  }
  1.3268 +
  1.3269 +  if (canHaveVertical) {
  1.3270 +    nsCOMPtr<nsINodeInfo> ni = nodeInfo;
  1.3271 +    NS_TrustedNewXULElement(getter_AddRefs(mVScrollbarContent), ni.forget());
  1.3272 +    mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
  1.3273 +                                NS_LITERAL_STRING("vertical"), false);
  1.3274 +    mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
  1.3275 +                                NS_LITERAL_STRING("always"), false);
  1.3276 +    if (mIsRoot) {
  1.3277 +      mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
  1.3278 +                                  NS_LITERAL_STRING("true"), false);
  1.3279 +    }
  1.3280 +    if (!aElements.AppendElement(mVScrollbarContent))
  1.3281 +      return NS_ERROR_OUT_OF_MEMORY;
  1.3282 +  }
  1.3283 +
  1.3284 +  if (isResizable) {
  1.3285 +    nsCOMPtr<nsINodeInfo> nodeInfo;
  1.3286 +    nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nullptr,
  1.3287 +                                            kNameSpaceID_XUL,
  1.3288 +                                            nsIDOMNode::ELEMENT_NODE);
  1.3289 +    NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
  1.3290 +
  1.3291 +    NS_TrustedNewXULElement(getter_AddRefs(mResizerContent), nodeInfo.forget());
  1.3292 +
  1.3293 +    nsAutoString dir;
  1.3294 +    switch (resizeStyle) {
  1.3295 +      case NS_STYLE_RESIZE_HORIZONTAL:
  1.3296 +        if (IsScrollbarOnRight()) {
  1.3297 +          dir.AssignLiteral("right");
  1.3298 +        }
  1.3299 +        else {
  1.3300 +          dir.AssignLiteral("left");
  1.3301 +        }
  1.3302 +        break;
  1.3303 +      case NS_STYLE_RESIZE_VERTICAL:
  1.3304 +        dir.AssignLiteral("bottom");
  1.3305 +        break;
  1.3306 +      case NS_STYLE_RESIZE_BOTH:
  1.3307 +        dir.AssignLiteral("bottomend");
  1.3308 +        break;
  1.3309 +      default:
  1.3310 +        NS_WARNING("only resizable types should have resizers");
  1.3311 +    }
  1.3312 +    mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, false);
  1.3313 +
  1.3314 +    if (mIsRoot) {
  1.3315 +      nsIContent* browserRoot = GetBrowserRoot(mOuter->GetContent());
  1.3316 +      mCollapsedResizer = !(browserRoot &&
  1.3317 +                            browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer));
  1.3318 +    }
  1.3319 +    else {
  1.3320 +      mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
  1.3321 +                                    NS_LITERAL_STRING("_parent"), false);
  1.3322 +    }
  1.3323 +
  1.3324 +    mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
  1.3325 +                                  NS_LITERAL_STRING("always"), false);
  1.3326 +
  1.3327 +    if (!aElements.AppendElement(mResizerContent))
  1.3328 +      return NS_ERROR_OUT_OF_MEMORY;
  1.3329 +  }
  1.3330 +
  1.3331 +  if (canHaveHorizontal && canHaveVertical) {
  1.3332 +    nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nullptr,
  1.3333 +                                            kNameSpaceID_XUL,
  1.3334 +                                            nsIDOMNode::ELEMENT_NODE);
  1.3335 +    NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo.forget());
  1.3336 +    if (!aElements.AppendElement(mScrollCornerContent))
  1.3337 +      return NS_ERROR_OUT_OF_MEMORY;
  1.3338 +  }
  1.3339 +
  1.3340 +  return NS_OK;
  1.3341 +}
  1.3342 +
  1.3343 +void
  1.3344 +ScrollFrameHelper::AppendAnonymousContentTo(nsBaseContentList& aElements,
  1.3345 +                                                uint32_t aFilter)
  1.3346 +{
  1.3347 +  aElements.MaybeAppendElement(mHScrollbarContent);
  1.3348 +  aElements.MaybeAppendElement(mVScrollbarContent);
  1.3349 +  aElements.MaybeAppendElement(mScrollCornerContent);
  1.3350 +  aElements.MaybeAppendElement(mResizerContent);
  1.3351 +}
  1.3352 +
  1.3353 +void
  1.3354 +ScrollFrameHelper::Destroy()
  1.3355 +{
  1.3356 +  if (mScrollbarActivity) {
  1.3357 +    mScrollbarActivity->Destroy();
  1.3358 +    mScrollbarActivity = nullptr;
  1.3359 +  }
  1.3360 +
  1.3361 +  // Unbind any content created in CreateAnonymousContent from the tree
  1.3362 +  nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent);
  1.3363 +  nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent);
  1.3364 +  nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent);
  1.3365 +  nsContentUtils::DestroyAnonymousContent(&mResizerContent);
  1.3366 +
  1.3367 +  if (mPostedReflowCallback) {
  1.3368 +    mOuter->PresContext()->PresShell()->CancelReflowCallback(this);
  1.3369 +    mPostedReflowCallback = false;
  1.3370 +  }
  1.3371 +}
  1.3372 +
  1.3373 +/**
  1.3374 + * Called when we want to update the scrollbar position, either because scrolling happened
  1.3375 + * or the user moved the scrollbar position and we need to undo that (e.g., when the user
  1.3376 + * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
  1.3377 + * to its initial position for the start of the smooth sequence).
  1.3378 + */
  1.3379 +void
  1.3380 +ScrollFrameHelper::UpdateScrollbarPosition()
  1.3381 +{
  1.3382 +  nsWeakFrame weakFrame(mOuter);
  1.3383 +  mFrameIsUpdatingScrollbar = true;
  1.3384 +
  1.3385 +  nsPoint pt = GetScrollPosition();
  1.3386 +  if (mVScrollbarBox) {
  1.3387 +    SetCoordAttribute(mVScrollbarBox->GetContent(), nsGkAtoms::curpos,
  1.3388 +                      pt.y - GetScrolledRect().y);
  1.3389 +    if (!weakFrame.IsAlive()) {
  1.3390 +      return;
  1.3391 +    }
  1.3392 +  }
  1.3393 +  if (mHScrollbarBox) {
  1.3394 +    SetCoordAttribute(mHScrollbarBox->GetContent(), nsGkAtoms::curpos,
  1.3395 +                      pt.x - GetScrolledRect().x);
  1.3396 +    if (!weakFrame.IsAlive()) {
  1.3397 +      return;
  1.3398 +    }
  1.3399 +  }
  1.3400 +
  1.3401 +  mFrameIsUpdatingScrollbar = false;
  1.3402 +}
  1.3403 +
  1.3404 +void ScrollFrameHelper::CurPosAttributeChanged(nsIContent* aContent)
  1.3405 +{
  1.3406 +  NS_ASSERTION(aContent, "aContent must not be null");
  1.3407 +  NS_ASSERTION((mHScrollbarBox && mHScrollbarBox->GetContent() == aContent) ||
  1.3408 +               (mVScrollbarBox && mVScrollbarBox->GetContent() == aContent),
  1.3409 +               "unexpected child");
  1.3410 +
  1.3411 +  // Attribute changes on the scrollbars happen in one of three ways:
  1.3412 +  // 1) The scrollbar changed the attribute in response to some user event
  1.3413 +  // 2) We changed the attribute in response to a ScrollPositionDidChange
  1.3414 +  // callback from the scrolling view
  1.3415 +  // 3) We changed the attribute to adjust the scrollbars for the start
  1.3416 +  // of a smooth scroll operation
  1.3417 +  //
  1.3418 +  // In cases 2 and 3 we do not need to scroll because we're just
  1.3419 +  // updating our scrollbar.
  1.3420 +  if (mFrameIsUpdatingScrollbar)
  1.3421 +    return;
  1.3422 +
  1.3423 +  nsRect scrolledRect = GetScrolledRect();
  1.3424 +
  1.3425 +  nsPoint current = GetScrollPosition() - scrolledRect.TopLeft();
  1.3426 +  nsPoint dest;
  1.3427 +  nsRect allowedRange;
  1.3428 +  dest.x = GetCoordAttribute(mHScrollbarBox, nsGkAtoms::curpos, current.x,
  1.3429 +                             &allowedRange.x, &allowedRange.width);
  1.3430 +  dest.y = GetCoordAttribute(mVScrollbarBox, nsGkAtoms::curpos, current.y,
  1.3431 +                             &allowedRange.y, &allowedRange.height);
  1.3432 +  current += scrolledRect.TopLeft();
  1.3433 +  dest += scrolledRect.TopLeft();
  1.3434 +  allowedRange += scrolledRect.TopLeft();
  1.3435 +
  1.3436 +  // Don't try to scroll if we're already at an acceptable place.
  1.3437 +  // Don't call Contains here since Contains returns false when the point is
  1.3438 +  // on the bottom or right edge of the rectangle.
  1.3439 +  if (allowedRange.ClampPoint(current) == current) {
  1.3440 +    return;
  1.3441 +  }
  1.3442 +
  1.3443 +  if (mScrollbarActivity) {
  1.3444 +    nsRefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
  1.3445 +    scrollbarActivity->ActivityOccurred();
  1.3446 +  }
  1.3447 +
  1.3448 +  bool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::smooth);
  1.3449 +  if (isSmooth) {
  1.3450 +    // Make sure an attribute-setting callback occurs even if the view
  1.3451 +    // didn't actually move yet.  We need to make sure other listeners
  1.3452 +    // see that the scroll position is not (yet) what they thought it
  1.3453 +    // was.
  1.3454 +    nsWeakFrame weakFrame(mOuter);
  1.3455 +    UpdateScrollbarPosition();
  1.3456 +    if (!weakFrame.IsAlive()) {
  1.3457 +      return;
  1.3458 +    }
  1.3459 +  }
  1.3460 +  ScrollToWithOrigin(dest,
  1.3461 +                     isSmooth ? nsIScrollableFrame::SMOOTH : nsIScrollableFrame::INSTANT,
  1.3462 +                     nsGkAtoms::scrollbars, &allowedRange);
  1.3463 +  // 'this' might be destroyed here
  1.3464 +}
  1.3465 +
  1.3466 +/* ============= Scroll events ========== */
  1.3467 +
  1.3468 +NS_IMETHODIMP
  1.3469 +ScrollFrameHelper::ScrollEvent::Run()
  1.3470 +{
  1.3471 +  if (mHelper)
  1.3472 +    mHelper->FireScrollEvent();
  1.3473 +  return NS_OK;
  1.3474 +}
  1.3475 +
  1.3476 +void
  1.3477 +ScrollFrameHelper::FireScrollEvent()
  1.3478 +{
  1.3479 +  mScrollEvent.Forget();
  1.3480 +
  1.3481 +  WidgetGUIEvent event(true, NS_SCROLL_EVENT, nullptr);
  1.3482 +  nsEventStatus status = nsEventStatus_eIgnore;
  1.3483 +  nsIContent* content = mOuter->GetContent();
  1.3484 +  nsPresContext* prescontext = mOuter->PresContext();
  1.3485 +  // Fire viewport scroll events at the document (where they
  1.3486 +  // will bubble to the window)
  1.3487 +  if (mIsRoot) {
  1.3488 +    nsIDocument* doc = content->GetCurrentDoc();
  1.3489 +    if (doc) {
  1.3490 +      EventDispatcher::Dispatch(doc, prescontext, &event, nullptr,  &status);
  1.3491 +    }
  1.3492 +  } else {
  1.3493 +    // scroll events fired at elements don't bubble (although scroll events
  1.3494 +    // fired at documents do, to the window)
  1.3495 +    event.mFlags.mBubbles = false;
  1.3496 +    EventDispatcher::Dispatch(content, prescontext, &event, nullptr, &status);
  1.3497 +  }
  1.3498 +}
  1.3499 +
  1.3500 +void
  1.3501 +ScrollFrameHelper::PostScrollEvent()
  1.3502 +{
  1.3503 +  if (mScrollEvent.IsPending())
  1.3504 +    return;
  1.3505 +
  1.3506 +  nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext();
  1.3507 +  if (!rpc)
  1.3508 +    return;
  1.3509 +  mScrollEvent = new ScrollEvent(this);
  1.3510 +  rpc->AddWillPaintObserver(mScrollEvent.get());
  1.3511 +}
  1.3512 +
  1.3513 +NS_IMETHODIMP
  1.3514 +ScrollFrameHelper::AsyncScrollPortEvent::Run()
  1.3515 +{
  1.3516 +  if (mHelper) {
  1.3517 +    mHelper->mOuter->PresContext()->GetPresShell()->
  1.3518 +      FlushPendingNotifications(Flush_InterruptibleLayout);
  1.3519 +  }
  1.3520 +  return mHelper ? mHelper->FireScrollPortEvent() : NS_OK;
  1.3521 +}
  1.3522 +
  1.3523 +bool
  1.3524 +nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
  1.3525 +{
  1.3526 +  if (!mHelper.mHScrollbarBox)
  1.3527 +    return true;
  1.3528 +
  1.3529 +  return AddRemoveScrollbar(aState, aOnBottom, true, true);
  1.3530 +}
  1.3531 +
  1.3532 +bool
  1.3533 +nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
  1.3534 +{
  1.3535 +  if (!mHelper.mVScrollbarBox)
  1.3536 +    return true;
  1.3537 +
  1.3538 +  return AddRemoveScrollbar(aState, aOnRight, false, true);
  1.3539 +}
  1.3540 +
  1.3541 +void
  1.3542 +nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState, bool aOnBottom)
  1.3543 +{
  1.3544 +  // removing a scrollbar should always fit
  1.3545 +  DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnBottom, true, false);
  1.3546 +  NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
  1.3547 +}
  1.3548 +
  1.3549 +void
  1.3550 +nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState, bool aOnRight)
  1.3551 +{
  1.3552 +  // removing a scrollbar should always fit
  1.3553 +  DebugOnly<bool> result = AddRemoveScrollbar(aState, aOnRight, false, false);
  1.3554 +  NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
  1.3555 +}
  1.3556 +
  1.3557 +bool
  1.3558 +nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState,
  1.3559 +                                     bool aOnRightOrBottom, bool aHorizontal, bool aAdd)
  1.3560 +{
  1.3561 +  if (aHorizontal) {
  1.3562 +     if (mHelper.mNeverHasHorizontalScrollbar || !mHelper.mHScrollbarBox)
  1.3563 +       return false;
  1.3564 +
  1.3565 +     nsSize hSize = mHelper.mHScrollbarBox->GetPrefSize(aState);
  1.3566 +     nsBox::AddMargin(mHelper.mHScrollbarBox, hSize);
  1.3567 +
  1.3568 +     mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, aAdd);
  1.3569 +
  1.3570 +     bool hasHorizontalScrollbar;
  1.3571 +     bool fit = AddRemoveScrollbar(hasHorizontalScrollbar,
  1.3572 +                                     mHelper.mScrollPort.y,
  1.3573 +                                     mHelper.mScrollPort.height,
  1.3574 +                                     hSize.height, aOnRightOrBottom, aAdd);
  1.3575 +     mHelper.mHasHorizontalScrollbar = hasHorizontalScrollbar;    // because mHasHorizontalScrollbar is a bool
  1.3576 +     if (!fit)
  1.3577 +        mHelper.SetScrollbarVisibility(mHelper.mHScrollbarBox, !aAdd);
  1.3578 +
  1.3579 +     return fit;
  1.3580 +  } else {
  1.3581 +     if (mHelper.mNeverHasVerticalScrollbar || !mHelper.mVScrollbarBox)
  1.3582 +       return false;
  1.3583 +
  1.3584 +     nsSize vSize = mHelper.mVScrollbarBox->GetPrefSize(aState);
  1.3585 +     nsBox::AddMargin(mHelper.mVScrollbarBox, vSize);
  1.3586 +
  1.3587 +     mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, aAdd);
  1.3588 +
  1.3589 +     bool hasVerticalScrollbar;
  1.3590 +     bool fit = AddRemoveScrollbar(hasVerticalScrollbar,
  1.3591 +                                     mHelper.mScrollPort.x,
  1.3592 +                                     mHelper.mScrollPort.width,
  1.3593 +                                     vSize.width, aOnRightOrBottom, aAdd);
  1.3594 +     mHelper.mHasVerticalScrollbar = hasVerticalScrollbar;    // because mHasVerticalScrollbar is a bool
  1.3595 +     if (!fit)
  1.3596 +        mHelper.SetScrollbarVisibility(mHelper.mVScrollbarBox, !aAdd);
  1.3597 +
  1.3598 +     return fit;
  1.3599 +  }
  1.3600 +}
  1.3601 +
  1.3602 +bool
  1.3603 +nsXULScrollFrame::AddRemoveScrollbar(bool& aHasScrollbar, nscoord& aXY,
  1.3604 +                                     nscoord& aSize, nscoord aSbSize,
  1.3605 +                                     bool aOnRightOrBottom, bool aAdd)
  1.3606 +{
  1.3607 +   nscoord size = aSize;
  1.3608 +   nscoord xy = aXY;
  1.3609 +
  1.3610 +   if (size != NS_INTRINSICSIZE) {
  1.3611 +     if (aAdd) {
  1.3612 +        size -= aSbSize;
  1.3613 +        if (!aOnRightOrBottom && size >= 0)
  1.3614 +          xy += aSbSize;
  1.3615 +     } else {
  1.3616 +        size += aSbSize;
  1.3617 +        if (!aOnRightOrBottom)
  1.3618 +          xy -= aSbSize;
  1.3619 +     }
  1.3620 +   }
  1.3621 +
  1.3622 +   // not enough room? Yes? Return true.
  1.3623 +   if (size >= 0) {
  1.3624 +       aHasScrollbar = aAdd;
  1.3625 +       aSize = size;
  1.3626 +       aXY = xy;
  1.3627 +       return true;
  1.3628 +   }
  1.3629 +
  1.3630 +   aHasScrollbar = false;
  1.3631 +   return false;
  1.3632 +}
  1.3633 +
  1.3634 +void
  1.3635 +nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState,
  1.3636 +                                   const nsPoint& aScrollPosition)
  1.3637 +{
  1.3638 +  uint32_t oldflags = aState.LayoutFlags();
  1.3639 +  nsRect childRect = nsRect(mHelper.mScrollPort.TopLeft() - aScrollPosition,
  1.3640 +                            mHelper.mScrollPort.Size());
  1.3641 +  int32_t flags = NS_FRAME_NO_MOVE_VIEW;
  1.3642 +
  1.3643 +  nsSize minSize = mHelper.mScrolledFrame->GetMinSize(aState);
  1.3644 +
  1.3645 +  if (minSize.height > childRect.height)
  1.3646 +    childRect.height = minSize.height;
  1.3647 +
  1.3648 +  if (minSize.width > childRect.width)
  1.3649 +    childRect.width = minSize.width;
  1.3650 +
  1.3651 +  aState.SetLayoutFlags(flags);
  1.3652 +  ClampAndSetBounds(aState, childRect, aScrollPosition);
  1.3653 +  mHelper.mScrolledFrame->Layout(aState);
  1.3654 +
  1.3655 +  childRect = mHelper.mScrolledFrame->GetRect();
  1.3656 +
  1.3657 +  if (childRect.width < mHelper.mScrollPort.width ||
  1.3658 +      childRect.height < mHelper.mScrollPort.height)
  1.3659 +  {
  1.3660 +    childRect.width = std::max(childRect.width, mHelper.mScrollPort.width);
  1.3661 +    childRect.height = std::max(childRect.height, mHelper.mScrollPort.height);
  1.3662 +
  1.3663 +    // remove overflow areas when we update the bounds,
  1.3664 +    // because we've already accounted for it
  1.3665 +    // REVIEW: Have we accounted for both?
  1.3666 +    ClampAndSetBounds(aState, childRect, aScrollPosition, true);
  1.3667 +  }
  1.3668 +
  1.3669 +  aState.SetLayoutFlags(oldflags);
  1.3670 +
  1.3671 +}
  1.3672 +
  1.3673 +void ScrollFrameHelper::PostOverflowEvent()
  1.3674 +{
  1.3675 +  if (mAsyncScrollPortEvent.IsPending())
  1.3676 +    return;
  1.3677 +
  1.3678 +  // Keep this in sync with FireScrollPortEvent().
  1.3679 +  nsSize scrollportSize = mScrollPort.Size();
  1.3680 +  nsSize childSize = GetScrolledRect().Size();
  1.3681 +
  1.3682 +  bool newVerticalOverflow = childSize.height > scrollportSize.height;
  1.3683 +  bool vertChanged = mVerticalOverflow != newVerticalOverflow;
  1.3684 +
  1.3685 +  bool newHorizontalOverflow = childSize.width > scrollportSize.width;
  1.3686 +  bool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
  1.3687 +
  1.3688 +  if (!vertChanged && !horizChanged) {
  1.3689 +    return;
  1.3690 +  }
  1.3691 +
  1.3692 +  nsRootPresContext* rpc = mOuter->PresContext()->GetRootPresContext();
  1.3693 +  if (!rpc)
  1.3694 +    return;
  1.3695 +
  1.3696 +  mAsyncScrollPortEvent = new AsyncScrollPortEvent(this);
  1.3697 +  rpc->AddWillPaintObserver(mAsyncScrollPortEvent.get());
  1.3698 +}
  1.3699 +
  1.3700 +bool
  1.3701 +ScrollFrameHelper::IsLTR() const
  1.3702 +{
  1.3703 +  //TODO make bidi code set these from preferences
  1.3704 +
  1.3705 +  nsIFrame *frame = mOuter;
  1.3706 +  // XXX This is a bit on the slow side.
  1.3707 +  if (mIsRoot) {
  1.3708 +    // If we're the root scrollframe, we need the root element's style data.
  1.3709 +    nsPresContext *presContext = mOuter->PresContext();
  1.3710 +    nsIDocument *document = presContext->Document();
  1.3711 +    Element *root = document->GetRootElement();
  1.3712 +
  1.3713 +    // But for HTML and XHTML we want the body element.
  1.3714 +    nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(document);
  1.3715 +    if (htmlDoc) {
  1.3716 +      Element *bodyElement = document->GetBodyElement();
  1.3717 +      if (bodyElement)
  1.3718 +        root = bodyElement; // we can trust the document to hold on to it
  1.3719 +    }
  1.3720 +
  1.3721 +    if (root) {
  1.3722 +      nsIFrame *rootsFrame = root->GetPrimaryFrame();
  1.3723 +      if (rootsFrame)
  1.3724 +        frame = rootsFrame;
  1.3725 +    }
  1.3726 +  }
  1.3727 +
  1.3728 +  return frame->StyleVisibility()->mDirection != NS_STYLE_DIRECTION_RTL;
  1.3729 +}
  1.3730 +
  1.3731 +bool
  1.3732 +ScrollFrameHelper::IsScrollbarOnRight() const
  1.3733 +{
  1.3734 +  nsPresContext *presContext = mOuter->PresContext();
  1.3735 +
  1.3736 +  // The position of the scrollbar in top-level windows depends on the pref
  1.3737 +  // layout.scrollbar.side. For non-top-level elements, it depends only on the
  1.3738 +  // directionaliy of the element (equivalent to a value of "1" for the pref).
  1.3739 +  if (!mIsRoot)
  1.3740 +    return IsLTR();
  1.3741 +  switch (presContext->GetCachedIntPref(kPresContext_ScrollbarSide)) {
  1.3742 +    default:
  1.3743 +    case 0: // UI directionality
  1.3744 +      return presContext->GetCachedIntPref(kPresContext_BidiDirection)
  1.3745 +             == IBMBIDI_TEXTDIRECTION_LTR;
  1.3746 +    case 1: // Document / content directionality
  1.3747 +      return IsLTR();
  1.3748 +    case 2: // Always right
  1.3749 +      return true;
  1.3750 +    case 3: // Always left
  1.3751 +      return false;
  1.3752 +  }
  1.3753 +}
  1.3754 +
  1.3755 +/**
  1.3756 + * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
  1.3757 + * cause any of the scrollbars to need to be reflowed.
  1.3758 + */
  1.3759 +nsresult
  1.3760 +nsXULScrollFrame::Layout(nsBoxLayoutState& aState)
  1.3761 +{
  1.3762 +  bool scrollbarRight = mHelper.IsScrollbarOnRight();
  1.3763 +  bool scrollbarBottom = true;
  1.3764 +
  1.3765 +  // get the content rect
  1.3766 +  nsRect clientRect(0,0,0,0);
  1.3767 +  GetClientRect(clientRect);
  1.3768 +
  1.3769 +  nsRect oldScrollAreaBounds = mHelper.mScrollPort;
  1.3770 +  nsPoint oldScrollPosition = mHelper.GetLogicalScrollPosition();
  1.3771 +
  1.3772 +  // the scroll area size starts off as big as our content area
  1.3773 +  mHelper.mScrollPort = clientRect;
  1.3774 +
  1.3775 +  /**************
  1.3776 +   Our basic strategy here is to first try laying out the content with
  1.3777 +   the scrollbars in their current state. We're hoping that that will
  1.3778 +   just "work"; the content will overflow wherever there's a scrollbar
  1.3779 +   already visible. If that does work, then there's no need to lay out
  1.3780 +   the scrollarea. Otherwise we fix up the scrollbars; first we add a
  1.3781 +   vertical one to scroll the content if necessary, or remove it if
  1.3782 +   it's not needed. Then we reflow the content if the scrollbar
  1.3783 +   changed.  Then we add a horizontal scrollbar if necessary (or
  1.3784 +   remove if not needed), and if that changed, we reflow the content
  1.3785 +   again. At this point, any scrollbars that are needed to scroll the
  1.3786 +   content have been added.
  1.3787 +
  1.3788 +   In the second phase we check to see if any scrollbars are too small
  1.3789 +   to display, and if so, we remove them. We check the horizontal
  1.3790 +   scrollbar first; removing it might make room for the vertical
  1.3791 +   scrollbar, and if we have room for just one scrollbar we'll save
  1.3792 +   the vertical one.
  1.3793 +
  1.3794 +   Finally we position and size the scrollbars and scrollcorner (the
  1.3795 +   square that is needed in the corner of the window when two
  1.3796 +   scrollbars are visible), and reflow any fixed position views
  1.3797 +   (if we're the viewport and we added or removed a scrollbar).
  1.3798 +   **************/
  1.3799 +
  1.3800 +  ScrollbarStyles styles = GetScrollbarStyles();
  1.3801 +
  1.3802 +  // Look at our style do we always have vertical or horizontal scrollbars?
  1.3803 +  if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
  1.3804 +     mHelper.mHasHorizontalScrollbar = true;
  1.3805 +  if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
  1.3806 +     mHelper.mHasVerticalScrollbar = true;
  1.3807 +
  1.3808 +  if (mHelper.mHasHorizontalScrollbar)
  1.3809 +     AddHorizontalScrollbar(aState, scrollbarBottom);
  1.3810 +
  1.3811 +  if (mHelper.mHasVerticalScrollbar)
  1.3812 +     AddVerticalScrollbar(aState, scrollbarRight);
  1.3813 +
  1.3814 +  // layout our the scroll area
  1.3815 +  LayoutScrollArea(aState, oldScrollPosition);
  1.3816 +
  1.3817 +  // now look at the content area and see if we need scrollbars or not
  1.3818 +  bool needsLayout = false;
  1.3819 +
  1.3820 +  // if we have 'auto' scrollbars look at the vertical case
  1.3821 +  if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
  1.3822 +    // These are only good until the call to LayoutScrollArea.
  1.3823 +    nsRect scrolledRect = mHelper.GetScrolledRect();
  1.3824 +
  1.3825 +    // There are two cases to consider
  1.3826 +      if (scrolledRect.height <= mHelper.mScrollPort.height
  1.3827 +          || styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
  1.3828 +        if (mHelper.mHasVerticalScrollbar) {
  1.3829 +          // We left room for the vertical scrollbar, but it's not needed;
  1.3830 +          // remove it.
  1.3831 +          RemoveVerticalScrollbar(aState, scrollbarRight);
  1.3832 +          needsLayout = true;
  1.3833 +        }
  1.3834 +      } else {
  1.3835 +        if (!mHelper.mHasVerticalScrollbar) {
  1.3836 +          // We didn't leave room for the vertical scrollbar, but it turns
  1.3837 +          // out we needed it
  1.3838 +          if (AddVerticalScrollbar(aState, scrollbarRight))
  1.3839 +            needsLayout = true;
  1.3840 +        }
  1.3841 +    }
  1.3842 +
  1.3843 +    // ok layout at the right size
  1.3844 +    if (needsLayout) {
  1.3845 +       nsBoxLayoutState resizeState(aState);
  1.3846 +       LayoutScrollArea(resizeState, oldScrollPosition);
  1.3847 +       needsLayout = false;
  1.3848 +    }
  1.3849 +  }
  1.3850 +
  1.3851 +
  1.3852 +  // if scrollbars are auto look at the horizontal case
  1.3853 +  if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
  1.3854 +  {
  1.3855 +    // These are only good until the call to LayoutScrollArea.
  1.3856 +    nsRect scrolledRect = mHelper.GetScrolledRect();
  1.3857 +
  1.3858 +    // if the child is wider that the scroll area
  1.3859 +    // and we don't have a scrollbar add one.
  1.3860 +    if ((scrolledRect.width > mHelper.mScrollPort.width)
  1.3861 +        && styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
  1.3862 +
  1.3863 +      if (!mHelper.mHasHorizontalScrollbar) {
  1.3864 +           // no scrollbar?
  1.3865 +          if (AddHorizontalScrollbar(aState, scrollbarBottom))
  1.3866 +             needsLayout = true;
  1.3867 +
  1.3868 +           // if we added a horizontal scrollbar and we did not have a vertical
  1.3869 +           // there is a chance that by adding the horizontal scrollbar we will
  1.3870 +           // suddenly need a vertical scrollbar. Is a special case but its
  1.3871 +           // important.
  1.3872 +           //if (!mHasVerticalScrollbar && scrolledRect.height > scrollAreaRect.height - sbSize.height)
  1.3873 +           //  printf("****Gfx Scrollbar Special case hit!!*****\n");
  1.3874 +
  1.3875 +      }
  1.3876 +    } else {
  1.3877 +        // if the area is smaller or equal to and we have a scrollbar then
  1.3878 +        // remove it.
  1.3879 +      if (mHelper.mHasHorizontalScrollbar) {
  1.3880 +        RemoveHorizontalScrollbar(aState, scrollbarBottom);
  1.3881 +        needsLayout = true;
  1.3882 +      }
  1.3883 +    }
  1.3884 +  }
  1.3885 +
  1.3886 +  // we only need to set the rect. The inner child stays the same size.
  1.3887 +  if (needsLayout) {
  1.3888 +     nsBoxLayoutState resizeState(aState);
  1.3889 +     LayoutScrollArea(resizeState, oldScrollPosition);
  1.3890 +     needsLayout = false;
  1.3891 +  }
  1.3892 +
  1.3893 +  // get the preferred size of the scrollbars
  1.3894 +  nsSize hMinSize(0, 0);
  1.3895 +  if (mHelper.mHScrollbarBox && mHelper.mHasHorizontalScrollbar) {
  1.3896 +    GetScrollbarMetrics(aState, mHelper.mHScrollbarBox, &hMinSize, nullptr, false);
  1.3897 +  }
  1.3898 +  nsSize vMinSize(0, 0);
  1.3899 +  if (mHelper.mVScrollbarBox && mHelper.mHasVerticalScrollbar) {
  1.3900 +    GetScrollbarMetrics(aState, mHelper.mVScrollbarBox, &vMinSize, nullptr, true);
  1.3901 +  }
  1.3902 +
  1.3903 +  // Disable scrollbars that are too small
  1.3904 +  // Disable horizontal scrollbar first. If we have to disable only one
  1.3905 +  // scrollbar, we'd rather keep the vertical scrollbar.
  1.3906 +  // Note that we always give horizontal scrollbars their preferred height,
  1.3907 +  // never their min-height. So check that there's room for the preferred height.
  1.3908 +  if (mHelper.mHasHorizontalScrollbar &&
  1.3909 +      (hMinSize.width > clientRect.width - vMinSize.width
  1.3910 +       || hMinSize.height > clientRect.height)) {
  1.3911 +    RemoveHorizontalScrollbar(aState, scrollbarBottom);
  1.3912 +    needsLayout = true;
  1.3913 +  }
  1.3914 +  // Now disable vertical scrollbar if necessary
  1.3915 +  if (mHelper.mHasVerticalScrollbar &&
  1.3916 +      (vMinSize.height > clientRect.height - hMinSize.height
  1.3917 +       || vMinSize.width > clientRect.width)) {
  1.3918 +    RemoveVerticalScrollbar(aState, scrollbarRight);
  1.3919 +    needsLayout = true;
  1.3920 +  }
  1.3921 +
  1.3922 +  // we only need to set the rect. The inner child stays the same size.
  1.3923 +  if (needsLayout) {
  1.3924 +    nsBoxLayoutState resizeState(aState);
  1.3925 +    LayoutScrollArea(resizeState, oldScrollPosition);
  1.3926 +  }
  1.3927 +
  1.3928 +  if (!mHelper.mSupppressScrollbarUpdate) {
  1.3929 +    mHelper.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds);
  1.3930 +  }
  1.3931 +  if (!mHelper.mPostedReflowCallback) {
  1.3932 +    // Make sure we'll try scrolling to restored position
  1.3933 +    PresContext()->PresShell()->PostReflowCallback(&mHelper);
  1.3934 +    mHelper.mPostedReflowCallback = true;
  1.3935 +  }
  1.3936 +  if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
  1.3937 +    mHelper.mHadNonInitialReflow = true;
  1.3938 +  }
  1.3939 +
  1.3940 +  mHelper.UpdateSticky();
  1.3941 +
  1.3942 +  // Set up overflow areas for block frames for the benefit of
  1.3943 +  // text-overflow.
  1.3944 +  nsIFrame* f = mHelper.mScrolledFrame->GetContentInsertionFrame();
  1.3945 +  if (nsLayoutUtils::GetAsBlock(f)) {
  1.3946 +    nsRect origRect = f->GetRect();
  1.3947 +    nsRect clippedRect = origRect;
  1.3948 +    clippedRect.MoveBy(mHelper.mScrollPort.TopLeft());
  1.3949 +    clippedRect.IntersectRect(clippedRect, mHelper.mScrollPort);
  1.3950 +    nsOverflowAreas overflow = f->GetOverflowAreas();
  1.3951 +    f->FinishAndStoreOverflow(overflow, clippedRect.Size());
  1.3952 +    clippedRect.MoveTo(origRect.TopLeft());
  1.3953 +    f->SetRect(clippedRect);
  1.3954 +  }
  1.3955 +
  1.3956 +  mHelper.PostOverflowEvent();
  1.3957 +  return NS_OK;
  1.3958 +}
  1.3959 +
  1.3960 +void
  1.3961 +ScrollFrameHelper::FinishReflowForScrollbar(nsIContent* aContent,
  1.3962 +                                                nscoord aMinXY, nscoord aMaxXY,
  1.3963 +                                                nscoord aCurPosXY,
  1.3964 +                                                nscoord aPageIncrement,
  1.3965 +                                                nscoord aIncrement)
  1.3966 +{
  1.3967 +  // Scrollbars assume zero is the minimum position, so translate for them.
  1.3968 +  SetCoordAttribute(aContent, nsGkAtoms::curpos, aCurPosXY - aMinXY);
  1.3969 +  SetScrollbarEnabled(aContent, aMaxXY - aMinXY);
  1.3970 +  SetCoordAttribute(aContent, nsGkAtoms::maxpos, aMaxXY - aMinXY);
  1.3971 +  SetCoordAttribute(aContent, nsGkAtoms::pageincrement, aPageIncrement);
  1.3972 +  SetCoordAttribute(aContent, nsGkAtoms::increment, aIncrement);
  1.3973 +}
  1.3974 +
  1.3975 +bool
  1.3976 +ScrollFrameHelper::ReflowFinished()
  1.3977 +{
  1.3978 +  nsAutoScriptBlocker scriptBlocker;
  1.3979 +  mPostedReflowCallback = false;
  1.3980 +
  1.3981 +  ScrollToRestoredPosition();
  1.3982 +
  1.3983 +  // Clamp current scroll position to new bounds. Normally this won't
  1.3984 +  // do anything.
  1.3985 +  nsPoint currentScrollPos = GetScrollPosition();
  1.3986 +  ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
  1.3987 +  if (!mAsyncScroll) {
  1.3988 +    // We need to have mDestination track the current scroll position,
  1.3989 +    // in case it falls outside the new reflow area. mDestination is used
  1.3990 +    // by ScrollBy as its starting position.
  1.3991 +    mDestination = GetScrollPosition();
  1.3992 +  }
  1.3993 +
  1.3994 +  if (NS_SUBTREE_DIRTY(mOuter) || !mUpdateScrollbarAttributes)
  1.3995 +    return false;
  1.3996 +
  1.3997 +  mUpdateScrollbarAttributes = false;
  1.3998 +
  1.3999 +  // Update scrollbar attributes.
  1.4000 +  nsPresContext* presContext = mOuter->PresContext();
  1.4001 +
  1.4002 +  if (mMayHaveDirtyFixedChildren) {
  1.4003 +    mMayHaveDirtyFixedChildren = false;
  1.4004 +    nsIFrame* parentFrame = mOuter->GetParent();
  1.4005 +    for (nsIFrame* fixedChild =
  1.4006 +           parentFrame->GetFirstChild(nsIFrame::kFixedList);
  1.4007 +         fixedChild; fixedChild = fixedChild->GetNextSibling()) {
  1.4008 +      // force a reflow of the fixed child
  1.4009 +      presContext->PresShell()->
  1.4010 +        FrameNeedsReflow(fixedChild, nsIPresShell::eResize,
  1.4011 +                         NS_FRAME_HAS_DIRTY_CHILDREN);
  1.4012 +    }
  1.4013 +  }
  1.4014 +
  1.4015 +  nsRect scrolledContentRect = GetScrolledRect();
  1.4016 +  nscoord minX = scrolledContentRect.x;
  1.4017 +  nscoord maxX = scrolledContentRect.XMost() - mScrollPort.width;
  1.4018 +  nscoord minY = scrolledContentRect.y;
  1.4019 +  nscoord maxY = scrolledContentRect.YMost() - mScrollPort.height;
  1.4020 +
  1.4021 +  // Suppress handling of the curpos attribute changes we make here.
  1.4022 +  NS_ASSERTION(!mFrameIsUpdatingScrollbar, "We shouldn't be reentering here");
  1.4023 +  mFrameIsUpdatingScrollbar = true;
  1.4024 +
  1.4025 +  nsCOMPtr<nsIContent> vScroll =
  1.4026 +    mVScrollbarBox ? mVScrollbarBox->GetContent() : nullptr;
  1.4027 +  nsCOMPtr<nsIContent> hScroll =
  1.4028 +    mHScrollbarBox ? mHScrollbarBox->GetContent() : nullptr;
  1.4029 +
  1.4030 +  // Note, in some cases mOuter may get deleted while finishing reflow
  1.4031 +  // for scrollbars. XXXmats is this still true now that we have a script
  1.4032 +  // blocker in this scope? (if not, remove the weak frame checks below).
  1.4033 +  if (vScroll || hScroll) {
  1.4034 +    nsWeakFrame weakFrame(mOuter);
  1.4035 +    nsPoint scrollPos = GetScrollPosition();
  1.4036 +    nsSize lineScrollAmount = GetLineScrollAmount();
  1.4037 +    if (vScroll) {
  1.4038 +      const double kScrollMultiplier =
  1.4039 +        Preferences::GetInt("toolkit.scrollbox.verticalScrollDistance",
  1.4040 +                            NS_DEFAULT_VERTICAL_SCROLL_DISTANCE);
  1.4041 +      nscoord increment = lineScrollAmount.height * kScrollMultiplier;
  1.4042 +      // We normally use (scrollArea.height - increment) for height
  1.4043 +      // of page scrolling.  However, it is too small when
  1.4044 +      // increment is very large. (If increment is larger than
  1.4045 +      // scrollArea.height, direction of scrolling will be opposite).
  1.4046 +      // To avoid it, we use (float(scrollArea.height) * 0.8) as
  1.4047 +      // lower bound value of height of page scrolling. (bug 383267)
  1.4048 +      // XXX shouldn't we use GetPageScrollAmount here?
  1.4049 +      nscoord pageincrement = nscoord(mScrollPort.height - increment);
  1.4050 +      nscoord pageincrementMin = nscoord(float(mScrollPort.height) * 0.8);
  1.4051 +      FinishReflowForScrollbar(vScroll, minY, maxY, scrollPos.y,
  1.4052 +                               std::max(pageincrement, pageincrementMin),
  1.4053 +                               increment);
  1.4054 +    }
  1.4055 +    if (hScroll) {
  1.4056 +      const double kScrollMultiplier =
  1.4057 +        Preferences::GetInt("toolkit.scrollbox.horizontalScrollDistance",
  1.4058 +                            NS_DEFAULT_HORIZONTAL_SCROLL_DISTANCE);
  1.4059 +      nscoord increment = lineScrollAmount.width * kScrollMultiplier;
  1.4060 +      FinishReflowForScrollbar(hScroll, minX, maxX, scrollPos.x,
  1.4061 +                               nscoord(float(mScrollPort.width) * 0.8),
  1.4062 +                               increment);
  1.4063 +    }
  1.4064 +    NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
  1.4065 +  }
  1.4066 +
  1.4067 +  mFrameIsUpdatingScrollbar = false;
  1.4068 +  // We used to rely on the curpos attribute changes above to scroll the
  1.4069 +  // view.  However, for scrolling to the left of the viewport, we
  1.4070 +  // rescale the curpos attribute, which means that operations like
  1.4071 +  // resizing the window while it is scrolled all the way to the left
  1.4072 +  // hold the curpos attribute constant at 0 while still requiring
  1.4073 +  // scrolling.  So we suppress the effect of the changes above with
  1.4074 +  // mFrameIsUpdatingScrollbar and call CurPosAttributeChanged here.
  1.4075 +  // (It actually even works some of the time without this, thanks to
  1.4076 +  // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
  1.4077 +  // we hide the scrollbar on a large size change, such as
  1.4078 +  // maximization.)
  1.4079 +  if (!mHScrollbarBox && !mVScrollbarBox)
  1.4080 +    return false;
  1.4081 +  CurPosAttributeChanged(mVScrollbarBox ? mVScrollbarBox->GetContent()
  1.4082 +                                        : mHScrollbarBox->GetContent());
  1.4083 +  return true;
  1.4084 +}
  1.4085 +
  1.4086 +void
  1.4087 +ScrollFrameHelper::ReflowCallbackCanceled()
  1.4088 +{
  1.4089 +  mPostedReflowCallback = false;
  1.4090 +}
  1.4091 +
  1.4092 +bool
  1.4093 +ScrollFrameHelper::UpdateOverflow()
  1.4094 +{
  1.4095 +  nsIScrollableFrame* sf = do_QueryFrame(mOuter);
  1.4096 +  ScrollbarStyles ss = sf->GetScrollbarStyles();
  1.4097 +
  1.4098 +  if (ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN ||
  1.4099 +      ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN ||
  1.4100 +      GetScrollPosition() != nsPoint()) {
  1.4101 +    // If there are scrollbars, or we're not at the beginning of the pane,
  1.4102 +    // the scroll position may change. In this case, mark the frame as
  1.4103 +    // needing reflow. Don't use NS_FRAME_IS_DIRTY as dirty as that means
  1.4104 +    // we have to reflow the frame and all its descendants, and we don't
  1.4105 +    // have to do that here. Only this frame needs to be reflowed.
  1.4106 +    mOuter->PresContext()->PresShell()->FrameNeedsReflow(
  1.4107 +      mOuter, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
  1.4108 +    // Ensure that next time nsHTMLScrollFrame::Reflow runs, we don't skip
  1.4109 +    // updating the scrollbars. (Because the overflow area of the scrolled
  1.4110 +    // frame has probably just been updated, Reflow won't see it change.)
  1.4111 +    mSkippedScrollbarLayout = true;
  1.4112 +    return false;  // reflowing will update overflow
  1.4113 +  }
  1.4114 +  PostOverflowEvent();
  1.4115 +  return mOuter->nsContainerFrame::UpdateOverflow();
  1.4116 +}
  1.4117 +
  1.4118 +void
  1.4119 +ScrollFrameHelper::UpdateSticky()
  1.4120 +{
  1.4121 +  StickyScrollContainer* ssc = StickyScrollContainer::
  1.4122 +    GetStickyScrollContainerForScrollFrame(mOuter);
  1.4123 +  if (ssc) {
  1.4124 +    nsIScrollableFrame* scrollFrame = do_QueryFrame(mOuter);
  1.4125 +    ssc->UpdatePositions(scrollFrame->GetScrollPosition(), mOuter);
  1.4126 +  }
  1.4127 +}
  1.4128 +
  1.4129 +void
  1.4130 +ScrollFrameHelper::AdjustScrollbarRectForResizer(
  1.4131 +                         nsIFrame* aFrame, nsPresContext* aPresContext,
  1.4132 +                         nsRect& aRect, bool aHasResizer, bool aVertical)
  1.4133 +{
  1.4134 +  if ((aVertical ? aRect.width : aRect.height) == 0)
  1.4135 +    return;
  1.4136 +
  1.4137 +  // if a content resizer is present, use its size. Otherwise, check if the
  1.4138 +  // widget has a resizer.
  1.4139 +  nsRect resizerRect;
  1.4140 +  if (aHasResizer) {
  1.4141 +    resizerRect = mResizerBox->GetRect();
  1.4142 +  }
  1.4143 +  else {
  1.4144 +    nsPoint offset;
  1.4145 +    nsIWidget* widget = aFrame->GetNearestWidget(offset);
  1.4146 +    nsIntRect widgetRect;
  1.4147 +    if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
  1.4148 +      return;
  1.4149 +
  1.4150 +    resizerRect = nsRect(aPresContext->DevPixelsToAppUnits(widgetRect.x) - offset.x,
  1.4151 +                         aPresContext->DevPixelsToAppUnits(widgetRect.y) - offset.y,
  1.4152 +                         aPresContext->DevPixelsToAppUnits(widgetRect.width),
  1.4153 +                         aPresContext->DevPixelsToAppUnits(widgetRect.height));
  1.4154 +  }
  1.4155 +
  1.4156 +  if (!resizerRect.Contains(aRect.BottomRight() - nsPoint(1, 1)))
  1.4157 +    return;
  1.4158 +
  1.4159 +  if (aVertical)
  1.4160 +    aRect.height = std::max(0, resizerRect.y - aRect.y);
  1.4161 +  else
  1.4162 +    aRect.width = std::max(0, resizerRect.x - aRect.x);
  1.4163 +}
  1.4164 +
  1.4165 +static void
  1.4166 +AdjustOverlappingScrollbars(nsRect& aVRect, nsRect& aHRect)
  1.4167 +{
  1.4168 +  if (aVRect.IsEmpty() || aHRect.IsEmpty())
  1.4169 +    return;
  1.4170 +
  1.4171 +  const nsRect oldVRect = aVRect;
  1.4172 +  const nsRect oldHRect = aHRect;
  1.4173 +  if (oldVRect.Contains(oldHRect.BottomRight() - nsPoint(1, 1))) {
  1.4174 +    aHRect.width = std::max(0, oldVRect.x - oldHRect.x);
  1.4175 +  } else if (oldVRect.Contains(oldHRect.BottomLeft() - nsPoint(0, 1))) {
  1.4176 +    nscoord overlap = std::min(oldHRect.width, oldVRect.XMost() - oldHRect.x);
  1.4177 +    aHRect.x += overlap;
  1.4178 +    aHRect.width -= overlap;
  1.4179 +  }
  1.4180 +  if (oldHRect.Contains(oldVRect.BottomRight() - nsPoint(1, 1))) {
  1.4181 +    aVRect.height = std::max(0, oldHRect.y - oldVRect.y);
  1.4182 +  }
  1.4183 +}
  1.4184 +
  1.4185 +void
  1.4186 +ScrollFrameHelper::LayoutScrollbars(nsBoxLayoutState& aState,
  1.4187 +                                        const nsRect& aContentArea,
  1.4188 +                                        const nsRect& aOldScrollArea)
  1.4189 +{
  1.4190 +  NS_ASSERTION(!mSupppressScrollbarUpdate,
  1.4191 +               "This should have been suppressed");
  1.4192 +
  1.4193 +  bool hasResizer = HasResizer();
  1.4194 +  bool scrollbarOnLeft = !IsScrollbarOnRight();
  1.4195 +
  1.4196 +  // place the scrollcorner
  1.4197 +  if (mScrollCornerBox || mResizerBox) {
  1.4198 +    NS_PRECONDITION(!mScrollCornerBox || mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
  1.4199 +
  1.4200 +    nsRect r(0, 0, 0, 0);
  1.4201 +    if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) {
  1.4202 +      // scrollbar (if any) on left
  1.4203 +      r.x = aContentArea.x;
  1.4204 +      r.width = mScrollPort.x - aContentArea.x;
  1.4205 +      NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
  1.4206 +    } else {
  1.4207 +      // scrollbar (if any) on right
  1.4208 +      r.width = aContentArea.XMost() - mScrollPort.XMost();
  1.4209 +      r.x = aContentArea.XMost() - r.width;
  1.4210 +      NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
  1.4211 +    }
  1.4212 +    if (aContentArea.y != mScrollPort.y) {
  1.4213 +      NS_ERROR("top scrollbars not supported");
  1.4214 +    } else {
  1.4215 +      // scrollbar (if any) on bottom
  1.4216 +      r.height = aContentArea.YMost() - mScrollPort.YMost();
  1.4217 +      r.y = aContentArea.YMost() - r.height;
  1.4218 +      NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
  1.4219 +    }
  1.4220 +
  1.4221 +    if (mScrollCornerBox) {
  1.4222 +      nsBoxFrame::LayoutChildAt(aState, mScrollCornerBox, r);
  1.4223 +    }
  1.4224 +
  1.4225 +    if (hasResizer) {
  1.4226 +      // if a resizer is present, get its size. Assume a default size of 15 pixels.
  1.4227 +      nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
  1.4228 +      nsSize resizerMinSize = mResizerBox->GetMinSize(aState);
  1.4229 +
  1.4230 +      nscoord vScrollbarWidth = mVScrollbarBox ?
  1.4231 +        mVScrollbarBox->GetPrefSize(aState).width : defaultSize;
  1.4232 +      r.width = std::max(std::max(r.width, vScrollbarWidth), resizerMinSize.width);
  1.4233 +      if (aContentArea.x == mScrollPort.x && !scrollbarOnLeft) {
  1.4234 +        r.x = aContentArea.XMost() - r.width;
  1.4235 +      }
  1.4236 +
  1.4237 +      nscoord hScrollbarHeight = mHScrollbarBox ?
  1.4238 +        mHScrollbarBox->GetPrefSize(aState).height : defaultSize;
  1.4239 +      r.height = std::max(std::max(r.height, hScrollbarHeight), resizerMinSize.height);
  1.4240 +      if (aContentArea.y == mScrollPort.y) {
  1.4241 +        r.y = aContentArea.YMost() - r.height;
  1.4242 +      }
  1.4243 +
  1.4244 +      nsBoxFrame::LayoutChildAt(aState, mResizerBox, r);
  1.4245 +    }
  1.4246 +    else if (mResizerBox) {
  1.4247 +      // otherwise lay out the resizer with an empty rectangle
  1.4248 +      nsBoxFrame::LayoutChildAt(aState, mResizerBox, nsRect());
  1.4249 +    }
  1.4250 +  }
  1.4251 +
  1.4252 +  nsPresContext* presContext = mScrolledFrame->PresContext();
  1.4253 +  nsRect vRect;
  1.4254 +  if (mVScrollbarBox) {
  1.4255 +    NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!");
  1.4256 +    vRect = mScrollPort;
  1.4257 +    vRect.width = aContentArea.width - mScrollPort.width;
  1.4258 +    vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.XMost();
  1.4259 +    if (mHasVerticalScrollbar) {
  1.4260 +      nsMargin margin;
  1.4261 +      mVScrollbarBox->GetMargin(margin);
  1.4262 +      vRect.Deflate(margin);
  1.4263 +    }
  1.4264 +    AdjustScrollbarRectForResizer(mOuter, presContext, vRect, hasResizer, true);
  1.4265 +  }
  1.4266 +
  1.4267 +  nsRect hRect;
  1.4268 +  if (mHScrollbarBox) {
  1.4269 +    NS_PRECONDITION(mHScrollbarBox->IsBoxFrame(), "Must be a box frame!");
  1.4270 +    hRect = mScrollPort;
  1.4271 +    hRect.height = aContentArea.height - mScrollPort.height;
  1.4272 +    hRect.y = true ? mScrollPort.YMost() : aContentArea.y;
  1.4273 +    if (mHasHorizontalScrollbar) {
  1.4274 +      nsMargin margin;
  1.4275 +      mHScrollbarBox->GetMargin(margin);
  1.4276 +      hRect.Deflate(margin);
  1.4277 +    }
  1.4278 +    AdjustScrollbarRectForResizer(mOuter, presContext, hRect, hasResizer, false);
  1.4279 +  }
  1.4280 +
  1.4281 +  if (!LookAndFeel::GetInt(LookAndFeel::eIntID_AllowOverlayScrollbarsOverlap)) {
  1.4282 +    AdjustOverlappingScrollbars(vRect, hRect);
  1.4283 +  }
  1.4284 +  if (mVScrollbarBox) {
  1.4285 +    nsBoxFrame::LayoutChildAt(aState, mVScrollbarBox, vRect);
  1.4286 +  }
  1.4287 +  if (mHScrollbarBox) {
  1.4288 +    nsBoxFrame::LayoutChildAt(aState, mHScrollbarBox, hRect);
  1.4289 +  }
  1.4290 +
  1.4291 +  // may need to update fixed position children of the viewport,
  1.4292 +  // if the client area changed size because of an incremental
  1.4293 +  // reflow of a descendant.  (If the outer frame is dirty, the fixed
  1.4294 +  // children will be re-laid out anyway)
  1.4295 +  if (aOldScrollArea.Size() != mScrollPort.Size() &&
  1.4296 +      !(mOuter->GetStateBits() & NS_FRAME_IS_DIRTY) &&
  1.4297 +      mIsRoot) {
  1.4298 +    mMayHaveDirtyFixedChildren = true;
  1.4299 +  }
  1.4300 +
  1.4301 +  // post reflow callback to modify scrollbar attributes
  1.4302 +  mUpdateScrollbarAttributes = true;
  1.4303 +  if (!mPostedReflowCallback) {
  1.4304 +    aState.PresContext()->PresShell()->PostReflowCallback(this);
  1.4305 +    mPostedReflowCallback = true;
  1.4306 +  }
  1.4307 +}
  1.4308 +
  1.4309 +#if DEBUG
  1.4310 +static bool ShellIsAlive(nsWeakPtr& aWeakPtr)
  1.4311 +{
  1.4312 +  nsCOMPtr<nsIPresShell> shell(do_QueryReferent(aWeakPtr));
  1.4313 +  return !!shell;
  1.4314 +}
  1.4315 +#endif
  1.4316 +
  1.4317 +void
  1.4318 +ScrollFrameHelper::SetScrollbarEnabled(nsIContent* aContent, nscoord aMaxPos)
  1.4319 +{
  1.4320 +  DebugOnly<nsWeakPtr> weakShell(
  1.4321 +    do_GetWeakReference(mOuter->PresContext()->PresShell()));
  1.4322 +  if (aMaxPos) {
  1.4323 +    aContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
  1.4324 +  } else {
  1.4325 +    aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
  1.4326 +                      NS_LITERAL_STRING("true"), true);
  1.4327 +  }
  1.4328 +  MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
  1.4329 +}
  1.4330 +
  1.4331 +void
  1.4332 +ScrollFrameHelper::SetCoordAttribute(nsIContent* aContent, nsIAtom* aAtom,
  1.4333 +                                         nscoord aSize)
  1.4334 +{
  1.4335 +  DebugOnly<nsWeakPtr> weakShell(
  1.4336 +    do_GetWeakReference(mOuter->PresContext()->PresShell()));
  1.4337 +  // convert to pixels
  1.4338 +  aSize = nsPresContext::AppUnitsToIntCSSPixels(aSize);
  1.4339 +
  1.4340 +  // only set the attribute if it changed.
  1.4341 +
  1.4342 +  nsAutoString newValue;
  1.4343 +  newValue.AppendInt(aSize);
  1.4344 +
  1.4345 +  if (aContent->AttrValueIs(kNameSpaceID_None, aAtom, newValue, eCaseMatters))
  1.4346 +    return;
  1.4347 +
  1.4348 +  nsWeakFrame weakFrame(mOuter);
  1.4349 +  nsCOMPtr<nsIContent> kungFuDeathGrip = aContent;
  1.4350 +  aContent->SetAttr(kNameSpaceID_None, aAtom, newValue, true);
  1.4351 +  MOZ_ASSERT(ShellIsAlive(weakShell), "pres shell was destroyed by scrolling");
  1.4352 +  if (!weakFrame.IsAlive()) {
  1.4353 +    return;
  1.4354 +  }
  1.4355 +
  1.4356 +  if (mScrollbarActivity) {
  1.4357 +    nsRefPtr<ScrollbarActivity> scrollbarActivity(mScrollbarActivity);
  1.4358 +    scrollbarActivity->ActivityOccurred();
  1.4359 +  }
  1.4360 +}
  1.4361 +
  1.4362 +static void
  1.4363 +ReduceRadii(nscoord aXBorder, nscoord aYBorder,
  1.4364 +            nscoord& aXRadius, nscoord& aYRadius)
  1.4365 +{
  1.4366 +  // In order to ensure that the inside edge of the border has no
  1.4367 +  // curvature, we need at least one of its radii to be zero.
  1.4368 +  if (aXRadius <= aXBorder || aYRadius <= aYBorder)
  1.4369 +    return;
  1.4370 +
  1.4371 +  // For any corner where we reduce the radii, preserve the corner's shape.
  1.4372 +  double ratio = std::max(double(aXBorder) / aXRadius,
  1.4373 +                        double(aYBorder) / aYRadius);
  1.4374 +  aXRadius *= ratio;
  1.4375 +  aYRadius *= ratio;
  1.4376 +}
  1.4377 +
  1.4378 +/**
  1.4379 + * Implement an override for nsIFrame::GetBorderRadii to ensure that
  1.4380 + * the clipping region for the border radius does not clip the scrollbars.
  1.4381 + *
  1.4382 + * In other words, we require that the border radius be reduced until the
  1.4383 + * inner border radius at the inner edge of the border is 0 wherever we
  1.4384 + * have scrollbars.
  1.4385 + */
  1.4386 +bool
  1.4387 +ScrollFrameHelper::GetBorderRadii(nscoord aRadii[8]) const
  1.4388 +{
  1.4389 +  if (!mOuter->nsContainerFrame::GetBorderRadii(aRadii))
  1.4390 +    return false;
  1.4391 +
  1.4392 +  // Since we can use GetActualScrollbarSizes (rather than
  1.4393 +  // GetDesiredScrollbarSizes) since this doesn't affect reflow, we
  1.4394 +  // probably should.
  1.4395 +  nsMargin sb = GetActualScrollbarSizes();
  1.4396 +  nsMargin border = mOuter->GetUsedBorder();
  1.4397 +
  1.4398 +  if (sb.left > 0 || sb.top > 0) {
  1.4399 +    ReduceRadii(border.left, border.top,
  1.4400 +                aRadii[NS_CORNER_TOP_LEFT_X],
  1.4401 +                aRadii[NS_CORNER_TOP_LEFT_Y]);
  1.4402 +  }
  1.4403 +
  1.4404 +  if (sb.top > 0 || sb.right > 0) {
  1.4405 +    ReduceRadii(border.right, border.top,
  1.4406 +                aRadii[NS_CORNER_TOP_RIGHT_X],
  1.4407 +                aRadii[NS_CORNER_TOP_RIGHT_Y]);
  1.4408 +  }
  1.4409 +
  1.4410 +  if (sb.right > 0 || sb.bottom > 0) {
  1.4411 +    ReduceRadii(border.right, border.bottom,
  1.4412 +                aRadii[NS_CORNER_BOTTOM_RIGHT_X],
  1.4413 +                aRadii[NS_CORNER_BOTTOM_RIGHT_Y]);
  1.4414 +  }
  1.4415 +
  1.4416 +  if (sb.bottom > 0 || sb.left > 0) {
  1.4417 +    ReduceRadii(border.left, border.bottom,
  1.4418 +                aRadii[NS_CORNER_BOTTOM_LEFT_X],
  1.4419 +                aRadii[NS_CORNER_BOTTOM_LEFT_Y]);
  1.4420 +  }
  1.4421 +
  1.4422 +  return true;
  1.4423 +}
  1.4424 +
  1.4425 +nsRect
  1.4426 +ScrollFrameHelper::GetScrolledRect() const
  1.4427 +{
  1.4428 +  nsRect result =
  1.4429 +    GetScrolledRectInternal(mScrolledFrame->GetScrollableOverflowRect(),
  1.4430 +                            mScrollPort.Size());
  1.4431 +
  1.4432 +  if (result.width < mScrollPort.width) {
  1.4433 +    NS_WARNING("Scrolled rect smaller than scrollport?");
  1.4434 +  }
  1.4435 +  if (result.height < mScrollPort.height) {
  1.4436 +    NS_WARNING("Scrolled rect smaller than scrollport?");
  1.4437 +  }
  1.4438 +  return result;
  1.4439 +}
  1.4440 +
  1.4441 +nsRect
  1.4442 +ScrollFrameHelper::GetScrolledRectInternal(const nsRect& aScrolledFrameOverflowArea,
  1.4443 +                                               const nsSize& aScrollPortSize) const
  1.4444 +{
  1.4445 +  return nsLayoutUtils::GetScrolledRect(mScrolledFrame,
  1.4446 +      aScrolledFrameOverflowArea, aScrollPortSize,
  1.4447 +      IsLTR() ? NS_STYLE_DIRECTION_LTR : NS_STYLE_DIRECTION_RTL);
  1.4448 +}
  1.4449 +
  1.4450 +nsMargin
  1.4451 +ScrollFrameHelper::GetActualScrollbarSizes() const
  1.4452 +{
  1.4453 +  nsRect r = mOuter->GetPaddingRect() - mOuter->GetPosition();
  1.4454 +
  1.4455 +  return nsMargin(mScrollPort.y - r.y,
  1.4456 +                  r.XMost() - mScrollPort.XMost(),
  1.4457 +                  r.YMost() - mScrollPort.YMost(),
  1.4458 +                  mScrollPort.x - r.x);
  1.4459 +}
  1.4460 +
  1.4461 +void
  1.4462 +ScrollFrameHelper::SetScrollbarVisibility(nsIFrame* aScrollbar, bool aVisible)
  1.4463 +{
  1.4464 +  nsScrollbarFrame* scrollbar = do_QueryFrame(aScrollbar);
  1.4465 +  if (scrollbar) {
  1.4466 +    // See if we have a mediator.
  1.4467 +    nsIScrollbarMediator* mediator = scrollbar->GetScrollbarMediator();
  1.4468 +    if (mediator) {
  1.4469 +      // Inform the mediator of the visibility change.
  1.4470 +      mediator->VisibilityChanged(aVisible);
  1.4471 +    }
  1.4472 +  }
  1.4473 +}
  1.4474 +
  1.4475 +nscoord
  1.4476 +ScrollFrameHelper::GetCoordAttribute(nsIFrame* aBox, nsIAtom* aAtom,
  1.4477 +                                         nscoord aDefaultValue,
  1.4478 +                                         nscoord* aRangeStart,
  1.4479 +                                         nscoord* aRangeLength)
  1.4480 +{
  1.4481 +  if (aBox) {
  1.4482 +    nsIContent* content = aBox->GetContent();
  1.4483 +
  1.4484 +    nsAutoString value;
  1.4485 +    content->GetAttr(kNameSpaceID_None, aAtom, value);
  1.4486 +    if (!value.IsEmpty())
  1.4487 +    {
  1.4488 +      nsresult error;
  1.4489 +      // convert it to appunits
  1.4490 +      nscoord result = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
  1.4491 +      nscoord halfPixel = nsPresContext::CSSPixelsToAppUnits(0.5f);
  1.4492 +      // Any nscoord value that would round to the attribute value when converted
  1.4493 +      // to CSS pixels is allowed.
  1.4494 +      *aRangeStart = result - halfPixel;
  1.4495 +      *aRangeLength = halfPixel*2 - 1;
  1.4496 +      return result;
  1.4497 +    }
  1.4498 +  }
  1.4499 +
  1.4500 +  // Only this exact default value is allowed.
  1.4501 +  *aRangeStart = aDefaultValue;
  1.4502 +  *aRangeLength = 0;
  1.4503 +  return aDefaultValue;
  1.4504 +}
  1.4505 +
  1.4506 +nsPresState*
  1.4507 +ScrollFrameHelper::SaveState() const
  1.4508 +{
  1.4509 +  nsIScrollbarMediator* mediator = do_QueryFrame(GetScrolledFrame());
  1.4510 +  if (mediator) {
  1.4511 +    // child handles its own scroll state, so don't bother saving state here
  1.4512 +    return nullptr;
  1.4513 +  }
  1.4514 +
  1.4515 +  // Don't store a scroll state if we never have been scrolled or restored
  1.4516 +  // a previous scroll state.
  1.4517 +  if (!mHasBeenScrolled && !mDidHistoryRestore) {
  1.4518 +    return nullptr;
  1.4519 +  }
  1.4520 +
  1.4521 +  nsPresState* state = new nsPresState();
  1.4522 +  // Save mRestorePos instead of our actual current scroll position, if it's
  1.4523 +  // valid and we haven't moved since the last update of mLastPos (same check
  1.4524 +  // that ScrollToRestoredPosition uses). This ensures if a reframe occurs
  1.4525 +  // while we're in the process of loading content to scroll to a restored
  1.4526 +  // position, we'll keep trying after the reframe.
  1.4527 +  nsPoint pt = GetLogicalScrollPosition();
  1.4528 +  if (mRestorePos.y != -1 && pt == mLastPos) {
  1.4529 +    pt = mRestorePos;
  1.4530 +  }
  1.4531 +  state->SetScrollState(pt);
  1.4532 +  state->SetResolution(mResolution);
  1.4533 +  return state;
  1.4534 +}
  1.4535 +
  1.4536 +void
  1.4537 +ScrollFrameHelper::RestoreState(nsPresState* aState)
  1.4538 +{
  1.4539 +  mRestorePos = aState->GetScrollState();
  1.4540 +  mDidHistoryRestore = true;
  1.4541 +  mLastPos = mScrolledFrame ? GetLogicalScrollPosition() : nsPoint(0,0);
  1.4542 +  mResolution = aState->GetResolution();
  1.4543 +  mIsResolutionSet = true;
  1.4544 +
  1.4545 +  if (mIsRoot) {
  1.4546 +    mOuter->PresContext()->PresShell()->SetResolution(mResolution.width, mResolution.height);
  1.4547 +  }
  1.4548 +}
  1.4549 +
  1.4550 +void
  1.4551 +ScrollFrameHelper::PostScrolledAreaEvent()
  1.4552 +{
  1.4553 +  if (mScrolledAreaEvent.IsPending()) {
  1.4554 +    return;
  1.4555 +  }
  1.4556 +  mScrolledAreaEvent = new ScrolledAreaEvent(this);
  1.4557 +  nsContentUtils::AddScriptRunner(mScrolledAreaEvent.get());
  1.4558 +}
  1.4559 +
  1.4560 +////////////////////////////////////////////////////////////////////////////////
  1.4561 +// ScrolledArea change event dispatch
  1.4562 +
  1.4563 +NS_IMETHODIMP
  1.4564 +ScrollFrameHelper::ScrolledAreaEvent::Run()
  1.4565 +{
  1.4566 +  if (mHelper) {
  1.4567 +    mHelper->FireScrolledAreaEvent();
  1.4568 +  }
  1.4569 +  return NS_OK;
  1.4570 +}
  1.4571 +
  1.4572 +void
  1.4573 +ScrollFrameHelper::FireScrolledAreaEvent()
  1.4574 +{
  1.4575 +  mScrolledAreaEvent.Forget();
  1.4576 +
  1.4577 +  InternalScrollAreaEvent event(true, NS_SCROLLEDAREACHANGED, nullptr);
  1.4578 +  nsPresContext *prescontext = mOuter->PresContext();
  1.4579 +  nsIContent* content = mOuter->GetContent();
  1.4580 +
  1.4581 +  event.mArea = mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
  1.4582 +
  1.4583 +  nsIDocument *doc = content->GetCurrentDoc();
  1.4584 +  if (doc) {
  1.4585 +    EventDispatcher::Dispatch(doc, prescontext, &event, nullptr);
  1.4586 +  }
  1.4587 +}
  1.4588 +
  1.4589 +uint32_t
  1.4590 +nsIScrollableFrame::GetPerceivedScrollingDirections() const
  1.4591 +{
  1.4592 +  nscoord oneDevPixel = GetScrolledFrame()->PresContext()->AppUnitsPerDevPixel();
  1.4593 +  uint32_t directions = GetScrollbarVisibility();
  1.4594 +  nsRect scrollRange = GetScrollRange();
  1.4595 +  if (scrollRange.width >= oneDevPixel) {
  1.4596 +    directions |= HORIZONTAL;
  1.4597 +  }
  1.4598 +  if (scrollRange.height >= oneDevPixel) {
  1.4599 +    directions |= VERTICAL;
  1.4600 +  }
  1.4601 +  return directions;
  1.4602 +}

mercurial