1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Pierre Phaneuf <pp@ludusdesign.com>
24 * Mats Palmgren <matspal@gmail.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* rendering object to wrap rendering objects that should be scrollable */
43 #include "nsHTMLParts.h"
44 #include "nsPresContext.h"
45 #include "nsIServiceManager.h"
47 #include "nsIScrollable.h"
48 #include "nsIViewManager.h"
49 #include "nsHTMLContainerFrame.h"
50 #include "nsGfxScrollFrame.h"
51 #include "nsGkAtoms.h"
52 #include "nsINameSpaceManager.h"
53 #include "nsIDocument.h"
54 #include "nsIFontMetrics.h"
55 #include "nsIDocumentObserver.h"
56 #include "nsIDocument.h"
57 #include "nsBoxLayoutState.h"
58 #include "nsINodeInfo.h"
59 #include "nsIScrollbarFrame.h"
60 #include "nsIScrollbarMediator.h"
61 #include "nsITextControlFrame.h"
62 #include "nsIDOMHTMLTextAreaElement.h"
63 #include "nsNodeInfoManager.h"
65 #include "nsGUIEvent.h"
66 #include "nsContentCreatorFunctions.h"
67 #include "nsISupportsPrimitives.h"
68 #include "nsAutoPtr.h"
69 #include "nsPresState.h"
70 #include "nsIGlobalHistory3.h"
71 #include "nsDocShellCID.h"
72 #include "nsIHTMLDocument.h"
73 #include "nsEventDispatcher.h"
74 #include "nsContentUtils.h"
75 #include "nsLayoutUtils.h"
77 #include "nsIAccessibilityService.h"
79 #include "nsDisplayList.h"
80 #include "nsBidiUtils.h"
81 #include "nsFrameManager.h"
82 #include "nsIPrefService.h"
83 #include "mozilla/dom/Element.h"
85 using namespace mozilla::dom
;
87 //----------------------------------------------------------------------
89 //----------nsHTMLScrollFrame-------------------------------------------
92 NS_NewHTMLScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
94 return new (aPresShell
) nsHTMLScrollFrame(aPresShell
, aContext
, aIsRoot
);
97 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame
)
99 nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
100 : nsHTMLContainerFrame(aContext
),
101 mInner(this, aIsRoot
, PR_FALSE
)
106 nsHTMLScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
108 return mInner
.CreateAnonymousContent(aElements
);
112 nsHTMLScrollFrame::AppendAnonymousContentTo(nsBaseContentList
& aElements
)
114 mInner
.AppendAnonymousContentTo(aElements
);
118 nsHTMLScrollFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
121 nsHTMLContainerFrame::DestroyFrom(aDestructRoot
);
125 nsHTMLScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
126 nsFrameList
& aChildList
)
128 nsresult rv
= nsHTMLContainerFrame::SetInitialChildList(aListName
, aChildList
);
129 mInner
.ReloadChildFrames();
135 nsHTMLScrollFrame::AppendFrames(nsIAtom
* aListName
,
136 nsFrameList
& aFrameList
)
138 NS_ASSERTION(!aListName
, "Only main list supported");
139 mFrames
.AppendFrames(nsnull
, aFrameList
);
140 mInner
.ReloadChildFrames();
145 nsHTMLScrollFrame::InsertFrames(nsIAtom
* aListName
,
146 nsIFrame
* aPrevFrame
,
147 nsFrameList
& aFrameList
)
149 NS_ASSERTION(!aListName
, "Only main list supported");
150 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
151 "inserting after sibling frame with different parent");
152 mFrames
.InsertFrames(nsnull
, aPrevFrame
, aFrameList
);
153 mInner
.ReloadChildFrames();
158 nsHTMLScrollFrame::RemoveFrame(nsIAtom
* aListName
,
161 NS_ASSERTION(!aListName
, "Only main list supported");
162 mFrames
.DestroyFrame(aOldFrame
);
163 mInner
.ReloadChildFrames();
168 nsHTMLScrollFrame::GetSplittableType() const
170 return NS_FRAME_NOT_SPLITTABLE
;
174 nsHTMLScrollFrame::GetSkipSides() const
180 nsHTMLScrollFrame::GetType() const
182 return nsGkAtoms::scrollFrame
;
186 nsHTMLScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
187 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
191 if (aForChild
== mInner
.mScrolledFrame
) {
192 // restrict aDamageRect to the scrollable view's bounds
193 nsRect damage
= aDamageRect
+ nsPoint(aX
, aY
);
194 // damage is now in our coordinate system, which means it was
195 // translated using the current scroll position. Adjust it to
196 // reflect the scroll position at last paint, since that's what
197 // the layer system wants us to invalidate.
198 damage
+= GetScrollPosition() - mInner
.mScrollPosAtLastPaint
;
200 if (r
.IntersectRect(damage
, mInner
.mScrollPort
)) {
201 nsHTMLContainerFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
203 if (mInner
.mIsRoot
&& r
!= damage
) {
204 // Make sure we notify our prescontext about invalidations outside
205 // viewport clipping.
206 // This is important for things that are snapshotting the viewport,
207 // possibly outside the scrolled bounds.
208 // We don't need to propagate this any further up, though. Anyone who
209 // cares about scrolled-out-of-view invalidates had better be listening
210 // to our window directly.
211 PresContext()->NotifyInvalidation(damage
, aFlags
);
214 } else if (aForChild
== mInner
.mHScrollbarBox
) {
215 if (!mInner
.mHasHorizontalScrollbar
) {
216 // Our scrollbars may send up invalidations even when they're collapsed,
217 // because we just size a collapsed scrollbar to empty and some
218 // descendants may be non-empty. Suppress that invalidation here.
221 } else if (aForChild
== mInner
.mVScrollbarBox
) {
222 if (!mInner
.mHasVerticalScrollbar
) {
223 // Our scrollbars may send up invalidations even when they're collapsed,
224 // because we just size a collapsed scrollbar to empty and some
225 // descendants may be non-empty. Suppress that invalidation here.
231 nsHTMLContainerFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
235 HTML scrolling implementation
237 All other things being equal, we prefer layouts with fewer scrollbars showing.
240 struct ScrollReflowState
{
241 const nsHTMLReflowState
& mReflowState
;
242 nsBoxLayoutState mBoxState
;
243 nsGfxScrollFrameInner::ScrollbarStyles mStyles
;
244 nsMargin mComputedBorder
;
246 // === Filled in by ReflowScrolledFrame ===
247 nsRect mContentsOverflowArea
;
248 PRPackedBool mReflowedContentsWithHScrollbar
;
249 PRPackedBool mReflowedContentsWithVScrollbar
;
251 // === Filled in when TryLayout succeeds ===
252 // The size of the inside-border area
253 nsSize mInsideBorderSize
;
254 // Whether we decided to show the horizontal scrollbar
255 PRPackedBool mShowHScrollbar
;
256 // Whether we decided to show the vertical scrollbar
257 PRPackedBool mShowVScrollbar
;
259 ScrollReflowState(nsIScrollableFrame
* aFrame
,
260 const nsHTMLReflowState
& aState
) :
261 mReflowState(aState
),
262 // mBoxState is just used for scrollbars so we don't need to
263 // worry about the reflow depth here
264 mBoxState(aState
.frame
->PresContext(), aState
.rendContext
, 0),
265 mStyles(aFrame
->GetScrollbarStyles()) {
269 // XXXldb Can this go away?
270 static nsSize
ComputeInsideBorderSize(ScrollReflowState
* aState
,
271 const nsSize
& aDesiredInsideBorderSize
)
273 // aDesiredInsideBorderSize is the frame size; i.e., it includes
274 // borders and padding (but the scrolled child doesn't have
275 // borders). The scrolled child has the same padding as us.
276 nscoord contentWidth
= aState
->mReflowState
.ComputedWidth();
277 if (contentWidth
== NS_UNCONSTRAINEDSIZE
) {
278 contentWidth
= aDesiredInsideBorderSize
.width
-
279 aState
->mReflowState
.mComputedPadding
.LeftRight();
281 nscoord contentHeight
= aState
->mReflowState
.ComputedHeight();
282 if (contentHeight
== NS_UNCONSTRAINEDSIZE
) {
283 contentHeight
= aDesiredInsideBorderSize
.height
-
284 aState
->mReflowState
.mComputedPadding
.TopBottom();
287 aState
->mReflowState
.ApplyMinMaxConstraints(&contentWidth
, &contentHeight
);
288 return nsSize(contentWidth
+ aState
->mReflowState
.mComputedPadding
.LeftRight(),
289 contentHeight
+ aState
->mReflowState
.mComputedPadding
.TopBottom());
293 GetScrollbarMetrics(nsBoxLayoutState
& aState
, nsIBox
* aBox
, nsSize
* aMin
,
294 nsSize
* aPref
, PRBool aVertical
)
296 NS_ASSERTION(aState
.GetRenderingContext(),
297 "Must have rendering context in layout state for size "
301 *aMin
= aBox
->GetMinSize(aState
);
302 nsBox::AddMargin(aBox
, *aMin
);
306 *aPref
= aBox
->GetPrefSize(aState
);
307 nsBox::AddMargin(aBox
, *aPref
);
312 * Assuming that we know the metrics for our wrapped frame and
313 * whether the horizontal and/or vertical scrollbars are present,
314 * compute the resulting layout and return PR_TRUE if the layout is
315 * consistent. If the layout is consistent then we fill in the
316 * computed fields of the ScrollReflowState.
318 * The layout is consistent when both scrollbars are showing if and only
319 * if they should be showing. A horizontal scrollbar should be showing if all
320 * following conditions are met:
321 * 1) the style is not HIDDEN
322 * 2) our inside-border height is at least the scrollbar height (i.e., the
323 * scrollbar fits vertically)
324 * 3) our scrollport width (the inside-border width minus the width allocated for a
325 * vertical scrollbar, if showing) is at least the scrollbar's min-width
326 * (i.e., the scrollbar fits horizontally)
327 * 4) the style is SCROLL, or the kid's overflow-area XMost is
328 * greater than the scrollport width
330 * @param aForce if PR_TRUE, then we just assume the layout is consistent.
333 nsHTMLScrollFrame::TryLayout(ScrollReflowState
* aState
,
334 nsHTMLReflowMetrics
* aKidMetrics
,
335 PRBool aAssumeHScroll
, PRBool aAssumeVScroll
,
336 PRBool aForce
, nsresult
* aResult
)
340 if ((aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeVScroll
) ||
341 (aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeHScroll
)) {
342 NS_ASSERTION(!aForce
, "Shouldn't be forcing a hidden scrollbar to show!");
346 if (aAssumeVScroll
!= aState
->mReflowedContentsWithVScrollbar
||
347 (aAssumeHScroll
!= aState
->mReflowedContentsWithHScrollbar
&&
348 ScrolledContentDependsOnHeight(aState
))) {
349 nsresult rv
= ReflowScrolledFrame(aState
, aAssumeHScroll
, aAssumeVScroll
,
350 aKidMetrics
, PR_FALSE
);
357 nsSize
vScrollbarMinSize(0, 0);
358 nsSize
vScrollbarPrefSize(0, 0);
359 if (mInner
.mVScrollbarBox
) {
360 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mVScrollbarBox
,
362 aAssumeVScroll
? &vScrollbarPrefSize
: nsnull
, PR_TRUE
);
364 nscoord vScrollbarDesiredWidth
= aAssumeVScroll
? vScrollbarPrefSize
.width
: 0;
365 nscoord vScrollbarMinHeight
= aAssumeVScroll
? vScrollbarMinSize
.height
: 0;
367 nsSize
hScrollbarMinSize(0, 0);
368 nsSize
hScrollbarPrefSize(0, 0);
369 if (mInner
.mHScrollbarBox
) {
370 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mHScrollbarBox
,
372 aAssumeHScroll
? &hScrollbarPrefSize
: nsnull
, PR_FALSE
);
374 nscoord hScrollbarDesiredHeight
= aAssumeHScroll
? hScrollbarPrefSize
.height
: 0;
375 nscoord hScrollbarMinWidth
= aAssumeHScroll
? hScrollbarMinSize
.width
: 0;
377 // First, compute our inside-border size and scrollport size
378 // XXXldb Can we depend more on ComputeSize here?
379 nsSize desiredInsideBorderSize
;
380 desiredInsideBorderSize
.width
= vScrollbarDesiredWidth
+
381 NS_MAX(aKidMetrics
->width
, hScrollbarMinWidth
);
382 desiredInsideBorderSize
.height
= hScrollbarDesiredHeight
+
383 NS_MAX(aKidMetrics
->height
, vScrollbarMinHeight
);
384 aState
->mInsideBorderSize
=
385 ComputeInsideBorderSize(aState
, desiredInsideBorderSize
);
386 nsSize scrollPortSize
= nsSize(NS_MAX(0, aState
->mInsideBorderSize
.width
- vScrollbarDesiredWidth
),
387 NS_MAX(0, aState
->mInsideBorderSize
.height
- hScrollbarDesiredHeight
));
390 nsRect scrolledRect
=
391 mInner
.GetScrolledRectInternal(aState
->mContentsOverflowArea
, scrollPortSize
);
392 nscoord oneDevPixel
= aState
->mBoxState
.PresContext()->DevPixelsToAppUnits(1);
394 // If the style is HIDDEN then we already know that aAssumeHScroll is PR_FALSE
395 if (aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
) {
396 PRBool wantHScrollbar
=
397 aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
||
398 scrolledRect
.XMost() >= scrollPortSize
.width
+ oneDevPixel
||
399 scrolledRect
.x
<= -oneDevPixel
;
400 if (aState
->mInsideBorderSize
.height
< hScrollbarMinSize
.height
||
401 scrollPortSize
.width
< hScrollbarMinSize
.width
)
402 wantHScrollbar
= PR_FALSE
;
403 if (wantHScrollbar
!= aAssumeHScroll
)
407 // If the style is HIDDEN then we already know that aAssumeVScroll is PR_FALSE
408 if (aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
) {
409 PRBool wantVScrollbar
=
410 aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
||
411 scrolledRect
.YMost() >= scrollPortSize
.height
+ oneDevPixel
||
412 scrolledRect
.y
<= -oneDevPixel
;
413 if (aState
->mInsideBorderSize
.width
< vScrollbarMinSize
.width
||
414 scrollPortSize
.height
< vScrollbarMinSize
.height
)
415 wantVScrollbar
= PR_FALSE
;
416 if (wantVScrollbar
!= aAssumeVScroll
)
421 nscoord vScrollbarActualWidth
= aState
->mInsideBorderSize
.width
- scrollPortSize
.width
;
423 aState
->mShowHScrollbar
= aAssumeHScroll
;
424 aState
->mShowVScrollbar
= aAssumeVScroll
;
425 nsPoint
scrollPortOrigin(aState
->mComputedBorder
.left
,
426 aState
->mComputedBorder
.top
);
427 if (!mInner
.IsScrollbarOnRight()) {
428 scrollPortOrigin
.x
+= vScrollbarActualWidth
;
430 mInner
.mScrollPort
= nsRect(scrollPortOrigin
, scrollPortSize
);
435 nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState
* aState
)
437 // Return true if ReflowScrolledFrame is going to do something different
438 // based on the presence of a horizontal scrollbar.
439 return (mInner
.mScrolledFrame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
) ||
440 aState
->mReflowState
.ComputedHeight() != NS_UNCONSTRAINEDSIZE
||
441 aState
->mReflowState
.mComputedMinHeight
> 0 ||
442 aState
->mReflowState
.mComputedMaxHeight
!= NS_UNCONSTRAINEDSIZE
;
446 nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState
* aState
,
447 PRBool aAssumeHScroll
,
448 PRBool aAssumeVScroll
,
449 nsHTMLReflowMetrics
* aMetrics
,
452 // these could be NS_UNCONSTRAINEDSIZE ... NS_MIN arithmetic should
454 nscoord paddingLR
= aState
->mReflowState
.mComputedPadding
.LeftRight();
456 nscoord availWidth
= aState
->mReflowState
.ComputedWidth() + paddingLR
;
458 nscoord computedHeight
= aState
->mReflowState
.ComputedHeight();
459 nscoord computedMinHeight
= aState
->mReflowState
.mComputedMinHeight
;
460 nscoord computedMaxHeight
= aState
->mReflowState
.mComputedMaxHeight
;
461 if (!ShouldPropagateComputedHeightToScrolledContent()) {
462 computedHeight
= NS_UNCONSTRAINEDSIZE
;
463 computedMinHeight
= 0;
464 computedMaxHeight
= NS_UNCONSTRAINEDSIZE
;
466 if (aAssumeHScroll
) {
467 nsSize hScrollbarPrefSize
=
468 mInner
.mHScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
469 if (computedHeight
!= NS_UNCONSTRAINEDSIZE
)
470 computedHeight
= NS_MAX(0, computedHeight
- hScrollbarPrefSize
.height
);
471 computedMinHeight
= NS_MAX(0, computedMinHeight
- hScrollbarPrefSize
.height
);
472 if (computedMaxHeight
!= NS_UNCONSTRAINEDSIZE
)
473 computedMaxHeight
= NS_MAX(0, computedMaxHeight
- hScrollbarPrefSize
.height
);
476 if (aAssumeVScroll
) {
477 nsSize vScrollbarPrefSize
=
478 mInner
.mVScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
479 availWidth
= NS_MAX(0, availWidth
- vScrollbarPrefSize
.width
);
482 nsPresContext
* presContext
= PresContext();
484 // We're forcing the padding on our scrolled frame, so let it know what that
486 presContext
->PropertyTable()->
487 Set(mInner
.mScrolledFrame
, UsedPaddingProperty(),
488 new nsMargin(aState
->mReflowState
.mComputedPadding
));
490 // Pass PR_FALSE for aInit so we can pass in the correct padding
491 nsHTMLReflowState
kidReflowState(presContext
, aState
->mReflowState
,
492 mInner
.mScrolledFrame
,
493 nsSize(availWidth
, NS_UNCONSTRAINEDSIZE
),
495 kidReflowState
.Init(presContext
, -1, -1, nsnull
,
496 &aState
->mReflowState
.mComputedPadding
);
497 kidReflowState
.mFlags
.mAssumingHScrollbar
= aAssumeHScroll
;
498 kidReflowState
.mFlags
.mAssumingVScrollbar
= aAssumeVScroll
;
499 kidReflowState
.SetComputedHeight(computedHeight
);
500 kidReflowState
.mComputedMinHeight
= computedMinHeight
;
501 kidReflowState
.mComputedMaxHeight
= computedMaxHeight
;
503 // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to
504 // reflect our assumptions while we reflow the child.
505 PRBool didHaveHorizontalScrollbar
= mInner
.mHasHorizontalScrollbar
;
506 PRBool didHaveVerticalScrollbar
= mInner
.mHasVerticalScrollbar
;
507 mInner
.mHasHorizontalScrollbar
= aAssumeHScroll
;
508 mInner
.mHasVerticalScrollbar
= aAssumeVScroll
;
510 nsReflowStatus status
;
511 nsresult rv
= ReflowChild(mInner
.mScrolledFrame
, presContext
, *aMetrics
,
512 kidReflowState
, 0, 0,
513 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
, status
);
515 mInner
.mHasHorizontalScrollbar
= didHaveHorizontalScrollbar
;
516 mInner
.mHasVerticalScrollbar
= didHaveVerticalScrollbar
;
518 // Don't resize or position the view (if any) because we're going to resize
519 // it to the correct size anyway in PlaceScrollArea. Allowing it to
520 // resize here would size it to the natural height of the frame,
521 // which will usually be different from the scrollport height;
522 // invalidating the difference will cause unnecessary repainting.
523 FinishReflowChild(mInner
.mScrolledFrame
, presContext
,
524 &kidReflowState
, *aMetrics
, 0, 0,
525 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
| NS_FRAME_NO_SIZE_VIEW
);
527 // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
528 // setting their mOverflowArea. This is wrong because every frame should
529 // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
530 // support the 'outline' property because of this. Rather than fix the world
531 // right now, just fix up the overflow area if necessary. Note that we don't
532 // check HasOverflowRect() because it could be set even though the
533 // overflow area doesn't include the frame bounds.
534 aMetrics
->mOverflowArea
.UnionRect(aMetrics
->mOverflowArea
,
535 nsRect(0, 0, aMetrics
->width
, aMetrics
->height
));
537 aState
->mContentsOverflowArea
= aMetrics
->mOverflowArea
;
538 aState
->mReflowedContentsWithHScrollbar
= aAssumeHScroll
;
539 aState
->mReflowedContentsWithVScrollbar
= aAssumeVScroll
;
545 nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowState
& aState
)
547 if (aState
.mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_AUTO
)
548 // no guessing required
549 return aState
.mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
;
551 return mInner
.mHasHorizontalScrollbar
;
555 nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowState
& aState
)
557 if (aState
.mStyles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
)
558 // no guessing required
559 return aState
.mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
;
561 // If we've had at least one non-initial reflow, then just assume
562 // the state of the vertical scrollbar will be what we determined
564 if (mInner
.mHadNonInitialReflow
) {
565 return mInner
.mHasVerticalScrollbar
;
568 // If this is the initial reflow, guess PR_FALSE because usually
569 // we have very little content by then.
570 if (InInitialReflow())
573 if (mInner
.mIsRoot
) {
574 // Assume that there will be a scrollbar; it seems to me
575 // that 'most pages' do have a scrollbar, and anyway, it's cheaper
576 // to do an extra reflow for the pages that *don't* need a
577 // scrollbar (because on average they will have less content).
581 // For non-viewports, just guess that we don't need a scrollbar.
582 // XXX I wonder if statistically this is the right idea; I'm
583 // basically guessing that there are a lot of overflow:auto DIVs
584 // that get their intrinsic size and don't overflow
589 nsHTMLScrollFrame::InInitialReflow() const
591 // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
592 // root scrollframe. In that case we want to skip this clause altogether.
593 // The guess here is that there are lots of overflow:auto divs out there that
594 // end up auto-sizing so they don't overflow, and that the root basically
595 // always needs a scrollbar if it did last time we loaded this page (good
596 // assumption, because our initial reflow is no longer synchronous).
597 return !mInner
.mIsRoot
&& (GetStateBits() & NS_FRAME_FIRST_REFLOW
);
601 nsHTMLScrollFrame::ReflowContents(ScrollReflowState
* aState
,
602 const nsHTMLReflowMetrics
& aDesiredSize
)
604 nsHTMLReflowMetrics
kidDesiredSize(aDesiredSize
.mFlags
);
605 nsresult rv
= ReflowScrolledFrame(aState
, GuessHScrollbarNeeded(*aState
),
606 GuessVScrollbarNeeded(*aState
), &kidDesiredSize
, PR_TRUE
);
607 NS_ENSURE_SUCCESS(rv
, rv
);
609 // There's an important special case ... if the child appears to fit
610 // in the inside-border rect (but overflows the scrollport), we
611 // should try laying it out without a vertical scrollbar. It will
612 // usually fit because making the available-width wider will not
613 // normally make the child taller. (The only situation I can think
614 // of is when you have a line containing %-width inline replaced
615 // elements whose percentages sum to more than 100%, so increasing
616 // the available width makes the line break where it was fitting
617 // before.) If we don't treat this case specially, then we will
618 // decide that showing scrollbars is OK because the content
619 // overflows when we're showing scrollbars and we won't try to
620 // remove the vertical scrollbar.
622 // Detecting when we enter this special case is important for when
623 // people design layouts that exactly fit the container "most of the
626 // XXX Is this check really sufficient to catch all the incremental cases
627 // where the ideal case doesn't have a scrollbar?
628 if ((aState
->mReflowedContentsWithHScrollbar
|| aState
->mReflowedContentsWithVScrollbar
) &&
629 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
&&
630 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
) {
631 nsSize insideBorderSize
=
632 ComputeInsideBorderSize(aState
,
633 nsSize(kidDesiredSize
.width
, kidDesiredSize
.height
));
634 nsRect scrolledRect
=
635 mInner
.GetScrolledRectInternal(kidDesiredSize
.mOverflowArea
, insideBorderSize
);
636 if (nsRect(nsPoint(0, 0), insideBorderSize
).Contains(scrolledRect
)) {
637 // Let's pretend we had no scrollbars coming in here
638 rv
= ReflowScrolledFrame(aState
, PR_FALSE
, PR_FALSE
,
639 &kidDesiredSize
, PR_FALSE
);
640 NS_ENSURE_SUCCESS(rv
, rv
);
644 // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
645 // Do this first because changing the vertical scrollbar setting is expensive,
646 // forcing a reflow always.
648 // Try leaving the horizontal scrollbar unchanged first. This will be more
650 if (TryLayout(aState
, &kidDesiredSize
, aState
->mReflowedContentsWithHScrollbar
,
651 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
653 if (TryLayout(aState
, &kidDesiredSize
, !aState
->mReflowedContentsWithHScrollbar
,
654 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
657 // OK, now try toggling the vertical scrollbar. The performance advantage
658 // of trying the status-quo horizontal scrollbar state
659 // does not exist here (we'll have to reflow due to the vertical scrollbar
660 // change), so always try no horizontal scrollbar first.
661 PRBool newVScrollbarState
= !aState
->mReflowedContentsWithVScrollbar
;
662 if (TryLayout(aState
, &kidDesiredSize
, PR_FALSE
, newVScrollbarState
, PR_FALSE
, &rv
))
664 if (TryLayout(aState
, &kidDesiredSize
, PR_TRUE
, newVScrollbarState
, PR_FALSE
, &rv
))
667 // OK, we're out of ideas. Try again enabling whatever scrollbars we can
668 // enable and force the layout to stick even if it's inconsistent.
669 // This just happens sometimes.
670 TryLayout(aState
, &kidDesiredSize
,
671 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
,
672 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
,
678 nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState
& aState
,
679 const nsPoint
& aScrollPosition
)
681 nsIFrame
*scrolledFrame
= mInner
.mScrolledFrame
;
682 // Set the x,y of the scrolled frame to the correct value
683 scrolledFrame
->SetPosition(mInner
.mScrollPort
.TopLeft() - aScrollPosition
);
686 // Preserve the width or height of empty rects
687 nsSize portSize
= mInner
.mScrollPort
.Size();
688 nsRect scrolledRect
= mInner
.GetScrolledRectInternal(aState
.mContentsOverflowArea
, portSize
);
689 scrolledArea
.UnionRectIncludeEmpty(scrolledRect
,
690 nsRect(nsPoint(0,0), portSize
));
692 // Store the new overflow area. Note that this changes where an outline
693 // of the scrolled frame would be painted, but scrolled frames can't have
694 // outlines (the outline would go on this scrollframe instead).
695 // Using FinishAndStoreOverflow is needed so the overflow rect
696 // gets set correctly. It also messes with the overflow rect in the
697 // -moz-hidden-unscrollable case, but scrolled frames can't have
698 // 'overflow' either.
699 // This needs to happen before SyncFrameViewAfterReflow so
700 // HasOverflowRect() will return the correct value.
701 scrolledFrame
->FinishAndStoreOverflow(&scrolledArea
,
702 scrolledFrame
->GetSize());
704 // Note that making the view *exactly* the size of the scrolled area
705 // is critical, since the view scrolling code uses the size of the
706 // scrolled view to clamp scroll requests.
707 // Normally the scrolledFrame won't have a view but in some cases it
708 // might create its own.
709 nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame
->PresContext(),
711 scrolledFrame
->GetView(),
717 nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsIRenderingContext
*aRenderingContext
)
719 nsGfxScrollFrameInner::ScrollbarStyles ss
= GetScrollbarStyles();
720 if (ss
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
|| !mInner
.mVScrollbarBox
)
723 // Don't need to worry about reflow depth here since it's
724 // just for scrollbars
725 nsBoxLayoutState
bls(PresContext(), aRenderingContext
, 0);
726 nsSize
vScrollbarPrefSize(0, 0);
727 GetScrollbarMetrics(bls
, mInner
.mVScrollbarBox
,
728 nsnull
, &vScrollbarPrefSize
, PR_TRUE
);
729 return vScrollbarPrefSize
.width
;
732 /* virtual */ nscoord
733 nsHTMLScrollFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
735 nscoord result
= mInner
.mScrolledFrame
->GetMinWidth(aRenderingContext
);
736 DISPLAY_MIN_WIDTH(this, result
);
737 return result
+ GetIntrinsicVScrollbarWidth(aRenderingContext
);
740 /* virtual */ nscoord
741 nsHTMLScrollFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
743 nscoord result
= mInner
.mScrolledFrame
->GetPrefWidth(aRenderingContext
);
744 DISPLAY_PREF_WIDTH(this, result
);
745 return NSCoordSaturatingAdd(result
, GetIntrinsicVScrollbarWidth(aRenderingContext
));
749 nsHTMLScrollFrame::GetPadding(nsMargin
& aMargin
)
751 // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
752 // reaize that. If we're stuck inside a XUL box, we need to claim no
754 // @see also nsXULScrollFrame::GetPadding.
755 aMargin
.SizeTo(0,0,0,0);
760 nsHTMLScrollFrame::IsCollapsed(nsBoxLayoutState
& aBoxLayoutState
)
762 // We're never collapsed in the box sense.
767 nsHTMLScrollFrame::Reflow(nsPresContext
* aPresContext
,
768 nsHTMLReflowMetrics
& aDesiredSize
,
769 const nsHTMLReflowState
& aReflowState
,
770 nsReflowStatus
& aStatus
)
772 DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
773 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
775 ScrollReflowState
state(this, aReflowState
);
776 // sanity check: ensure that if we have no scrollbar, we treat it
778 if (!mInner
.mVScrollbarBox
|| mInner
.mNeverHasVerticalScrollbar
)
779 state
.mStyles
.mVertical
= NS_STYLE_OVERFLOW_HIDDEN
;
780 if (!mInner
.mHScrollbarBox
|| mInner
.mNeverHasHorizontalScrollbar
)
781 state
.mStyles
.mHorizontal
= NS_STYLE_OVERFLOW_HIDDEN
;
783 //------------ Handle Incremental Reflow -----------------
784 PRBool reflowContents
= PR_TRUE
; // XXX Ignored
785 PRBool reflowHScrollbar
= PR_TRUE
;
786 PRBool reflowVScrollbar
= PR_TRUE
;
787 PRBool reflowScrollCorner
= PR_TRUE
;
788 if (!aReflowState
.ShouldReflowAllKids()) {
789 #define NEEDS_REFLOW(frame_) \
790 ((frame_) && NS_SUBTREE_DIRTY(frame_))
792 reflowContents
= NEEDS_REFLOW(mInner
.mScrolledFrame
);
793 reflowHScrollbar
= NEEDS_REFLOW(mInner
.mHScrollbarBox
);
794 reflowVScrollbar
= NEEDS_REFLOW(mInner
.mVScrollbarBox
);
795 reflowScrollCorner
= NEEDS_REFLOW(mInner
.mScrollCornerBox
);
800 nsRect oldScrollAreaBounds
= mInner
.mScrollPort
;
801 nsRect oldScrolledAreaBounds
=
802 mInner
.mScrolledFrame
->GetOverflowRectRelativeToParent();
803 // Adjust to a multiple of device pixels to restore the invariant that
804 // oldScrollPosition is a multiple of device pixels. This could have been
805 // thrown out by a zoom change.
807 nsPoint oldScrollPosition
= mInner
.GetScrollPosition();
809 state
.mComputedBorder
= aReflowState
.mComputedBorderPadding
-
810 aReflowState
.mComputedPadding
;
812 nsresult rv
= ReflowContents(&state
, aDesiredSize
);
816 // Restore the old scroll position, for now, even if that's not valid anymore
817 // because we changed size. We'll fix it up in a post-reflow callback, because
818 // our current size may only be temporary (e.g. we're compute XUL desired sizes).
819 PlaceScrollArea(state
, oldScrollPosition
);
820 if (!mInner
.mPostedReflowCallback
) {
821 // Make sure we'll try scrolling to restored position
822 PresContext()->PresShell()->PostReflowCallback(&mInner
);
823 mInner
.mPostedReflowCallback
= PR_TRUE
;
826 PRBool didHaveHScrollbar
= mInner
.mHasHorizontalScrollbar
;
827 PRBool didHaveVScrollbar
= mInner
.mHasVerticalScrollbar
;
828 mInner
.mHasHorizontalScrollbar
= state
.mShowHScrollbar
;
829 mInner
.mHasVerticalScrollbar
= state
.mShowVScrollbar
;
830 nsRect newScrollAreaBounds
= mInner
.mScrollPort
;
831 nsRect newScrolledAreaBounds
=
832 mInner
.mScrolledFrame
->GetOverflowRectRelativeToParent();
833 if (mInner
.mSkippedScrollbarLayout
||
834 reflowHScrollbar
|| reflowVScrollbar
|| reflowScrollCorner
||
835 (GetStateBits() & NS_FRAME_IS_DIRTY
) ||
836 didHaveHScrollbar
!= state
.mShowHScrollbar
||
837 didHaveVScrollbar
!= state
.mShowVScrollbar
||
838 oldScrollAreaBounds
!= newScrollAreaBounds
||
839 oldScrolledAreaBounds
!= newScrolledAreaBounds
) {
840 if (!mInner
.mSupppressScrollbarUpdate
) {
841 mInner
.mSkippedScrollbarLayout
= PR_FALSE
;
842 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, state
.mShowHScrollbar
);
843 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, state
.mShowVScrollbar
);
844 // place and reflow scrollbars
845 nsRect insideBorderArea
=
846 nsRect(nsPoint(state
.mComputedBorder
.left
, state
.mComputedBorder
.top
),
847 state
.mInsideBorderSize
);
848 mInner
.LayoutScrollbars(state
.mBoxState
, insideBorderArea
,
849 oldScrollAreaBounds
);
851 mInner
.mSkippedScrollbarLayout
= PR_TRUE
;
855 aDesiredSize
.width
= state
.mInsideBorderSize
.width
+
856 state
.mComputedBorder
.LeftRight();
857 aDesiredSize
.height
= state
.mInsideBorderSize
.height
+
858 state
.mComputedBorder
.TopBottom();
860 aDesiredSize
.mOverflowArea
= nsRect(0, 0, aDesiredSize
.width
, aDesiredSize
.height
);
862 CheckInvalidateSizeChange(aDesiredSize
);
864 FinishAndStoreOverflow(&aDesiredSize
);
866 if (!InInitialReflow() && !mInner
.mHadNonInitialReflow
) {
867 mInner
.mHadNonInitialReflow
= PR_TRUE
;
870 if (mInner
.mIsRoot
&& oldScrolledAreaBounds
!= newScrolledAreaBounds
) {
871 mInner
.PostScrolledAreaEvent();
874 aStatus
= NS_FRAME_COMPLETE
;
875 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
876 mInner
.PostOverflowEvent();
881 ////////////////////////////////////////////////////////////////////////////////
885 nsHTMLScrollFrame::GetFrameName(nsAString
& aResult
) const
887 return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult
);
892 already_AddRefed
<nsAccessible
>
893 nsHTMLScrollFrame::CreateAccessible()
895 if (!IsFocusable()) {
898 // Focusable via CSS, so needs to be in accessibility hierarchy
899 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
902 return accService
->CreateHyperTextAccessible(mContent
,
903 PresContext()->PresShell());
910 NS_QUERYFRAME_HEAD(nsHTMLScrollFrame
)
911 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
912 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
913 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
914 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame
)
916 //----------nsXULScrollFrame-------------------------------------------
919 NS_NewXULScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
921 return new (aPresShell
) nsXULScrollFrame(aPresShell
, aContext
, aIsRoot
);
924 NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame
)
926 nsXULScrollFrame::nsXULScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
927 : nsBoxFrame(aShell
, aContext
, aIsRoot
),
928 mInner(this, aIsRoot
, PR_TRUE
)
930 SetLayoutManager(nsnull
);
933 nsMargin
nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
934 NS_ASSERTION(aState
&& aState
->GetRenderingContext(),
935 "Must have rendering context in layout state for size "
938 nsMargin
result(0, 0, 0, 0);
940 if (mVScrollbarBox
) {
941 nsSize size
= mVScrollbarBox
->GetPrefSize(*aState
);
942 nsBox::AddMargin(mVScrollbarBox
, size
);
943 if (IsScrollbarOnRight())
944 result
.left
= size
.width
;
946 result
.right
= size
.width
;
949 if (mHScrollbarBox
) {
950 nsSize size
= mHScrollbarBox
->GetPrefSize(*aState
);
951 nsBox::AddMargin(mHScrollbarBox
, size
);
952 // We don't currently support any scripts that would require a scrollbar
953 // at the top. (Are there any?)
954 result
.bottom
= size
.height
;
961 nsXULScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
963 return mInner
.CreateAnonymousContent(aElements
);
967 nsXULScrollFrame::AppendAnonymousContentTo(nsBaseContentList
& aElements
)
969 mInner
.AppendAnonymousContentTo(aElements
);
973 nsXULScrollFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
976 nsBoxFrame::DestroyFrom(aDestructRoot
);
980 nsXULScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
981 nsFrameList
& aChildList
)
983 nsresult rv
= nsBoxFrame::SetInitialChildList(aListName
, aChildList
);
984 mInner
.ReloadChildFrames();
990 nsXULScrollFrame::AppendFrames(nsIAtom
* aListName
,
991 nsFrameList
& aFrameList
)
993 nsresult rv
= nsBoxFrame::AppendFrames(aListName
, aFrameList
);
994 mInner
.ReloadChildFrames();
999 nsXULScrollFrame::InsertFrames(nsIAtom
* aListName
,
1000 nsIFrame
* aPrevFrame
,
1001 nsFrameList
& aFrameList
)
1003 nsresult rv
= nsBoxFrame::InsertFrames(aListName
, aPrevFrame
, aFrameList
);
1004 mInner
.ReloadChildFrames();
1009 nsXULScrollFrame::RemoveFrame(nsIAtom
* aListName
,
1010 nsIFrame
* aOldFrame
)
1012 nsresult rv
= nsBoxFrame::RemoveFrame(aListName
, aOldFrame
);
1013 mInner
.ReloadChildFrames();
1018 nsXULScrollFrame::GetSplittableType() const
1020 return NS_FRAME_NOT_SPLITTABLE
;
1024 nsXULScrollFrame::GetPadding(nsMargin
& aMargin
)
1026 aMargin
.SizeTo(0,0,0,0);
1031 nsXULScrollFrame::GetSkipSides() const
1037 nsXULScrollFrame::GetType() const
1039 return nsGkAtoms::scrollFrame
;
1043 nsXULScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
1044 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
1047 if (aForChild
== mInner
.mScrolledFrame
) {
1048 // restrict aDamageRect to the scrollable view's bounds
1049 nsRect damage
= aDamageRect
+ nsPoint(aX
, aY
) +
1050 GetScrollPosition() - mInner
.mScrollPosAtLastPaint
;
1052 if (r
.IntersectRect(damage
, mInner
.mScrollPort
)) {
1053 nsBoxFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
1058 nsBoxFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
1062 nsXULScrollFrame::GetBoxAscent(nsBoxLayoutState
& aState
)
1064 if (!mInner
.mScrolledFrame
)
1067 nscoord ascent
= mInner
.mScrolledFrame
->GetBoxAscent(aState
);
1068 nsMargin
m(0,0,0,0);
1069 GetBorderAndPadding(m
);
1078 nsXULScrollFrame::GetPrefSize(nsBoxLayoutState
& aState
)
1081 PropagateDebug(aState
);
1084 nsSize pref
= mInner
.mScrolledFrame
->GetPrefSize(aState
);
1086 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1088 // scrolled frames don't have their own margins
1090 if (mInner
.mVScrollbarBox
&&
1091 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1092 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
1093 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
1094 pref
.width
+= vSize
.width
;
1097 if (mInner
.mHScrollbarBox
&&
1098 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1099 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
1100 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
1101 pref
.height
+= hSize
.height
;
1104 AddBorderAndPadding(pref
);
1105 PRBool widthSet
, heightSet
;
1106 nsIBox::AddCSSPrefSize(this, pref
, widthSet
, heightSet
);
1111 nsXULScrollFrame::GetMinSize(nsBoxLayoutState
& aState
)
1114 PropagateDebug(aState
);
1117 nsSize min
= mInner
.mScrolledFrame
->GetMinSizeForScrollArea(aState
);
1119 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1121 if (mInner
.mVScrollbarBox
&&
1122 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1123 nsSize vSize
= mInner
.mVScrollbarBox
->GetMinSize(aState
);
1124 AddMargin(mInner
.mVScrollbarBox
, vSize
);
1125 min
.width
+= vSize
.width
;
1126 if (min
.height
< vSize
.height
)
1127 min
.height
= vSize
.height
;
1130 if (mInner
.mHScrollbarBox
&&
1131 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1132 nsSize hSize
= mInner
.mHScrollbarBox
->GetMinSize(aState
);
1133 AddMargin(mInner
.mHScrollbarBox
, hSize
);
1134 min
.height
+= hSize
.height
;
1135 if (min
.width
< hSize
.width
)
1136 min
.width
= hSize
.width
;
1139 AddBorderAndPadding(min
);
1140 PRBool widthSet
, heightSet
;
1141 nsIBox::AddCSSMinSize(aState
, this, min
, widthSet
, heightSet
);
1146 nsXULScrollFrame::GetMaxSize(nsBoxLayoutState
& aState
)
1149 PropagateDebug(aState
);
1152 nsSize
maxSize(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
1154 AddBorderAndPadding(maxSize
);
1155 PRBool widthSet
, heightSet
;
1156 nsIBox::AddCSSMaxSize(this, maxSize
, widthSet
, heightSet
);
1162 nsXULScrollFrame::GetFrameName(nsAString
& aResult
) const
1164 return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult
);
1169 nsXULScrollFrame::DoLayout(nsBoxLayoutState
& aState
)
1171 PRUint32 flags
= aState
.LayoutFlags();
1172 nsresult rv
= Layout(aState
);
1173 aState
.SetLayoutFlags(flags
);
1175 nsBox::DoLayout(aState
);
1179 NS_QUERYFRAME_HEAD(nsXULScrollFrame
)
1180 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
1181 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
1182 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
1183 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
)
1185 //-------------------- Inner ----------------------
1187 #define SMOOTH_SCROLL_MSECS_PER_FRAME 10
1188 #define SMOOTH_SCROLL_FRAMES 10
1190 #define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
1192 class nsGfxScrollFrameInner::AsyncScroll
{
1196 if (mScrollTimer
) mScrollTimer
->Cancel();
1199 nsCOMPtr
<nsITimer
> mScrollTimer
;
1200 PRInt32 mVelocities
[SMOOTH_SCROLL_FRAMES
*2];
1201 PRInt32 mFrameIndex
;
1202 PRPackedBool mIsSmoothScroll
;
1205 static void ComputeVelocities(PRInt32 aCurVelocity
, nscoord aCurPos
, nscoord aDstPos
,
1206 PRInt32
* aVelocities
, PRInt32 aP2A
)
1208 // scrolling always works in units of whole pixels. So compute velocities
1209 // in pixels and then scale them up. This ensures, for example, that
1210 // a 1-pixel scroll isn't broken into N frames of 1/N pixels each, each
1211 // frame increment being rounded to 0 whole pixels.
1212 aCurPos
= NSAppUnitsToIntPixels(aCurPos
, aP2A
);
1213 aDstPos
= NSAppUnitsToIntPixels(aDstPos
, aP2A
);
1216 PRInt32 direction
= (aCurPos
< aDstPos
? 1 : -1);
1217 PRInt32 absDelta
= (aDstPos
- aCurPos
)*direction
;
1218 PRInt32 baseVelocity
= absDelta
/SMOOTH_SCROLL_FRAMES
;
1220 for (i
= 0; i
< SMOOTH_SCROLL_FRAMES
; i
++) {
1221 aVelocities
[i
*2] = baseVelocity
;
1223 nscoord total
= baseVelocity
*SMOOTH_SCROLL_FRAMES
;
1224 for (i
= 0; i
< SMOOTH_SCROLL_FRAMES
; i
++) {
1225 if (total
< absDelta
) {
1230 NS_ASSERTION(total
== absDelta
, "Invalid velocity sum");
1232 PRInt32 scale
= NSIntPixelsToAppUnits(direction
, aP2A
);
1233 for (i
= 0; i
< SMOOTH_SCROLL_FRAMES
; i
++) {
1234 aVelocities
[i
*2] *= scale
;
1239 IsSmoothScrollingEnabled()
1241 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1244 nsresult rv
= prefs
->GetBoolPref(SMOOTH_SCROLL_PREF_NAME
, &enabled
);
1245 if (NS_SUCCEEDED(rv
)) {
1252 class ScrollFrameActivityTracker
: public nsExpirationTracker
<nsGfxScrollFrameInner
,4> {
1254 // Wait for 75-100ms between scrolls before we switch the appearance back to
1255 // subpixel AA. That's 4 generations of 25ms each.
1256 enum { TIMEOUT_MS
= 25 };
1257 ScrollFrameActivityTracker()
1258 : nsExpirationTracker
<nsGfxScrollFrameInner
,4>(TIMEOUT_MS
) {}
1259 ~ScrollFrameActivityTracker() {
1260 AgeAllGenerations();
1263 virtual void NotifyExpired(nsGfxScrollFrameInner
*aObject
) {
1264 RemoveObject(aObject
);
1265 aObject
->mScrollingActive
= PR_FALSE
;
1266 aObject
->mOuter
->InvalidateOverflowRect();
1270 static ScrollFrameActivityTracker
*gScrollFrameActivityTracker
= nsnull
;
1272 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame
* aOuter
,
1275 : mHScrollbarBox(nsnull
),
1276 mVScrollbarBox(nsnull
),
1277 mScrolledFrame(nsnull
),
1278 mScrollCornerBox(nsnull
),
1280 mAsyncScroll(nsnull
),
1282 mScrollPosAtLastPaint(0, 0),
1283 mRestorePos(-1, -1),
1285 mNeverHasVerticalScrollbar(PR_FALSE
),
1286 mNeverHasHorizontalScrollbar(PR_FALSE
),
1287 mHasVerticalScrollbar(PR_FALSE
),
1288 mHasHorizontalScrollbar(PR_FALSE
),
1289 mFrameIsUpdatingScrollbar(PR_FALSE
),
1290 mDidHistoryRestore(PR_FALSE
),
1293 mSupppressScrollbarUpdate(PR_FALSE
),
1294 mSkippedScrollbarLayout(PR_FALSE
),
1295 mHadNonInitialReflow(PR_FALSE
),
1296 mHorizontalOverflow(PR_FALSE
),
1297 mVerticalOverflow(PR_FALSE
),
1298 mPostedReflowCallback(PR_FALSE
),
1299 mMayHaveDirtyFixedChildren(PR_FALSE
),
1300 mUpdateScrollbarAttributes(PR_FALSE
),
1301 mScrollingActive(PR_FALSE
)
1305 nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
1307 if (mActivityExpirationState
.IsTracked()) {
1308 gScrollFrameActivityTracker
->RemoveObject(this);
1310 if (gScrollFrameActivityTracker
&&
1311 gScrollFrameActivityTracker
->IsEmpty()) {
1312 delete gScrollFrameActivityTracker
;
1313 gScrollFrameActivityTracker
= nsnull
;
1315 delete mAsyncScroll
;
1319 Clamp(nscoord aLower
, nscoord aVal
, nscoord aUpper
)
1329 nsGfxScrollFrameInner::ClampScrollPosition(const nsPoint
& aPt
) const
1331 nsRect range
= GetScrollRange();
1332 return nsPoint(Clamp(range
.x
, aPt
.x
, range
.XMost()),
1333 Clamp(range
.y
, aPt
.y
, range
.YMost()));
1337 * Callback function from timer used in nsGfxScrollFrameInner::ScrollTo
1340 nsGfxScrollFrameInner::AsyncScrollCallback(nsITimer
*aTimer
, void* anInstance
)
1342 nsGfxScrollFrameInner
* self
= static_cast<nsGfxScrollFrameInner
*>(anInstance
);
1343 if (!self
|| !self
->mAsyncScroll
)
1346 if (self
->mAsyncScroll
->mIsSmoothScroll
) {
1347 // XXX this is crappy, the scroll position needs to be based on the
1349 NS_ASSERTION(self
->mAsyncScroll
->mFrameIndex
< SMOOTH_SCROLL_FRAMES
,
1350 "Past last frame?");
1351 nscoord
* velocities
=
1352 &self
->mAsyncScroll
->mVelocities
[self
->mAsyncScroll
->mFrameIndex
*2];
1353 nsPoint destination
=
1354 self
->GetScrollPosition() + nsPoint(velocities
[0], velocities
[1]);
1356 self
->mAsyncScroll
->mFrameIndex
++;
1357 if (self
->mAsyncScroll
->mFrameIndex
>= SMOOTH_SCROLL_FRAMES
) {
1358 delete self
->mAsyncScroll
;
1359 self
->mAsyncScroll
= nsnull
;
1362 self
->ScrollToImpl(destination
);
1364 delete self
->mAsyncScroll
;
1365 self
->mAsyncScroll
= nsnull
;
1367 self
->ScrollToImpl(self
->mDestination
);
1372 * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
1373 * based on the setting of the smooth scroll pref
1376 nsGfxScrollFrameInner::ScrollTo(nsPoint aScrollPosition
,
1377 nsIScrollableFrame::ScrollMode aMode
)
1379 mDestination
= ClampScrollPosition(aScrollPosition
);
1381 if (aMode
== nsIScrollableFrame::INSTANT
) {
1382 // Asynchronous scrolling is not allowed, so we'll kill any existing
1383 // async-scrolling process and do an instant scroll
1384 delete mAsyncScroll
;
1385 mAsyncScroll
= nsnull
;
1386 ScrollToImpl(mDestination
);
1390 PRInt32 currentVelocityX
= 0;
1391 PRInt32 currentVelocityY
= 0;
1392 PRBool isSmoothScroll
= IsSmoothScrollingEnabled();
1395 if (mAsyncScroll
->mIsSmoothScroll
) {
1396 currentVelocityX
= mAsyncScroll
->mVelocities
[mAsyncScroll
->mFrameIndex
*2];
1397 currentVelocityY
= mAsyncScroll
->mVelocities
[mAsyncScroll
->mFrameIndex
*2 + 1];
1400 mAsyncScroll
= new AsyncScroll
;
1402 mAsyncScroll
->mScrollTimer
= do_CreateInstance("@mozilla.org/timer;1");
1403 if (!mAsyncScroll
->mScrollTimer
) {
1404 delete mAsyncScroll
;
1405 mAsyncScroll
= nsnull
;
1408 if (!mAsyncScroll
) {
1409 // some allocation failed. Scroll the normal way.
1410 ScrollToImpl(mDestination
);
1413 if (isSmoothScroll
) {
1414 mAsyncScroll
->mScrollTimer
->InitWithFuncCallback(
1415 AsyncScrollCallback
, this, SMOOTH_SCROLL_MSECS_PER_FRAME
,
1416 nsITimer::TYPE_REPEATING_PRECISE
);
1418 mAsyncScroll
->mScrollTimer
->InitWithFuncCallback(
1419 AsyncScrollCallback
, this, 0, nsITimer::TYPE_ONE_SHOT
);
1423 mAsyncScroll
->mFrameIndex
= 0;
1424 mAsyncScroll
->mIsSmoothScroll
= isSmoothScroll
;
1426 if (isSmoothScroll
) {
1427 PRInt32 p2a
= mOuter
->PresContext()->AppUnitsPerDevPixel();
1429 // compute velocity vectors
1430 nsPoint currentPos
= GetScrollPosition();
1431 ComputeVelocities(currentVelocityX
, currentPos
.x
, mDestination
.x
,
1432 mAsyncScroll
->mVelocities
, p2a
);
1433 ComputeVelocities(currentVelocityY
, currentPos
.y
, mDestination
.y
,
1434 mAsyncScroll
->mVelocities
+ 1, p2a
);
1438 static void InvalidateWidgets(nsIView
* aView
)
1440 if (aView
->HasWidget()) {
1441 nsIWidget
* widget
= aView
->GetWidget();
1443 widget
->GetWindowType(type
);
1444 if (type
!= eWindowType_popup
) {
1445 // Force the widget and everything in it to repaint. We can't
1446 // just use Invalidate because the widget might have child
1447 // widgets and they wouldn't get updated. We can't call
1448 // UpdateView(aView) because the area to be repainted might be
1449 // outside aView's clipped bounds. This isn't the greatest way
1450 // to achieve this, perhaps, but it works.
1451 widget
->Show(PR_FALSE
);
1452 widget
->Show(PR_TRUE
);
1457 for (nsIView
* v
= aView
->GetFirstChild(); v
; v
= v
->GetNextSibling()) {
1458 InvalidateWidgets(v
);
1462 // We can't use nsContainerFrame::PositionChildViews here because
1463 // we don't want to invalidate views that have moved.
1464 // aInvalidateWidgets is set to true if we should invalidate the area
1465 // covered by every widget in the subtree.
1466 static void AdjustViewsAndWidgets(nsIFrame
* aFrame
,
1467 PRBool aInvalidateWidgets
)
1469 nsIView
* view
= aFrame
->GetView();
1472 aFrame
->GetParent()->GetClosestView(&pt
);
1473 pt
+= aFrame
->GetPosition();
1474 view
->SetPosition(pt
.x
, pt
.y
);
1476 if (aInvalidateWidgets
) {
1477 InvalidateWidgets(view
);
1482 if (!(aFrame
->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW
)) {
1486 nsIAtom
* childListName
= nsnull
;
1487 PRInt32 childListIndex
= 0;
1489 // Recursively walk aFrame's child frames
1490 nsIFrame
* childFrame
= aFrame
->GetFirstChild(childListName
);
1491 while (childFrame
) {
1492 AdjustViewsAndWidgets(childFrame
, aInvalidateWidgets
);
1494 // Get the next sibling child frame
1495 childFrame
= childFrame
->GetNextSibling();
1498 // also process the additional child lists, but skip the popup list as the
1499 // views for popups are not scrolled.
1501 childListName
= aFrame
->GetAdditionalChildListName(childListIndex
++);
1502 } while (childListName
== nsGkAtoms::popupList
);
1503 } while (childListName
);
1507 CanScrollWithBlitting(nsIFrame
* aFrame
, nsIFrame
* aDisplayRoot
)
1509 for (nsIFrame
* f
= aFrame
; f
;
1510 f
= nsLayoutUtils::GetCrossDocParentFrame(f
)) {
1511 if (f
->GetStyleDisplay()->HasTransform()) {
1515 if (nsSVGIntegrationUtils::UsingEffectsForFrame(f
) ||
1516 f
->IsFrameOfType(nsIFrame::eSVG
)) {
1520 if (f
== aDisplayRoot
)
1527 InvalidateFixedBackgroundFramesFromList(nsDisplayListBuilder
* aBuilder
,
1528 nsIFrame
* aMovingFrame
,
1529 const nsDisplayList
& aList
)
1531 for (nsDisplayItem
* item
= aList
.GetBottom(); item
; item
= item
->GetAbove()) {
1532 nsDisplayList
* sublist
= item
->GetList();
1534 InvalidateFixedBackgroundFramesFromList(aBuilder
, aMovingFrame
, *sublist
);
1537 nsIFrame
* f
= item
->GetUnderlyingFrame();
1539 item
->IsVaryingRelativeToMovingFrame(aBuilder
, aMovingFrame
)) {
1540 if (item
->IsFixedAndCoveringViewport(aBuilder
)) {
1541 // FrameLayerBuilder takes care of scrolling these
1543 f
->Invalidate(item
->GetVisibleRect() - item
->ToReferenceFrame());
1550 InvalidateFixedBackgroundFrames(nsIFrame
* aRootFrame
,
1551 nsIFrame
* aMovingFrame
,
1552 const nsRect
& aUpdateRect
)
1554 if (!aMovingFrame
->PresContext()->MayHaveFixedBackgroundFrames())
1557 NS_ASSERTION(aRootFrame
!= aMovingFrame
,
1558 "The root frame shouldn't be the one that's moving, that makes no sense");
1560 // Build the 'after' display list over the whole area of interest.
1561 nsDisplayListBuilder
builder(aRootFrame
, PR_FALSE
, PR_TRUE
);
1562 builder
.EnterPresShell(aRootFrame
, aUpdateRect
);
1565 aRootFrame
->BuildDisplayListForStackingContext(&builder
, aUpdateRect
, &list
);
1566 builder
.LeavePresShell(aRootFrame
, aUpdateRect
);
1570 nsRegion
visibleRegion(aUpdateRect
);
1571 list
.ComputeVisibility(&builder
, &visibleRegion
);
1573 InvalidateFixedBackgroundFramesFromList(&builder
, aMovingFrame
, list
);
1577 PRBool
nsGfxScrollFrameInner::IsAlwaysActive() const
1579 // The root scrollframe for a non-chrome document which is the direct
1580 // child of a chrome document is always treated as "active".
1581 // XXX maybe we should extend this so that IFRAMEs which are fill the
1582 // entire viewport (like GMail!) are always active
1584 !nsContentUtils::IsChildOfSameType(mOuter
->GetContent()->GetCurrentDoc());
1587 PRBool
nsGfxScrollFrameInner::IsScrollingActive() const
1589 return mScrollingActive
|| IsAlwaysActive();
1592 void nsGfxScrollFrameInner::MarkActive()
1594 if (IsAlwaysActive())
1597 mScrollingActive
= PR_TRUE
;
1598 if (mActivityExpirationState
.IsTracked()) {
1599 gScrollFrameActivityTracker
->MarkUsed(this);
1601 if (!gScrollFrameActivityTracker
) {
1602 gScrollFrameActivityTracker
= new ScrollFrameActivityTracker();
1604 gScrollFrameActivityTracker
->AddObject(this);
1608 void nsGfxScrollFrameInner::ScrollVisual(nsIntPoint aPixDelta
)
1610 nsRootPresContext
* rootPresContext
= mOuter
->PresContext()->GetRootPresContext();
1611 if (!rootPresContext
) {
1615 rootPresContext
->RequestUpdatePluginGeometry(mOuter
);
1617 AdjustViewsAndWidgets(mScrolledFrame
, PR_FALSE
);
1618 // We need to call this after fixing up the widget and view positions
1619 // to be consistent with the view and frame hierarchy.
1620 PRUint32 flags
= nsIFrame::INVALIDATE_REASON_SCROLL_REPAINT
;
1621 nsIFrame
* displayRoot
= nsLayoutUtils::GetDisplayRootFrame(mOuter
);
1622 if (IsScrollingActive() && CanScrollWithBlitting(mOuter
, displayRoot
)) {
1623 flags
|= nsIFrame::INVALIDATE_NO_THEBES_LAYERS
;
1626 mOuter
->InvalidateWithFlags(mScrollPort
, flags
);
1628 if (flags
& nsIFrame::INVALIDATE_NO_THEBES_LAYERS
) {
1630 GetScrollPortRect() + mOuter
->GetOffsetToCrossDoc(displayRoot
);
1631 update
= update
.ConvertAppUnitsRoundOut(
1632 mOuter
->PresContext()->AppUnitsPerDevPixel(),
1633 displayRoot
->PresContext()->AppUnitsPerDevPixel());
1634 InvalidateFixedBackgroundFrames(displayRoot
, mScrolledFrame
, update
);
1639 ClampInt(nscoord aLower
, nscoord aVal
, nscoord aUpper
, nscoord aAppUnitsPerPixel
)
1641 PRInt32 low
= NSToIntCeil(float(aLower
)/aAppUnitsPerPixel
);
1642 PRInt32 high
= NSToIntFloor(float(aUpper
)/aAppUnitsPerPixel
);
1643 PRInt32 v
= NSToIntRound(float(aVal
)/aAppUnitsPerPixel
);
1644 NS_ASSERTION(low
<= high
, "No integers in range; 0 is supposed to be in range");
1653 nsGfxScrollFrameInner::ClampAndRestrictToDevPixels(const nsPoint
& aPt
,
1654 nsIntPoint
* aPtDevPx
) const
1656 nsPresContext
* presContext
= mOuter
->PresContext();
1657 nscoord appUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
1658 // Convert to device pixels so we scroll to an integer offset of device
1659 // pixels. But we also need to make sure that our position remains
1660 // inside the allowed region.
1661 nsRect scrollRange
= GetScrollRange();
1662 *aPtDevPx
= nsIntPoint(ClampInt(scrollRange
.x
, aPt
.x
, scrollRange
.XMost(), appUnitsPerDevPixel
),
1663 ClampInt(scrollRange
.y
, aPt
.y
, scrollRange
.YMost(), appUnitsPerDevPixel
));
1664 return nsPoint(NSIntPixelsToAppUnits(aPtDevPx
->x
, appUnitsPerDevPixel
),
1665 NSIntPixelsToAppUnits(aPtDevPx
->y
, appUnitsPerDevPixel
));
1669 nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt
)
1671 nsPresContext
* presContext
= mOuter
->PresContext();
1672 nscoord appUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
1674 nsPoint pt
= ClampAndRestrictToDevPixels(aPt
, &ptDevPx
);
1676 nsPoint curPos
= GetScrollPosition();
1680 nsIntPoint
curPosDevPx(NSAppUnitsToIntPixels(curPos
.x
, appUnitsPerDevPixel
),
1681 NSAppUnitsToIntPixels(curPos
.y
, appUnitsPerDevPixel
));
1682 // We maintain the invariant that the scroll position is a multiple of device
1684 NS_ASSERTION(curPosDevPx
.x
*appUnitsPerDevPixel
== curPos
.x
,
1685 "curPos.x not a multiple of device pixels");
1686 NS_ASSERTION(curPosDevPx
.y
*appUnitsPerDevPixel
== curPos
.y
,
1687 "curPos.y not a multiple of device pixels");
1689 // notify the listeners.
1690 for (PRUint32 i
= 0; i
< mListeners
.Length(); i
++) {
1691 mListeners
[i
]->ScrollPositionWillChange(pt
.x
, pt
.y
);
1694 // Update frame position for scrolling
1695 mScrolledFrame
->SetPosition(mScrollPort
.TopLeft() - pt
);
1697 // We pass in the amount to move visually
1698 ScrollVisual(curPosDevPx
- ptDevPx
);
1700 presContext
->PresShell()->GetViewManager()->SynthesizeMouseMove(PR_TRUE
);
1701 UpdateScrollbarPosition();
1704 // notify the listeners.
1705 for (PRUint32 i
= 0; i
< mListeners
.Length(); i
++) {
1706 mListeners
[i
]->ScrollPositionDidChange(pt
.x
, pt
.y
);
1711 AppendToTop(nsDisplayListBuilder
* aBuilder
, nsDisplayList
* aDest
,
1712 nsDisplayList
* aSource
, nsIFrame
* aSourceFrame
, PRBool aOwnLayer
)
1715 aDest
->AppendNewToTop(
1716 new (aBuilder
) nsDisplayOwnLayer(aBuilder
, aSourceFrame
, aSource
));
1718 aDest
->AppendToTop(aSource
);
1723 nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1724 const nsRect
& aDirtyRect
,
1725 const nsDisplayListSet
& aLists
)
1727 nsresult rv
= mOuter
->DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1728 NS_ENSURE_SUCCESS(rv
, rv
);
1730 if (aBuilder
->IsPaintingToWindow()) {
1731 mScrollPosAtLastPaint
= GetScrollPosition();
1734 if (aBuilder
->GetIgnoreScrollFrame() == mOuter
) {
1735 // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
1736 // The scrolled frame shouldn't have its own background/border, so we
1737 // can just pass aLists directly.
1738 return mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
,
1739 aDirtyRect
, aLists
);
1742 // Now display the scrollbars and scrollcorner. These parts are drawn
1743 // in the border-background layer, on top of our own background and
1744 // borders and underneath borders and backgrounds of later elements
1746 PRBool hasResizer
= HasResizer();
1747 nsDisplayListCollection scrollParts
;
1748 // We put scrollbars in their own layers when this is the root scroll
1749 // frame and we are a toplevel content document. In this situation, the
1750 // scrollbar(s) would normally be assigned their own layer anyway, since
1751 // they're not scrolled with the rest of the document. But when both
1752 // scrollbars are visible, the layer's visible rectangle would be the size
1753 // of the viewport, so most layer implementations would create a layer buffer
1754 // that's much larger than necessary. Creating independent layers for each
1755 // scrollbar works around the problem.
1756 PRBool createLayersForScrollbars
= mIsRoot
&&
1757 !nsContentUtils::IsChildOfSameType(mOuter
->GetContent()->GetCurrentDoc());
1758 for (nsIFrame
* kid
= mOuter
->GetFirstChild(nsnull
); kid
; kid
= kid
->GetNextSibling()) {
1759 if (kid
!= mScrolledFrame
) {
1760 if (kid
== mScrollCornerBox
&& hasResizer
) {
1761 // skip the resizer as this will be drawn later on top of the scrolled content
1764 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, scrollParts
,
1765 nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT
);
1766 NS_ENSURE_SUCCESS(rv
, rv
);
1767 // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into the
1768 // PositionedDescendants list.
1769 ::AppendToTop(aBuilder
, aLists
.BorderBackground(),
1770 scrollParts
.PositionedDescendants(), kid
,
1771 createLayersForScrollbars
);
1776 // Overflow clipping can never clip frames outside our subtree, so there
1777 // is no need to worry about whether we are a moving frame that might clip
1778 // non-moving frames.
1780 // Not all our descendants will be clipped by overflow clipping, but all
1781 // the ones that aren't clipped will be out of flow frames that have already
1782 // had dirty rects saved for them by their parent frames calling
1783 // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
1785 dirtyRect
.IntersectRect(aDirtyRect
, mScrollPort
);
1787 nsDisplayListCollection set
;
1788 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
, dirtyRect
, set
);
1789 NS_ENSURE_SUCCESS(rv
, rv
);
1790 nsRect clip
= mScrollPort
+ aBuilder
->ToReferenceFrame(mOuter
);
1791 // mScrolledFrame may have given us a background, e.g., the scrolled canvas
1792 // frame below the viewport. If so, we want it to be clipped. We also want
1793 // to end up on our BorderBackground list.
1794 // If we are the viewport scrollframe, then clip all our descendants (to ensure
1795 // that fixed-pos elements get clipped by us).
1796 rv
= mOuter
->OverflowClip(aBuilder
, set
, aLists
, clip
, PR_TRUE
, mIsRoot
);
1797 NS_ENSURE_SUCCESS(rv
, rv
);
1799 // Place the resizer in the display list in our Content() list above
1800 // scrolled content in the Content() list.
1801 // This ensures that the resizer appears above the content and the mouse can
1802 // still target the resizer even when scrollbars are hidden.
1803 if (hasResizer
&& mScrollCornerBox
) {
1804 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, mScrollCornerBox
, aDirtyRect
, scrollParts
,
1805 nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT
);
1806 NS_ENSURE_SUCCESS(rv
, rv
);
1807 // DISPLAY_CHILD_FORCE_STACKING_CONTEXT puts everything into the
1808 // PositionedDescendants list.
1809 ::AppendToTop(aBuilder
, aLists
.Content(),
1810 scrollParts
.PositionedDescendants(), mScrollCornerBox
,
1811 createLayersForScrollbars
);
1817 static void HandleScrollPref(nsIScrollable
*aScrollable
, PRInt32 aOrientation
,
1821 aScrollable
->GetDefaultScrollbarPreferences(aOrientation
, &pref
);
1823 case nsIScrollable::Scrollbar_Auto
:
1824 // leave |aValue| untouched
1826 case nsIScrollable::Scrollbar_Never
:
1827 aValue
= NS_STYLE_OVERFLOW_HIDDEN
;
1829 case nsIScrollable::Scrollbar_Always
:
1830 aValue
= NS_STYLE_OVERFLOW_SCROLL
;
1835 nsGfxScrollFrameInner::ScrollbarStyles
1836 nsGfxScrollFrameInner::GetScrollbarStylesFromFrame() const
1838 ScrollbarStyles result
;
1840 nsPresContext
* presContext
= mOuter
->PresContext();
1841 if (!presContext
->IsDynamic() &&
1842 !(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
1843 return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN
, NS_STYLE_OVERFLOW_HIDDEN
);
1847 result
= presContext
->GetViewportOverflowOverride();
1849 nsCOMPtr
<nsISupports
> container
= presContext
->GetContainer();
1850 nsCOMPtr
<nsIScrollable
> scrollable
= do_QueryInterface(container
);
1852 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_X
,
1853 result
.mHorizontal
);
1854 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_Y
,
1858 const nsStyleDisplay
*disp
= mOuter
->GetStyleDisplay();
1859 result
.mHorizontal
= disp
->mOverflowX
;
1860 result
.mVertical
= disp
->mOverflowY
;
1863 NS_ASSERTION(result
.mHorizontal
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1864 result
.mHorizontal
!= NS_STYLE_OVERFLOW_CLIP
&&
1865 result
.mVertical
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1866 result
.mVertical
!= NS_STYLE_OVERFLOW_CLIP
,
1867 "scrollbars should not have been created");
1872 AlignToDevPixelRoundingToZero(nscoord aVal
, PRInt32 aAppUnitsPerDevPixel
)
1874 return (aVal
/aAppUnitsPerDevPixel
)*aAppUnitsPerDevPixel
;
1878 nsGfxScrollFrameInner::GetScrollRange() const
1880 nsRect range
= GetScrolledRect();
1881 range
.width
-= mScrollPort
.width
;
1882 range
.height
-= mScrollPort
.height
;
1884 nsPresContext
* presContext
= mOuter
->PresContext();
1885 PRInt32 appUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
1887 AlignToDevPixelRoundingToZero(range
.XMost(), appUnitsPerDevPixel
) - range
.x
;
1889 AlignToDevPixelRoundingToZero(range
.YMost(), appUnitsPerDevPixel
) - range
.y
;
1890 range
.x
= AlignToDevPixelRoundingToZero(range
.x
, appUnitsPerDevPixel
);
1891 range
.y
= AlignToDevPixelRoundingToZero(range
.y
, appUnitsPerDevPixel
);
1896 AdjustForWholeDelta(PRInt32 aDelta
, nscoord
* aCoord
)
1899 *aCoord
= nscoord_MIN
;
1900 } else if (aDelta
> 0) {
1901 *aCoord
= nscoord_MAX
;
1906 nsGfxScrollFrameInner::ScrollBy(nsIntPoint aDelta
,
1907 nsIScrollableFrame::ScrollUnit aUnit
,
1908 nsIScrollableFrame::ScrollMode aMode
,
1909 nsIntPoint
* aOverflow
)
1911 nsSize deltaMultiplier
;
1913 case nsIScrollableFrame::DEVICE_PIXELS
: {
1914 nscoord appUnitsPerDevPixel
=
1915 mOuter
->PresContext()->AppUnitsPerDevPixel();
1916 deltaMultiplier
= nsSize(appUnitsPerDevPixel
, appUnitsPerDevPixel
);
1919 case nsIScrollableFrame::LINES
: {
1920 deltaMultiplier
= GetLineScrollAmount();
1923 case nsIScrollableFrame::PAGES
: {
1924 deltaMultiplier
= GetPageScrollAmount();
1927 case nsIScrollableFrame::WHOLE
: {
1928 nsPoint pos
= GetScrollPosition();
1929 AdjustForWholeDelta(aDelta
.x
, &pos
.x
);
1930 AdjustForWholeDelta(aDelta
.y
, &pos
.y
);
1931 ScrollTo(pos
, aMode
);
1933 *aOverflow
= nsIntPoint(0, 0);
1938 NS_ERROR("Invalid scroll mode");
1942 nsPoint newPos
= mDestination
+
1943 nsPoint(aDelta
.x
*deltaMultiplier
.width
, aDelta
.y
*deltaMultiplier
.height
);
1944 ScrollTo(newPos
, aMode
);
1947 nsPoint clampAmount
= mDestination
- newPos
;
1948 float appUnitsPerDevPixel
= mOuter
->PresContext()->AppUnitsPerDevPixel();
1949 *aOverflow
= nsIntPoint(
1950 NSAppUnitsToIntPixels(PR_ABS(clampAmount
.x
), appUnitsPerDevPixel
),
1951 NSAppUnitsToIntPixels(PR_ABS(clampAmount
.y
), appUnitsPerDevPixel
));
1956 nsGfxScrollFrameInner::GetLineScrollAmount() const
1958 const nsStyleFont
* font
= mOuter
->GetStyleFont();
1959 const nsFont
& f
= font
->mFont
;
1960 nsCOMPtr
<nsIFontMetrics
> fm
= mOuter
->PresContext()->GetMetricsFor(f
);
1961 NS_ASSERTION(fm
, "FontMetrics is null, assuming fontHeight == 1 appunit");
1962 nscoord fontHeight
= 1;
1964 fm
->GetHeight(fontHeight
);
1967 return nsSize(fontHeight
, fontHeight
);
1971 nsGfxScrollFrameInner::GetPageScrollAmount() const
1973 nsSize lineScrollAmount
= GetLineScrollAmount();
1974 // The page increment is the size of the page, minus the smaller of
1975 // 10% of the size or 2 lines.
1977 mScrollPort
.width
- NS_MIN(mScrollPort
.width
/10, 2*lineScrollAmount
.width
),
1978 mScrollPort
.height
- NS_MIN(mScrollPort
.height
/10, 2*lineScrollAmount
.height
));
1982 * this code is resposible for restoring the scroll position back to some
1983 * saved position. if the user has not moved the scroll position manually
1984 * we keep scrolling down until we get to our original position. keep in
1985 * mind that content could incrementally be coming in. we only want to stop
1986 * when we reach our new position.
1989 nsGfxScrollFrameInner::ScrollToRestoredPosition()
1991 if (mRestorePos
.y
== -1 || mLastPos
.x
== -1 || mLastPos
.y
== -1) {
1994 // make sure our scroll position did not change for where we last put
1995 // it. if it does then the user must have moved it, and we no longer
1997 nsPoint scrollPos
= GetScrollPosition();
1999 // if we didn't move, we still need to restore
2000 if (scrollPos
== mLastPos
) {
2001 // if our desired position is different to the scroll position, scroll.
2002 // remember that we could be incrementally loading so we may enter
2003 // and scroll many times.
2004 if (mRestorePos
!= scrollPos
) {
2005 ScrollTo(mRestorePos
, nsIScrollableFrame::INSTANT
);
2006 // Re-get the scroll position, it might not be exactly equal to
2007 // mRestorePos due to rounding and clamping.
2008 mLastPos
= GetScrollPosition();
2010 // if we reached the position then stop
2016 // user moved the position, so we won't need to restore
2023 nsGfxScrollFrameInner::FireScrollPortEvent()
2025 mAsyncScrollPortEvent
.Forget();
2027 // Keep this in sync with PostOverflowEvent().
2028 nsSize scrollportSize
= mScrollPort
.Size();
2029 nsSize childSize
= GetScrolledRect().Size();
2031 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
2032 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
2034 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
2035 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
2037 if (!vertChanged
&& !horizChanged
) {
2041 // If both either overflowed or underflowed then we dispatch only one
2043 PRBool both
= vertChanged
&& horizChanged
&&
2044 newVerticalOverflow
== newHorizontalOverflow
;
2045 nsScrollPortEvent::orientType orient
;
2047 orient
= nsScrollPortEvent::both
;
2048 mHorizontalOverflow
= newHorizontalOverflow
;
2049 mVerticalOverflow
= newVerticalOverflow
;
2051 else if (vertChanged
) {
2052 orient
= nsScrollPortEvent::vertical
;
2053 mVerticalOverflow
= newVerticalOverflow
;
2055 // We need to dispatch a separate horizontal DOM event. Do that the next
2056 // time around since dispatching the vertical DOM event might destroy
2058 PostOverflowEvent();
2062 orient
= nsScrollPortEvent::horizontal
;
2063 mHorizontalOverflow
= newHorizontalOverflow
;
2066 nsScrollPortEvent
event(PR_TRUE
,
2067 (orient
== nsScrollPortEvent::horizontal
?
2068 mHorizontalOverflow
: mVerticalOverflow
) ?
2069 NS_SCROLLPORT_OVERFLOW
: NS_SCROLLPORT_UNDERFLOW
,
2071 event
.orient
= orient
;
2072 return nsEventDispatcher::Dispatch(mOuter
->GetContent(),
2073 mOuter
->PresContext(), &event
);
2077 nsGfxScrollFrameInner::ReloadChildFrames()
2079 mScrolledFrame
= nsnull
;
2080 mHScrollbarBox
= nsnull
;
2081 mVScrollbarBox
= nsnull
;
2082 mScrollCornerBox
= nsnull
;
2084 nsIFrame
* frame
= mOuter
->GetFirstChild(nsnull
);
2086 nsIContent
* content
= frame
->GetContent();
2087 if (content
== mOuter
->GetContent()) {
2088 NS_ASSERTION(!mScrolledFrame
, "Already found the scrolled frame");
2089 mScrolledFrame
= frame
;
2092 content
->GetAttr(kNameSpaceID_None
, nsGkAtoms::orient
, value
);
2093 if (!value
.IsEmpty()) {
2094 // probably a scrollbar then
2095 if (value
.LowerCaseEqualsLiteral("horizontal")) {
2096 NS_ASSERTION(!mHScrollbarBox
, "Found multiple horizontal scrollbars?");
2097 mHScrollbarBox
= frame
;
2099 NS_ASSERTION(!mVScrollbarBox
, "Found multiple vertical scrollbars?");
2100 mVScrollbarBox
= frame
;
2103 // probably a scrollcorner
2104 NS_ASSERTION(!mScrollCornerBox
, "Found multiple scrollcorners");
2105 mScrollCornerBox
= frame
;
2109 frame
= frame
->GetNextSibling();
2114 nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
2116 nsPresContext
* presContext
= mOuter
->PresContext();
2117 nsIFrame
* parent
= mOuter
->GetParent();
2119 // Don't create scrollbars if we're printing/print previewing
2120 // Get rid of this code when printing moves to its own presentation
2121 if (!presContext
->IsDynamic()) {
2122 // allow scrollbars if this is the child of the viewport, because
2123 // we must be the scrollbars for the print preview window
2124 if (!(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
2125 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
2130 // Check if the frame is resizable.
2131 PRInt8 resizeStyle
= mOuter
->GetStyleDisplay()->mResize
;
2132 PRBool isResizable
= resizeStyle
!= NS_STYLE_RESIZE_NONE
;
2134 nsIScrollableFrame
*scrollable
= do_QueryFrame(mOuter
);
2136 // If we're the scrollframe for the root, then we want to construct
2137 // our scrollbar frames no matter what. That way later dynamic
2138 // changes to propagated overflow styles will show or hide
2139 // scrollbars on the viewport without requiring frame reconstruction
2140 // of the viewport (good!).
2141 PRBool canHaveHorizontal
;
2142 PRBool canHaveVertical
;
2144 ScrollbarStyles styles
= scrollable
->GetScrollbarStyles();
2145 canHaveHorizontal
= styles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
;
2146 canHaveVertical
= styles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
;
2147 if (!canHaveHorizontal
&& !canHaveVertical
&& !isResizable
) {
2152 canHaveHorizontal
= PR_TRUE
;
2153 canHaveVertical
= PR_TRUE
;
2156 // The anonymous <div> used by <inputs> never gets scrollbars.
2157 nsITextControlFrame
* textFrame
= do_QueryFrame(parent
);
2159 // Make sure we are not a text area.
2160 nsCOMPtr
<nsIDOMHTMLTextAreaElement
> textAreaElement(do_QueryInterface(parent
->GetContent()));
2161 if (!textAreaElement
) {
2162 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
2169 nsNodeInfoManager
*nodeInfoManager
=
2170 presContext
->Document()->NodeInfoManager();
2171 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
2172 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollbar
, nsnull
,
2174 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
2176 if (canHaveHorizontal
) {
2177 nsCOMPtr
<nsINodeInfo
> ni
= nodeInfo
;
2178 rv
= NS_NewElement(getter_AddRefs(mHScrollbarContent
),
2179 kNameSpaceID_XUL
, ni
.forget(), PR_FALSE
);
2180 NS_ENSURE_SUCCESS(rv
, rv
);
2181 mHScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
2182 NS_LITERAL_STRING("horizontal"), PR_FALSE
);
2183 mHScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::clickthrough
,
2184 NS_LITERAL_STRING("always"), PR_FALSE
);
2185 if (!aElements
.AppendElement(mHScrollbarContent
))
2186 return NS_ERROR_OUT_OF_MEMORY
;
2189 if (canHaveVertical
) {
2190 nsCOMPtr
<nsINodeInfo
> ni
= nodeInfo
;
2191 rv
= NS_NewElement(getter_AddRefs(mVScrollbarContent
),
2192 kNameSpaceID_XUL
, ni
.forget(), PR_FALSE
);
2193 NS_ENSURE_SUCCESS(rv
, rv
);
2194 mVScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
2195 NS_LITERAL_STRING("vertical"), PR_FALSE
);
2196 mVScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::clickthrough
,
2197 NS_LITERAL_STRING("always"), PR_FALSE
);
2198 if (!aElements
.AppendElement(mVScrollbarContent
))
2199 return NS_ERROR_OUT_OF_MEMORY
;
2203 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
2204 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::resizer
, nsnull
,
2206 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
2208 rv
= NS_NewXULElement(getter_AddRefs(mScrollCornerContent
),
2210 NS_ENSURE_SUCCESS(rv
, rv
);
2213 switch (resizeStyle
) {
2214 case NS_STYLE_RESIZE_HORIZONTAL
:
2215 if (IsScrollbarOnRight()) {
2216 dir
.AssignLiteral("right");
2219 dir
.AssignLiteral("left");
2222 case NS_STYLE_RESIZE_VERTICAL
:
2223 dir
.AssignLiteral("bottom");
2225 case NS_STYLE_RESIZE_BOTH
:
2226 dir
.AssignLiteral("bottomend");
2229 NS_WARNING("only resizable types should have resizers");
2231 mScrollCornerContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, dir
, PR_FALSE
);
2232 mScrollCornerContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::element
,
2233 NS_LITERAL_STRING("_parent"), PR_FALSE
);
2234 mScrollCornerContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::clickthrough
,
2235 NS_LITERAL_STRING("always"), PR_FALSE
);
2237 if (!aElements
.AppendElement(mScrollCornerContent
))
2238 return NS_ERROR_OUT_OF_MEMORY
;
2240 else if (canHaveHorizontal
&& canHaveVertical
) {
2241 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollcorner
, nsnull
,
2243 rv
= NS_NewElement(getter_AddRefs(mScrollCornerContent
),
2244 kNameSpaceID_XUL
, nodeInfo
.forget(), PR_FALSE
);
2245 NS_ENSURE_SUCCESS(rv
, rv
);
2246 if (!aElements
.AppendElement(mScrollCornerContent
))
2247 return NS_ERROR_OUT_OF_MEMORY
;
2254 nsGfxScrollFrameInner::AppendAnonymousContentTo(nsBaseContentList
& aElements
)
2256 aElements
.MaybeAppendElement(mHScrollbarContent
);
2257 aElements
.MaybeAppendElement(mVScrollbarContent
);
2258 aElements
.MaybeAppendElement(mScrollCornerContent
);
2262 nsGfxScrollFrameInner::Destroy()
2264 // Unbind any content created in CreateAnonymousContent from the tree
2265 nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent
);
2266 nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent
);
2267 nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent
);
2269 if (mPostedReflowCallback
) {
2270 mOuter
->PresContext()->PresShell()->CancelReflowCallback(this);
2271 mPostedReflowCallback
= PR_FALSE
;
2276 * Called when we want to update the scrollbar position, either because scrolling happened
2277 * or the user moved the scrollbar position and we need to undo that (e.g., when the user
2278 * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
2279 * to its initial position for the start of the smooth sequence).
2282 nsGfxScrollFrameInner::UpdateScrollbarPosition()
2284 mFrameIsUpdatingScrollbar
= PR_TRUE
;
2286 nsPoint pt
= GetScrollPosition();
2287 if (mVScrollbarBox
) {
2288 SetCoordAttribute(mVScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
2289 pt
.y
- GetScrolledRect().y
);
2291 if (mHScrollbarBox
) {
2292 SetCoordAttribute(mHScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
2293 pt
.x
- GetScrolledRect().x
);
2296 mFrameIsUpdatingScrollbar
= PR_FALSE
;
2299 void nsGfxScrollFrameInner::CurPosAttributeChanged(nsIContent
* aContent
)
2301 NS_ASSERTION(aContent
, "aContent must not be null");
2302 NS_ASSERTION((mHScrollbarBox
&& mHScrollbarBox
->GetContent() == aContent
) ||
2303 (mVScrollbarBox
&& mVScrollbarBox
->GetContent() == aContent
),
2304 "unexpected child");
2306 // Attribute changes on the scrollbars happen in one of three ways:
2307 // 1) The scrollbar changed the attribute in response to some user event
2308 // 2) We changed the attribute in response to a ScrollPositionDidChange
2309 // callback from the scrolling view
2310 // 3) We changed the attribute to adjust the scrollbars for the start
2311 // of a smooth scroll operation
2313 // In cases 2 and 3 we do not need to scroll because we're just
2314 // updating our scrollbar.
2315 if (mFrameIsUpdatingScrollbar
)
2318 nsRect scrolledRect
= GetScrolledRect();
2320 nscoord x
= GetCoordAttribute(mHScrollbarBox
, nsGkAtoms::curpos
,
2323 nscoord y
= GetCoordAttribute(mVScrollbarBox
, nsGkAtoms::curpos
,
2327 PRBool isSmooth
= aContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::smooth
);
2329 // Make sure an attribute-setting callback occurs even if the view
2330 // didn't actually move yet. We need to make sure other listeners
2331 // see that the scroll position is not (yet) what they thought it
2333 UpdateScrollbarPosition();
2335 ScrollTo(nsPoint(x
, y
),
2336 isSmooth
? nsIScrollableFrame::SMOOTH
: nsIScrollableFrame::INSTANT
);
2339 /* ============= Scroll events ========== */
2342 nsGfxScrollFrameInner::ScrollEvent::Run()
2345 mInner
->FireScrollEvent();
2350 nsGfxScrollFrameInner::FireScrollEvent()
2352 mScrollEvent
.Forget();
2354 nsScrollbarEvent
event(PR_TRUE
, NS_SCROLL_EVENT
, nsnull
);
2355 nsEventStatus status
= nsEventStatus_eIgnore
;
2356 nsIContent
* content
= mOuter
->GetContent();
2357 nsPresContext
* prescontext
= mOuter
->PresContext();
2358 // Fire viewport scroll events at the document (where they
2359 // will bubble to the window)
2361 nsIDocument
* doc
= content
->GetCurrentDoc();
2363 nsEventDispatcher::Dispatch(doc
, prescontext
, &event
, nsnull
, &status
);
2366 // scroll events fired at elements don't bubble (although scroll events
2367 // fired at documents do, to the window)
2368 event
.flags
|= NS_EVENT_FLAG_CANT_BUBBLE
;
2369 nsEventDispatcher::Dispatch(content
, prescontext
, &event
, nsnull
, &status
);
2374 nsGfxScrollFrameInner::PostScrollEvent()
2376 if (mScrollEvent
.IsPending())
2379 nsRefPtr
<ScrollEvent
> ev
= new ScrollEvent(this);
2380 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
2381 NS_WARNING("failed to dispatch ScrollEvent");
2388 nsGfxScrollFrameInner::AsyncScrollPortEvent::Run()
2391 mInner
->mOuter
->PresContext()->GetPresShell()->
2392 FlushPendingNotifications(Flush_InterruptibleLayout
);
2394 return mInner
? mInner
->FireScrollPortEvent() : NS_OK
;
2398 nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState
& aState
, PRBool aOnTop
)
2400 if (!mInner
.mHScrollbarBox
)
2403 return AddRemoveScrollbar(aState
, aOnTop
, PR_TRUE
, PR_TRUE
);
2407 nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState
& aState
, PRBool aOnRight
)
2409 if (!mInner
.mVScrollbarBox
)
2412 return AddRemoveScrollbar(aState
, aOnRight
, PR_FALSE
, PR_TRUE
);
2416 nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState
& aState
, PRBool aOnTop
)
2418 // removing a scrollbar should always fit
2422 AddRemoveScrollbar(aState
, aOnTop
, PR_TRUE
, PR_FALSE
);
2423 NS_ASSERTION(result
, "Removing horizontal scrollbar failed to fit??");
2427 nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState
& aState
, PRBool aOnRight
)
2429 // removing a scrollbar should always fit
2433 AddRemoveScrollbar(aState
, aOnRight
, PR_FALSE
, PR_FALSE
);
2434 NS_ASSERTION(result
, "Removing vertical scrollbar failed to fit??");
2438 nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState
& aState
,
2439 PRBool aOnTop
, PRBool aHorizontal
, PRBool aAdd
)
2442 if (mInner
.mNeverHasHorizontalScrollbar
|| !mInner
.mHScrollbarBox
)
2445 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
2446 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
2448 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, aAdd
);
2450 PRBool hasHorizontalScrollbar
;
2451 PRBool fit
= AddRemoveScrollbar(hasHorizontalScrollbar
,
2452 mInner
.mScrollPort
.y
,
2453 mInner
.mScrollPort
.height
,
2454 hSize
.height
, aOnTop
, aAdd
);
2455 mInner
.mHasHorizontalScrollbar
= hasHorizontalScrollbar
; // because mHasHorizontalScrollbar is a PRPackedBool
2457 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, !aAdd
);
2461 if (mInner
.mNeverHasVerticalScrollbar
|| !mInner
.mVScrollbarBox
)
2464 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
2465 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
2467 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, aAdd
);
2469 PRBool hasVerticalScrollbar
;
2470 PRBool fit
= AddRemoveScrollbar(hasVerticalScrollbar
,
2471 mInner
.mScrollPort
.x
,
2472 mInner
.mScrollPort
.width
,
2473 vSize
.width
, aOnTop
, aAdd
);
2474 mInner
.mHasVerticalScrollbar
= hasVerticalScrollbar
; // because mHasVerticalScrollbar is a PRPackedBool
2476 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, !aAdd
);
2483 nsXULScrollFrame::AddRemoveScrollbar(PRBool
& aHasScrollbar
, nscoord
& aXY
,
2484 nscoord
& aSize
, nscoord aSbSize
,
2485 PRBool aRightOrBottom
, PRBool aAdd
)
2487 nscoord size
= aSize
;
2490 if (size
!= NS_INTRINSICSIZE
) {
2493 if (!aRightOrBottom
&& size
>= 0)
2497 if (!aRightOrBottom
)
2502 // not enough room? Yes? Return true.
2504 aHasScrollbar
= aAdd
;
2510 aHasScrollbar
= PR_FALSE
;
2515 nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState
& aState
,
2516 const nsPoint
& aScrollPosition
)
2518 PRUint32 oldflags
= aState
.LayoutFlags();
2519 nsRect childRect
= nsRect(mInner
.mScrollPort
.TopLeft() - aScrollPosition
,
2520 mInner
.mScrollPort
.Size());
2521 PRInt32 flags
= NS_FRAME_NO_MOVE_VIEW
;
2523 nsRect originalRect
= mInner
.mScrolledFrame
->GetRect();
2524 nsRect originalOverflow
= mInner
.mScrolledFrame
->GetOverflowRect();
2526 nsSize minSize
= mInner
.mScrolledFrame
->GetMinSize(aState
);
2528 if (minSize
.height
> childRect
.height
)
2529 childRect
.height
= minSize
.height
;
2531 if (minSize
.width
> childRect
.width
)
2532 childRect
.width
= minSize
.width
;
2534 aState
.SetLayoutFlags(flags
);
2535 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2536 mInner
.mScrolledFrame
->Layout(aState
);
2538 childRect
= mInner
.mScrolledFrame
->GetRect();
2540 if (childRect
.width
< mInner
.mScrollPort
.width
||
2541 childRect
.height
< mInner
.mScrollPort
.height
)
2543 childRect
.width
= NS_MAX(childRect
.width
, mInner
.mScrollPort
.width
);
2544 childRect
.height
= NS_MAX(childRect
.height
, mInner
.mScrollPort
.height
);
2546 // remove overflow area when we update the bounds,
2547 // because we've already accounted for it
2548 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2549 mInner
.mScrolledFrame
->ClearOverflowRect();
2552 nsRect finalRect
= mInner
.mScrolledFrame
->GetRect();
2553 nsRect finalOverflow
= mInner
.mScrolledFrame
->GetOverflowRect();
2554 // The position of the scrolled frame shouldn't change, but if it does or
2555 // the position of the overflow rect changes just invalidate both the old
2556 // and new overflow rect.
2557 if (originalRect
.TopLeft() != finalRect
.TopLeft() ||
2558 originalOverflow
.TopLeft() != finalOverflow
.TopLeft())
2560 // The old overflow rect needs to be adjusted if the frame's position
2562 mInner
.mScrolledFrame
->Invalidate(
2563 originalOverflow
+ originalRect
.TopLeft() - finalRect
.TopLeft());
2564 mInner
.mScrolledFrame
->Invalidate(finalOverflow
);
2565 } else if (!originalOverflow
.IsExactEqual(finalOverflow
)) {
2566 // If the overflow rect changed then invalidate the difference between the
2567 // old and new overflow rects.
2568 mInner
.mScrolledFrame
->CheckInvalidateSizeChange(
2569 originalRect
, originalOverflow
, finalRect
.Size());
2570 mInner
.mScrolledFrame
->InvalidateRectDifference(
2571 originalOverflow
, finalOverflow
);
2574 aState
.SetLayoutFlags(oldflags
);
2578 void nsGfxScrollFrameInner::PostOverflowEvent()
2580 if (mAsyncScrollPortEvent
.IsPending())
2583 // Keep this in sync with FireScrollPortEvent().
2584 nsSize scrollportSize
= mScrollPort
.Size();
2585 nsSize childSize
= GetScrolledRect().Size();
2587 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
2588 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
2590 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
2591 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
2593 if (!vertChanged
&& !horizChanged
) {
2597 nsRefPtr
<AsyncScrollPortEvent
> ev
= new AsyncScrollPortEvent(this);
2598 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev
)))
2599 mAsyncScrollPortEvent
= ev
;
2603 nsGfxScrollFrameInner::IsLTR() const
2605 //TODO make bidi code set these from preferences
2607 nsIFrame
*frame
= mOuter
;
2608 // XXX This is a bit on the slow side.
2610 // If we're the root scrollframe, we need the root element's style data.
2611 nsPresContext
*presContext
= mOuter
->PresContext();
2612 nsIDocument
*document
= presContext
->Document();
2613 Element
*root
= document
->GetRootElement();
2615 // But for HTML and XHTML we want the body element.
2616 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
2618 Element
*bodyElement
= document
->GetBodyElement();
2620 root
= bodyElement
; // we can trust the document to hold on to it
2624 nsIFrame
*rootsFrame
= root
->GetPrimaryFrame();
2630 return frame
->GetStyleVisibility()->mDirection
!= NS_STYLE_DIRECTION_RTL
;
2634 nsGfxScrollFrameInner::IsScrollbarOnRight() const
2636 nsPresContext
*presContext
= mOuter
->PresContext();
2638 // The position of the scrollbar in top-level windows depends on the pref
2639 // layout.scrollbar.side. For non-top-level elements, it depends only on the
2640 // directionaliy of the element (equivalent to a value of "1" for the pref).
2643 switch (presContext
->GetCachedIntPref(kPresContext_ScrollbarSide
)) {
2645 case 0: // UI directionality
2646 return presContext
->GetCachedIntPref(kPresContext_BidiDirection
)
2647 == IBMBIDI_TEXTDIRECTION_LTR
;
2648 case 1: // Document / content directionality
2650 case 2: // Always right
2652 case 3: // Always left
2658 * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
2659 * cause any of the scrollbars to need to be reflowed.
2662 nsXULScrollFrame::Layout(nsBoxLayoutState
& aState
)
2664 PRBool scrollbarRight
= mInner
.IsScrollbarOnRight();
2665 PRBool scrollbarBottom
= PR_TRUE
;
2667 // get the content rect
2668 nsRect
clientRect(0,0,0,0);
2669 GetClientRect(clientRect
);
2671 nsRect oldScrollAreaBounds
= mInner
.mScrollPort
;
2672 nsPoint oldScrollPosition
= mInner
.GetScrollPosition();
2674 // the scroll area size starts off as big as our content area
2675 mInner
.mScrollPort
= clientRect
;
2678 Our basic strategy here is to first try laying out the content with
2679 the scrollbars in their current state. We're hoping that that will
2680 just "work"; the content will overflow wherever there's a scrollbar
2681 already visible. If that does work, then there's no need to lay out
2682 the scrollarea. Otherwise we fix up the scrollbars; first we add a
2683 vertical one to scroll the content if necessary, or remove it if
2684 it's not needed. Then we reflow the content if the scrollbar
2685 changed. Then we add a horizontal scrollbar if necessary (or
2686 remove if not needed), and if that changed, we reflow the content
2687 again. At this point, any scrollbars that are needed to scroll the
2688 content have been added.
2690 In the second phase we check to see if any scrollbars are too small
2691 to display, and if so, we remove them. We check the horizontal
2692 scrollbar first; removing it might make room for the vertical
2693 scrollbar, and if we have room for just one scrollbar we'll save
2696 Finally we position and size the scrollbars and scrollcorner (the
2697 square that is needed in the corner of the window when two
2698 scrollbars are visible), and reflow any fixed position views
2699 (if we're the viewport and we added or removed a scrollbar).
2702 ScrollbarStyles styles
= GetScrollbarStyles();
2704 // Look at our style do we always have vertical or horizontal scrollbars?
2705 if (styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
)
2706 mInner
.mHasHorizontalScrollbar
= PR_TRUE
;
2707 if (styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
)
2708 mInner
.mHasVerticalScrollbar
= PR_TRUE
;
2710 if (mInner
.mHasHorizontalScrollbar
)
2711 AddHorizontalScrollbar(aState
, scrollbarBottom
);
2713 if (mInner
.mHasVerticalScrollbar
)
2714 AddVerticalScrollbar(aState
, scrollbarRight
);
2716 // layout our the scroll area
2717 LayoutScrollArea(aState
, oldScrollPosition
);
2719 // now look at the content area and see if we need scrollbars or not
2720 PRBool needsLayout
= PR_FALSE
;
2722 // if we have 'auto' scrollbars look at the vertical case
2723 if (styles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
) {
2724 // These are only good until the call to LayoutScrollArea.
2725 nsRect scrolledRect
= mInner
.GetScrolledRect();
2726 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2728 // There are two cases to consider
2729 if (scrolledContentSize
.height
<= mInner
.mScrollPort
.height
2730 || styles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
) {
2731 if (mInner
.mHasVerticalScrollbar
) {
2732 // We left room for the vertical scrollbar, but it's not needed;
2734 RemoveVerticalScrollbar(aState
, scrollbarRight
);
2735 needsLayout
= PR_TRUE
;
2738 if (!mInner
.mHasVerticalScrollbar
) {
2739 // We didn't leave room for the vertical scrollbar, but it turns
2741 if (AddVerticalScrollbar(aState
, scrollbarRight
))
2742 needsLayout
= PR_TRUE
;
2746 // ok layout at the right size
2748 nsBoxLayoutState
resizeState(aState
);
2749 LayoutScrollArea(resizeState
, oldScrollPosition
);
2750 needsLayout
= PR_FALSE
;
2755 // if scrollbars are auto look at the horizontal case
2756 if (styles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
)
2758 // These are only good until the call to LayoutScrollArea.
2759 nsRect scrolledRect
= mInner
.GetScrolledRect();
2760 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2762 // if the child is wider that the scroll area
2763 // and we don't have a scrollbar add one.
2764 if (scrolledContentSize
.width
> mInner
.mScrollPort
.width
2765 && styles
.mHorizontal
== NS_STYLE_OVERFLOW_AUTO
) {
2767 if (!mInner
.mHasHorizontalScrollbar
) {
2769 if (AddHorizontalScrollbar(aState
, scrollbarBottom
))
2770 needsLayout
= PR_TRUE
;
2772 // if we added a horizontal scrollbar and we did not have a vertical
2773 // there is a chance that by adding the horizontal scrollbar we will
2774 // suddenly need a vertical scrollbar. Is a special case but its
2776 //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
2777 // printf("****Gfx Scrollbar Special case hit!!*****\n");
2781 // if the area is smaller or equal to and we have a scrollbar then
2783 if (mInner
.mHasHorizontalScrollbar
) {
2784 RemoveHorizontalScrollbar(aState
, scrollbarBottom
);
2785 needsLayout
= PR_TRUE
;
2790 // we only need to set the rect. The inner child stays the same size.
2792 nsBoxLayoutState
resizeState(aState
);
2793 LayoutScrollArea(resizeState
, oldScrollPosition
);
2794 needsLayout
= PR_FALSE
;
2797 // get the preferred size of the scrollbars
2798 nsSize
hMinSize(0, 0);
2799 if (mInner
.mHScrollbarBox
&& mInner
.mHasHorizontalScrollbar
) {
2800 GetScrollbarMetrics(aState
, mInner
.mHScrollbarBox
, &hMinSize
, nsnull
, PR_FALSE
);
2802 nsSize
vMinSize(0, 0);
2803 if (mInner
.mVScrollbarBox
&& mInner
.mHasVerticalScrollbar
) {
2804 GetScrollbarMetrics(aState
, mInner
.mVScrollbarBox
, &vMinSize
, nsnull
, PR_TRUE
);
2807 // Disable scrollbars that are too small
2808 // Disable horizontal scrollbar first. If we have to disable only one
2809 // scrollbar, we'd rather keep the vertical scrollbar.
2810 // Note that we always give horizontal scrollbars their preferred height,
2811 // never their min-height. So check that there's room for the preferred height.
2812 if (mInner
.mHasHorizontalScrollbar
&&
2813 (hMinSize
.width
> clientRect
.width
- vMinSize
.width
2814 || hMinSize
.height
> clientRect
.height
)) {
2815 RemoveHorizontalScrollbar(aState
, scrollbarBottom
);
2816 needsLayout
= PR_TRUE
;
2818 // Now disable vertical scrollbar if necessary
2819 if (mInner
.mHasVerticalScrollbar
&&
2820 (vMinSize
.height
> clientRect
.height
- hMinSize
.height
2821 || vMinSize
.width
> clientRect
.width
)) {
2822 RemoveVerticalScrollbar(aState
, scrollbarRight
);
2823 needsLayout
= PR_TRUE
;
2826 // we only need to set the rect. The inner child stays the same size.
2828 nsBoxLayoutState
resizeState(aState
);
2829 LayoutScrollArea(resizeState
, oldScrollPosition
);
2832 if (!mInner
.mSupppressScrollbarUpdate
) {
2833 mInner
.LayoutScrollbars(aState
, clientRect
, oldScrollAreaBounds
);
2835 if (!mInner
.mPostedReflowCallback
) {
2836 // Make sure we'll try scrolling to restored position
2837 PresContext()->PresShell()->PostReflowCallback(&mInner
);
2838 mInner
.mPostedReflowCallback
= PR_TRUE
;
2840 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
2841 mInner
.mHadNonInitialReflow
= PR_TRUE
;
2844 mInner
.PostOverflowEvent();
2849 nsGfxScrollFrameInner::FinishReflowForScrollbar(nsIContent
* aContent
,
2850 nscoord aMinXY
, nscoord aMaxXY
,
2852 nscoord aPageIncrement
,
2855 // Scrollbars assume zero is the minimum position, so translate for them.
2856 SetCoordAttribute(aContent
, nsGkAtoms::curpos
, aCurPosXY
- aMinXY
);
2857 SetScrollbarEnabled(aContent
, aMaxXY
- aMinXY
);
2858 SetCoordAttribute(aContent
, nsGkAtoms::maxpos
, aMaxXY
- aMinXY
);
2859 SetCoordAttribute(aContent
, nsGkAtoms::pageincrement
, aPageIncrement
);
2860 SetCoordAttribute(aContent
, nsGkAtoms::increment
, aIncrement
);
2864 nsGfxScrollFrameInner::ReflowFinished()
2866 mPostedReflowCallback
= PR_FALSE
;
2868 ScrollToRestoredPosition();
2870 // Clamp scroll position
2871 ScrollToImpl(GetScrollPosition());
2873 if (NS_SUBTREE_DIRTY(mOuter
) || !mUpdateScrollbarAttributes
)
2876 mUpdateScrollbarAttributes
= PR_FALSE
;
2878 // Update scrollbar attributes.
2879 nsPresContext
* presContext
= mOuter
->PresContext();
2881 if (mMayHaveDirtyFixedChildren
) {
2882 mMayHaveDirtyFixedChildren
= PR_FALSE
;
2883 nsIFrame
* parentFrame
= mOuter
->GetParent();
2884 for (nsIFrame
* fixedChild
=
2885 parentFrame
->GetFirstChild(nsGkAtoms::fixedList
);
2886 fixedChild
; fixedChild
= fixedChild
->GetNextSibling()) {
2887 // force a reflow of the fixed child
2888 presContext
->PresShell()->
2889 FrameNeedsReflow(fixedChild
, nsIPresShell::eResize
,
2890 NS_FRAME_HAS_DIRTY_CHILDREN
);
2894 nsRect scrolledContentRect
= GetScrolledRect();
2895 nscoord minX
= scrolledContentRect
.x
;
2896 nscoord maxX
= scrolledContentRect
.XMost() - mScrollPort
.width
;
2897 nscoord minY
= scrolledContentRect
.y
;
2898 nscoord maxY
= scrolledContentRect
.YMost() - mScrollPort
.height
;
2900 // Suppress handling of the curpos attribute changes we make here.
2901 NS_ASSERTION(!mFrameIsUpdatingScrollbar
, "We shouldn't be reentering here");
2902 mFrameIsUpdatingScrollbar
= PR_TRUE
;
2904 nsCOMPtr
<nsIContent
> vScroll
=
2905 mVScrollbarBox
? mVScrollbarBox
->GetContent() : nsnull
;
2906 nsCOMPtr
<nsIContent
> hScroll
=
2907 mHScrollbarBox
? mHScrollbarBox
->GetContent() : nsnull
;
2909 // Note, in some cases mOuter may get deleted while finishing reflow
2911 if (vScroll
|| hScroll
) {
2912 nsWeakFrame
weakFrame(mOuter
);
2913 nsPoint scrollPos
= GetScrollPosition();
2914 // XXX shouldn't we use GetPageScrollAmount/GetLineScrollAmount here?
2916 nscoord fontHeight
= GetLineScrollAmount().height
;
2917 // We normally use (scrollArea.height - fontHeight) for height
2918 // of page scrolling. However, it is too small when
2919 // fontHeight is very large. (If fontHeight is larger than
2920 // scrollArea.height, direction of scrolling will be opposite).
2921 // To avoid it, we use (float(scrollArea.height) * 0.8) as
2922 // lower bound value of height of page scrolling. (bug 383267)
2923 nscoord pageincrement
= nscoord(mScrollPort
.height
- fontHeight
);
2924 nscoord pageincrementMin
= nscoord(float(mScrollPort
.height
) * 0.8);
2925 FinishReflowForScrollbar(vScroll
, minY
, maxY
, scrollPos
.y
,
2926 NS_MAX(pageincrement
,pageincrementMin
),
2930 FinishReflowForScrollbar(hScroll
, minX
, maxX
, scrollPos
.x
,
2931 nscoord(float(mScrollPort
.width
) * 0.8),
2932 nsPresContext::CSSPixelsToAppUnits(10));
2934 NS_ENSURE_TRUE(weakFrame
.IsAlive(), PR_FALSE
);
2937 mFrameIsUpdatingScrollbar
= PR_FALSE
;
2938 // We used to rely on the curpos attribute changes above to scroll the
2939 // view. However, for scrolling to the left of the viewport, we
2940 // rescale the curpos attribute, which means that operations like
2941 // resizing the window while it is scrolled all the way to the left
2942 // hold the curpos attribute constant at 0 while still requiring
2943 // scrolling. So we suppress the effect of the changes above with
2944 // mFrameIsUpdatingScrollbar and call CurPosAttributeChanged here.
2945 // (It actually even works some of the time without this, thanks to
2946 // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
2947 // we hide the scrollbar on a large size change, such as
2949 if (!mHScrollbarBox
&& !mVScrollbarBox
)
2951 CurPosAttributeChanged(mVScrollbarBox
? mVScrollbarBox
->GetContent()
2952 : mHScrollbarBox
->GetContent());
2957 nsGfxScrollFrameInner::ReflowCallbackCanceled()
2959 mPostedReflowCallback
= PR_FALSE
;
2962 static void LayoutAndInvalidate(nsBoxLayoutState
& aState
,
2963 nsIFrame
* aBox
, const nsRect
& aRect
,
2964 PRBool aScrollbarIsBeingHidden
)
2966 // When a child box changes shape of position, the parent
2967 // is responsible for invalidation; the overflow rect must be invalidated
2968 // to make sure to catch any overflow.
2969 // We invalidate the parent (i.e. the scrollframe) directly, because
2970 // invalidates coming from scrollbars are suppressed by nsHTMLScrollFrame when
2971 // mHasVScrollbar/mHasHScrollbar is false, and this is called after those
2972 // flags have been set ... if a scrollbar is being hidden, we still need
2973 // to invalidate the scrollbar area here.
2974 // But we also need to invalidate the scrollbar itself in case it has
2975 // its own layer; we need to ensure that layer is updated.
2976 PRBool rectChanged
= aBox
->GetRect() != aRect
;
2978 if (aScrollbarIsBeingHidden
) {
2979 aBox
->GetParent()->Invalidate(aBox
->GetOverflowRect() + aBox
->GetPosition());
2981 aBox
->InvalidateOverflowRect();
2984 nsBoxFrame::LayoutChildAt(aState
, aBox
, aRect
);
2986 if (aScrollbarIsBeingHidden
) {
2987 aBox
->GetParent()->Invalidate(aBox
->GetOverflowRect() + aBox
->GetPosition());
2989 aBox
->InvalidateOverflowRect();
2995 nsGfxScrollFrameInner::AdjustScrollbarRectForResizer(
2996 nsIFrame
* aFrame
, nsPresContext
* aPresContext
,
2997 nsRect
& aRect
, PRBool aHasResizer
, PRBool aVertical
)
2999 if ((aVertical
? aRect
.width
: aRect
.height
) == 0)
3002 // if a content resizer is present, use its size. Otherwise, check if the
3003 // widget has a resizer.
3005 if (aHasResizer
&& mScrollCornerBox
) {
3006 resizerRect
= mScrollCornerBox
->GetRect();
3010 nsIWidget
* widget
= aFrame
->GetNearestWidget(offset
);
3011 nsIntRect widgetRect
;
3012 if (!widget
|| !widget
->ShowsResizeIndicator(&widgetRect
))
3015 resizerRect
= nsRect(aPresContext
->DevPixelsToAppUnits(widgetRect
.x
) - offset
.x
,
3016 aPresContext
->DevPixelsToAppUnits(widgetRect
.y
) - offset
.y
,
3017 aPresContext
->DevPixelsToAppUnits(widgetRect
.width
),
3018 aPresContext
->DevPixelsToAppUnits(widgetRect
.height
));
3021 if (!resizerRect
.Contains(aRect
.BottomRight() - nsPoint(1, 1)))
3025 aRect
.height
= NS_MAX(0, resizerRect
.y
- aRect
.y
);
3027 aRect
.width
= NS_MAX(0, resizerRect
.x
- aRect
.x
);
3031 nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState
& aState
,
3032 const nsRect
& aContentArea
,
3033 const nsRect
& aOldScrollArea
)
3035 NS_ASSERTION(!mSupppressScrollbarUpdate
,
3036 "This should have been suppressed");
3038 PRBool hasResizer
= HasResizer();
3039 PRBool scrollbarOnLeft
= !IsScrollbarOnRight();
3041 // place the scrollcorner
3042 if (mScrollCornerBox
) {
3043 NS_PRECONDITION(mScrollCornerBox
->IsBoxFrame(), "Must be a box frame!");
3045 // if a resizer is present, get its size
3048 // just assume a default size of 15 pixels
3049 nscoord defaultSize
= nsPresContext::CSSPixelsToAppUnits(15);
3051 mVScrollbarBox
? mVScrollbarBox
->GetMinSize(aState
).width
: defaultSize
;
3052 resizerSize
.height
=
3053 mHScrollbarBox
? mHScrollbarBox
->GetMinSize(aState
).height
: defaultSize
;
3056 resizerSize
= nsSize(0, 0);
3059 nsRect
r(0, 0, 0, 0);
3060 if (aContentArea
.x
!= mScrollPort
.x
|| scrollbarOnLeft
) {
3061 // scrollbar (if any) on left
3062 r
.x
= aContentArea
.x
;
3063 r
.width
= PR_MAX(resizerSize
.width
, mScrollPort
.x
- aContentArea
.x
);
3064 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
3066 // scrollbar (if any) on right
3067 r
.width
= PR_MAX(resizerSize
.width
, aContentArea
.XMost() - mScrollPort
.XMost());
3068 r
.x
= aContentArea
.XMost() - r
.width
;
3069 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
3071 if (aContentArea
.y
!= mScrollPort
.y
) {
3072 NS_ERROR("top scrollbars not supported");
3074 // scrollbar (if any) on bottom
3075 r
.height
= PR_MAX(resizerSize
.height
, aContentArea
.YMost() - mScrollPort
.YMost());
3076 r
.y
= aContentArea
.YMost() - r
.height
;
3077 NS_ASSERTION(r
.height
>= 0, "Scroll area should be inside client rect");
3079 LayoutAndInvalidate(aState
, mScrollCornerBox
, r
, PR_FALSE
);
3082 nsPresContext
* presContext
= mScrolledFrame
->PresContext();
3083 if (mVScrollbarBox
) {
3084 NS_PRECONDITION(mVScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
3085 nsRect
vRect(mScrollPort
);
3086 vRect
.width
= aContentArea
.width
- mScrollPort
.width
;
3087 vRect
.x
= scrollbarOnLeft
? aContentArea
.x
: mScrollPort
.XMost();
3089 mVScrollbarBox
->GetMargin(margin
);
3090 vRect
.Deflate(margin
);
3091 AdjustScrollbarRectForResizer(mOuter
, presContext
, vRect
, hasResizer
, PR_TRUE
);
3092 LayoutAndInvalidate(aState
, mVScrollbarBox
, vRect
, !mHasVerticalScrollbar
);
3095 if (mHScrollbarBox
) {
3096 NS_PRECONDITION(mHScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
3097 nsRect
hRect(mScrollPort
);
3098 hRect
.height
= aContentArea
.height
- mScrollPort
.height
;
3099 hRect
.y
= PR_TRUE
? mScrollPort
.YMost() : aContentArea
.y
;
3101 mHScrollbarBox
->GetMargin(margin
);
3102 hRect
.Deflate(margin
);
3103 AdjustScrollbarRectForResizer(mOuter
, presContext
, hRect
, hasResizer
, PR_FALSE
);
3104 LayoutAndInvalidate(aState
, mHScrollbarBox
, hRect
, !mHasHorizontalScrollbar
);
3107 // may need to update fixed position children of the viewport,
3108 // if the client area changed size because of an incremental
3109 // reflow of a descendant. (If the outer frame is dirty, the fixed
3110 // children will be re-laid out anyway)
3111 if (aOldScrollArea
.Size() != mScrollPort
.Size() &&
3112 !(mOuter
->GetStateBits() & NS_FRAME_IS_DIRTY
) &&
3114 mMayHaveDirtyFixedChildren
= PR_TRUE
;
3117 // post reflow callback to modify scrollbar attributes
3118 mUpdateScrollbarAttributes
= PR_TRUE
;
3119 if (!mPostedReflowCallback
) {
3120 aState
.PresContext()->PresShell()->PostReflowCallback(this);
3121 mPostedReflowCallback
= PR_TRUE
;
3126 nsGfxScrollFrameInner::SetScrollbarEnabled(nsIContent
* aContent
, nscoord aMaxPos
)
3129 aContent
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
, PR_TRUE
);
3131 aContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
,
3132 NS_LITERAL_STRING("true"), PR_TRUE
);
3137 nsGfxScrollFrameInner::SetCoordAttribute(nsIContent
* aContent
, nsIAtom
* aAtom
,
3140 // convert to pixels
3141 aSize
= nsPresContext::AppUnitsToIntCSSPixels(aSize
);
3143 // only set the attribute if it changed.
3145 nsAutoString newValue
;
3146 newValue
.AppendInt(aSize
);
3148 if (aContent
->AttrValueIs(kNameSpaceID_None
, aAtom
, newValue
, eCaseMatters
))
3151 aContent
->SetAttr(kNameSpaceID_None
, aAtom
, newValue
, PR_TRUE
);
3155 nsGfxScrollFrameInner::GetScrolledRect() const
3158 GetScrolledRectInternal(mScrolledFrame
->GetOverflowRect(),
3159 mScrollPort
.Size());
3161 NS_ASSERTION(result
.width
>= mScrollPort
.width
,
3162 "Scrolled rect smaller than scrollport?");
3163 NS_ASSERTION(result
.height
>= mScrollPort
.height
,
3164 "Scrolled rect smaller than scrollport?");
3169 nsGfxScrollFrameInner::GetScrolledRectInternal(const nsRect
& aScrolledFrameOverflowArea
,
3170 const nsSize
& aScrollPortSize
) const
3172 nscoord x1
= aScrolledFrameOverflowArea
.x
,
3173 x2
= aScrolledFrameOverflowArea
.XMost(),
3174 y1
= aScrolledFrameOverflowArea
.y
,
3175 y2
= aScrolledFrameOverflowArea
.YMost();
3178 if (IsLTR() || mIsXUL
) {
3182 if (x2
> aScrollPortSize
.width
)
3183 x2
= aScrollPortSize
.width
;
3184 // When the scrolled frame chooses a size larger than its available width (because
3185 // its padding alone is larger than the available width), we need to keep the
3186 // start-edge of the scroll frame anchored to the start-edge of the scrollport.
3187 // When the scrolled frame is RTL, this means moving it in our left-based
3188 // coordinate system, so we need to compensate for its extra width here by
3189 // effectively repositioning the frame.
3190 nscoord extraWidth
= NS_MAX(0, mScrolledFrame
->GetSize().width
- aScrollPortSize
.width
);
3193 return nsRect(x1
, y1
, x2
- x1
, y2
- y1
);
3197 nsGfxScrollFrameInner::GetActualScrollbarSizes() const
3199 nsRect r
= mOuter
->GetPaddingRect() - mOuter
->GetPosition();
3201 return nsMargin(mScrollPort
.x
- r
.x
, mScrollPort
.y
- r
.y
,
3202 r
.XMost() - mScrollPort
.XMost(),
3203 r
.YMost() - mScrollPort
.YMost());
3207 nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox
* aScrollbar
, PRBool aVisible
)
3212 nsIScrollbarFrame
* scrollbar
= do_QueryFrame(aScrollbar
);
3214 // See if we have a mediator.
3215 nsIScrollbarMediator
* mediator
= scrollbar
->GetScrollbarMediator();
3217 // Inform the mediator of the visibility change.
3218 mediator
->VisibilityChanged(aVisible
);
3224 nsGfxScrollFrameInner::GetCoordAttribute(nsIBox
* aBox
, nsIAtom
* atom
, PRInt32 defaultValue
)
3227 nsIContent
* content
= aBox
->GetContent();
3230 content
->GetAttr(kNameSpaceID_None
, atom
, value
);
3231 if (!value
.IsEmpty())
3235 // convert it to an integer
3236 defaultValue
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
3240 return defaultValue
;
3244 nsGfxScrollFrameInner::SaveState(nsIStatefulFrame::SpecialStateID aStateID
)
3246 // Don't save "normal" state for the root scrollframe; that's
3247 // handled via the eDocumentScrollState state id
3248 if (mIsRoot
&& aStateID
== nsIStatefulFrame::eNoID
) {
3252 nsIScrollbarMediator
* mediator
= do_QueryFrame(GetScrolledFrame());
3254 // child handles its own scroll state, so don't bother saving state here
3258 nsPoint scrollPos
= GetScrollPosition();
3259 // Don't save scroll position if we are at (0,0)
3260 if (scrollPos
== nsPoint(0,0)) {
3264 nsPresState
* state
= new nsPresState();
3269 state
->SetScrollState(scrollPos
);
3275 nsGfxScrollFrameInner::RestoreState(nsPresState
* aState
)
3277 mRestorePos
= aState
->GetScrollState();
3280 mDidHistoryRestore
= PR_TRUE
;
3281 mLastPos
= mScrolledFrame
? GetScrollPosition() : nsPoint(0,0);
3285 nsGfxScrollFrameInner::PostScrolledAreaEvent()
3287 if (mScrolledAreaEvent
.IsPending()) {
3290 mScrolledAreaEvent
= new ScrolledAreaEvent(this);
3291 NS_DispatchToCurrentThread(mScrolledAreaEvent
.get());
3294 ////////////////////////////////////////////////////////////////////////////////
3295 // ScrolledArea change event dispatch
3298 nsGfxScrollFrameInner::ScrolledAreaEvent::Run()
3301 mInner
->FireScrolledAreaEvent();
3307 nsGfxScrollFrameInner::FireScrolledAreaEvent()
3309 mScrolledAreaEvent
.Forget();
3311 nsScrollAreaEvent
event(PR_TRUE
, NS_SCROLLEDAREACHANGED
, nsnull
);
3312 nsPresContext
*prescontext
= mOuter
->PresContext();
3313 nsIContent
* content
= mOuter
->GetContent();
3315 event
.mArea
= mScrolledFrame
->GetOverflowRectRelativeToParent();
3317 nsIDocument
*doc
= content
->GetCurrentDoc();
3319 nsEventDispatcher::Dispatch(doc
, prescontext
, &event
, nsnull
);