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 <mats.palmgren@bredband.net>
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 "nsIScrollableView.h"
48 #include "nsIScrollable.h"
49 #include "nsIViewManager.h"
50 #include "nsHTMLContainerFrame.h"
51 #include "nsGfxScrollFrame.h"
52 #include "nsGkAtoms.h"
53 #include "nsINameSpaceManager.h"
54 #include "nsIDocument.h"
55 #include "nsIFontMetrics.h"
56 #include "nsIDocumentObserver.h"
57 #include "nsIDocument.h"
58 #include "nsBoxLayoutState.h"
59 #include "nsINodeInfo.h"
60 #include "nsIScrollbarFrame.h"
61 #include "nsIScrollbarMediator.h"
62 #include "nsITextControlFrame.h"
63 #include "nsIDOMHTMLTextAreaElement.h"
64 #include "nsNodeInfoManager.h"
66 #include "nsGUIEvent.h"
67 #include "nsContentCreatorFunctions.h"
68 #include "nsISupportsPrimitives.h"
69 #include "nsAutoPtr.h"
70 #include "nsPresState.h"
71 #include "nsIGlobalHistory3.h"
72 #include "nsDocShellCID.h"
73 #include "nsIHTMLDocument.h"
74 #include "nsEventDispatcher.h"
75 #include "nsContentUtils.h"
76 #include "nsLayoutUtils.h"
78 #include "nsIAccessibilityService.h"
80 #include "nsDisplayList.h"
81 #include "nsBidiUtils.h"
83 //----------------------------------------------------------------------
85 //----------nsHTMLScrollFrame-------------------------------------------
88 NS_NewHTMLScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
90 return new (aPresShell
) nsHTMLScrollFrame(aPresShell
, aContext
, aIsRoot
);
93 nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
94 : nsHTMLContainerFrame(aContext
),
95 mInner(this, aIsRoot
, PR_FALSE
)
100 * Get the view that we are scrolling within the scrolling view.
103 nsIFrame
* nsHTMLScrollFrame::GetScrolledFrame() const
105 return mInner
.GetScrolledFrame();
108 nsIScrollableView
* nsHTMLScrollFrame::GetScrollableView()
110 return mInner
.GetScrollableView();
113 nsPoint
nsHTMLScrollFrame::GetScrollPosition() const
115 nsIScrollableView
* s
= mInner
.GetScrollableView();
116 nsPoint scrollPosition
;
117 s
->GetScrollPosition(scrollPosition
.x
, scrollPosition
.y
);
118 return scrollPosition
;
121 void nsHTMLScrollFrame::ScrollTo(nsPoint aScrollPosition
, PRUint32 aFlags
)
123 nsIScrollableView
* s
= mInner
.GetScrollableView();
124 s
->ScrollTo(aScrollPosition
.x
, aScrollPosition
.y
, aFlags
);
127 nsGfxScrollFrameInner::ScrollbarStyles
128 nsHTMLScrollFrame::GetScrollbarStyles() const {
129 return mInner
.GetScrollbarStylesFromFrame();
132 nsMargin
nsHTMLScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
133 return mInner
.GetDesiredScrollbarSizes(aState
);
136 void nsHTMLScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible
, PRBool aHorizontalVisible
)
138 mInner
.mNeverHasVerticalScrollbar
= !aVerticalVisible
;
139 mInner
.mNeverHasHorizontalScrollbar
= !aHorizontalVisible
;
142 nsIBox
* nsHTMLScrollFrame::GetScrollbarBox(PRBool aVertical
)
144 return aVertical
? mInner
.mVScrollbarBox
: mInner
.mHScrollbarBox
;
148 nsHTMLScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
150 return mInner
.CreateAnonymousContent(aElements
);
154 nsHTMLScrollFrame::Destroy()
157 nsHTMLContainerFrame::Destroy();
161 nsHTMLScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
162 nsFrameList
& aChildList
)
164 nsresult rv
= nsHTMLContainerFrame::SetInitialChildList(aListName
, aChildList
);
165 mInner
.CreateScrollableView();
166 mInner
.ReloadChildFrames();
168 // listen for scroll events.
169 mInner
.GetScrollableView()->AddScrollPositionListener(&mInner
);
176 nsHTMLScrollFrame::AppendFrames(nsIAtom
* aListName
,
177 nsFrameList
& aFrameList
)
179 NS_ASSERTION(!aListName
, "Only main list supported");
180 mFrames
.AppendFrames(nsnull
, aFrameList
);
181 mInner
.ReloadChildFrames();
186 nsHTMLScrollFrame::InsertFrames(nsIAtom
* aListName
,
187 nsIFrame
* aPrevFrame
,
188 nsFrameList
& aFrameList
)
190 NS_ASSERTION(!aListName
, "Only main list supported");
191 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
192 "inserting after sibling frame with different parent");
193 mFrames
.InsertFrames(nsnull
, aPrevFrame
, aFrameList
);
194 mInner
.ReloadChildFrames();
199 nsHTMLScrollFrame::RemoveFrame(nsIAtom
* aListName
,
202 NS_ASSERTION(!aListName
, "Only main list supported");
203 mFrames
.DestroyFrame(aOldFrame
);
204 mInner
.ReloadChildFrames();
209 nsHTMLScrollFrame::GetSplittableType() const
211 return NS_FRAME_NOT_SPLITTABLE
;
215 nsHTMLScrollFrame::GetSkipSides() const
221 nsHTMLScrollFrame::GetType() const
223 return nsGkAtoms::scrollFrame
;
227 nsHTMLScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
228 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
232 if (aForChild
== mInner
.mScrolledFrame
) {
233 // restrict aDamageRect to the scrollable view's bounds
234 nsRect damage
= aDamageRect
+ nsPoint(aX
, aY
);
236 if (r
.IntersectRect(damage
, mInner
.mScrollableView
->View()->GetBounds())) {
237 nsHTMLContainerFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
239 if (mInner
.mIsRoot
&& r
!= damage
) {
240 // Make sure we notify our prescontext about invalidations outside
241 // viewport clipping.
242 // This is important for things that are snapshotting the viewport,
243 // possibly outside the scrolled bounds.
244 // We don't need to propagate this any further up, though. Anyone who
245 // cares about scrolled-out-of-view invalidates had better be listening
246 // to our window directly.
247 PresContext()->NotifyInvalidation(damage
,
248 (aFlags
& INVALIDATE_CROSS_DOC
) != 0);
251 } else if (aForChild
== mInner
.mHScrollbarBox
) {
252 if (!mInner
.mHasHorizontalScrollbar
) {
253 // Our scrollbars may send up invalidations even when they're collapsed,
254 // because we just size a collapsed scrollbar to empty and some
255 // descendants may be non-empty. Suppress that invalidation here.
258 } else if (aForChild
== mInner
.mVScrollbarBox
) {
259 if (!mInner
.mHasVerticalScrollbar
) {
260 // Our scrollbars may send up invalidations even when they're collapsed,
261 // because we just size a collapsed scrollbar to empty and some
262 // descendants may be non-empty. Suppress that invalidation here.
268 nsHTMLContainerFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
272 HTML scrolling implementation
274 All other things being equal, we prefer layouts with fewer scrollbars showing.
277 struct ScrollReflowState
{
278 const nsHTMLReflowState
& mReflowState
;
279 nsBoxLayoutState mBoxState
;
280 nsGfxScrollFrameInner::ScrollbarStyles mStyles
;
281 nsMargin mComputedBorder
;
283 // === Filled in by ReflowScrolledFrame ===
284 PRPackedBool mReflowedContentsWithHScrollbar
;
285 PRPackedBool mReflowedContentsWithVScrollbar
;
287 // === Filled in when TryLayout succeeds ===
288 // The area of the scrollport, in coordinates relative to the scrollframe
289 nsRect mScrollPortRect
;
290 // The size of the inside-border area
291 nsSize mInsideBorderSize
;
292 // Whether we decided to show the horizontal scrollbar
293 PRPackedBool mShowHScrollbar
;
294 // Whether we decided to show the vertical scrollbar
295 PRPackedBool mShowVScrollbar
;
297 ScrollReflowState(nsIScrollableFrame
* aFrame
,
298 const nsHTMLReflowState
& aState
) :
299 mReflowState(aState
),
300 // mBoxState is just used for scrollbars so we don't need to
301 // worry about the reflow depth here
302 mBoxState(aState
.frame
->PresContext(), aState
.rendContext
, 0),
303 mStyles(aFrame
->GetScrollbarStyles()) {
307 // XXXldb Can this go away?
308 static nsSize
ComputeInsideBorderSize(ScrollReflowState
* aState
,
309 const nsSize
& aDesiredInsideBorderSize
)
311 // aDesiredInsideBorderSize is the frame size; i.e., it includes
312 // borders and padding (but the scrolled child doesn't have
313 // borders). The scrolled child has the same padding as us.
314 nscoord contentWidth
= aState
->mReflowState
.ComputedWidth();
315 if (contentWidth
== NS_UNCONSTRAINEDSIZE
) {
316 contentWidth
= aDesiredInsideBorderSize
.width
-
317 aState
->mReflowState
.mComputedPadding
.LeftRight();
319 nscoord contentHeight
= aState
->mReflowState
.ComputedHeight();
320 if (contentHeight
== NS_UNCONSTRAINEDSIZE
) {
321 contentHeight
= aDesiredInsideBorderSize
.height
-
322 aState
->mReflowState
.mComputedPadding
.TopBottom();
325 aState
->mReflowState
.ApplyMinMaxConstraints(&contentWidth
, &contentHeight
);
326 return nsSize(contentWidth
+ aState
->mReflowState
.mComputedPadding
.LeftRight(),
327 contentHeight
+ aState
->mReflowState
.mComputedPadding
.TopBottom());
331 GetScrollbarMetrics(nsBoxLayoutState
& aState
, nsIBox
* aBox
, nsSize
* aMin
,
332 nsSize
* aPref
, PRBool aVertical
)
334 NS_ASSERTION(aState
.GetRenderingContext(),
335 "Must have rendering context in layout state for size "
339 *aMin
= aBox
->GetMinSize(aState
);
340 nsBox::AddMargin(aBox
, *aMin
);
344 *aPref
= aBox
->GetPrefSize(aState
);
345 nsBox::AddMargin(aBox
, *aPref
);
350 * Assuming that we know the metrics for our wrapped frame and
351 * whether the horizontal and/or vertical scrollbars are present,
352 * compute the resulting layout and return PR_TRUE if the layout is
353 * consistent. If the layout is consistent then we fill in the
354 * computed fields of the ScrollReflowState.
356 * The layout is consistent when both scrollbars are showing if and only
357 * if they should be showing. A horizontal scrollbar should be showing if all
358 * following conditions are met:
359 * 1) the style is not HIDDEN
360 * 2) our inside-border height is at least the scrollbar height (i.e., the
361 * scrollbar fits vertically)
362 * 3) our scrollport width (the inside-border width minus the width allocated for a
363 * vertical scrollbar, if showing) is at least the scrollbar's min-width
364 * (i.e., the scrollbar fits horizontally)
365 * 4) the style is SCROLL, or the kid's overflow-area XMost is
366 * greater than the scrollport width
368 * @param aForce if PR_TRUE, then we just assume the layout is consistent.
371 nsHTMLScrollFrame::TryLayout(ScrollReflowState
* aState
,
372 nsHTMLReflowMetrics
* aKidMetrics
,
373 PRBool aAssumeHScroll
, PRBool aAssumeVScroll
,
374 PRBool aForce
, nsresult
* aResult
)
378 if ((aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeVScroll
) ||
379 (aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeHScroll
)) {
380 NS_ASSERTION(!aForce
, "Shouldn't be forcing a hidden scrollbar to show!");
384 if (aAssumeVScroll
!= aState
->mReflowedContentsWithVScrollbar
||
385 (aAssumeHScroll
!= aState
->mReflowedContentsWithHScrollbar
&&
386 ScrolledContentDependsOnHeight(aState
))) {
387 nsresult rv
= ReflowScrolledFrame(aState
, aAssumeHScroll
, aAssumeVScroll
,
388 aKidMetrics
, PR_FALSE
);
395 nsSize
vScrollbarMinSize(0, 0);
396 nsSize
vScrollbarPrefSize(0, 0);
397 if (mInner
.mVScrollbarBox
) {
398 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mVScrollbarBox
,
400 aAssumeVScroll
? &vScrollbarPrefSize
: nsnull
, PR_TRUE
);
402 nscoord vScrollbarDesiredWidth
= aAssumeVScroll
? vScrollbarPrefSize
.width
: 0;
403 nscoord vScrollbarMinHeight
= aAssumeVScroll
? vScrollbarMinSize
.height
: 0;
405 nsSize
hScrollbarMinSize(0, 0);
406 nsSize
hScrollbarPrefSize(0, 0);
407 if (mInner
.mHScrollbarBox
) {
408 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mHScrollbarBox
,
410 aAssumeHScroll
? &hScrollbarPrefSize
: nsnull
, PR_FALSE
);
412 nscoord hScrollbarDesiredHeight
= aAssumeHScroll
? hScrollbarPrefSize
.height
: 0;
413 nscoord hScrollbarMinWidth
= aAssumeHScroll
? hScrollbarMinSize
.width
: 0;
415 // First, compute our inside-border size and scrollport size
416 // XXXldb Can we depend more on ComputeSize here?
417 nsSize desiredInsideBorderSize
;
418 desiredInsideBorderSize
.width
= vScrollbarDesiredWidth
+
419 PR_MAX(aKidMetrics
->width
, hScrollbarMinWidth
);
420 desiredInsideBorderSize
.height
= hScrollbarDesiredHeight
+
421 PR_MAX(aKidMetrics
->height
, vScrollbarMinHeight
);
422 aState
->mInsideBorderSize
=
423 ComputeInsideBorderSize(aState
, desiredInsideBorderSize
);
424 nsSize scrollPortSize
= nsSize(PR_MAX(0, aState
->mInsideBorderSize
.width
- vScrollbarDesiredWidth
),
425 PR_MAX(0, aState
->mInsideBorderSize
.height
- hScrollbarDesiredHeight
));
428 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollPortSize
);
429 nscoord oneDevPixel
= aState
->mBoxState
.PresContext()->DevPixelsToAppUnits(1);
431 // If the style is HIDDEN then we already know that aAssumeHScroll is PR_FALSE
432 if (aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
) {
433 PRBool wantHScrollbar
=
434 aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
||
435 scrolledRect
.XMost() >= scrollPortSize
.width
+ oneDevPixel
||
436 scrolledRect
.x
<= -oneDevPixel
;
437 if (aState
->mInsideBorderSize
.height
< hScrollbarMinSize
.height
||
438 scrollPortSize
.width
< hScrollbarMinSize
.width
)
439 wantHScrollbar
= PR_FALSE
;
440 if (wantHScrollbar
!= aAssumeHScroll
)
444 // If the style is HIDDEN then we already know that aAssumeVScroll is PR_FALSE
445 if (aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
) {
446 PRBool wantVScrollbar
=
447 aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
||
448 scrolledRect
.YMost() >= scrollPortSize
.height
+ oneDevPixel
||
449 scrolledRect
.y
<= -oneDevPixel
;
450 if (aState
->mInsideBorderSize
.width
< vScrollbarMinSize
.width
||
451 scrollPortSize
.height
< vScrollbarMinSize
.height
)
452 wantVScrollbar
= PR_FALSE
;
453 if (wantVScrollbar
!= aAssumeVScroll
)
458 nscoord vScrollbarActualWidth
= aState
->mInsideBorderSize
.width
- scrollPortSize
.width
;
460 aState
->mShowHScrollbar
= aAssumeHScroll
;
461 aState
->mShowVScrollbar
= aAssumeVScroll
;
462 nsPoint
scrollPortOrigin(aState
->mComputedBorder
.left
,
463 aState
->mComputedBorder
.top
);
464 if (!mInner
.IsScrollbarOnRight()) {
465 scrollPortOrigin
.x
+= vScrollbarActualWidth
;
467 aState
->mScrollPortRect
= nsRect(scrollPortOrigin
, scrollPortSize
);
472 nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState
* aState
)
474 // Return true if ReflowScrolledFrame is going to do something different
475 // based on the presence of a horizontal scrollbar.
476 return (mInner
.mScrolledFrame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
) ||
477 aState
->mReflowState
.ComputedHeight() != NS_UNCONSTRAINEDSIZE
||
478 aState
->mReflowState
.mComputedMinHeight
> 0 ||
479 aState
->mReflowState
.mComputedMaxHeight
!= NS_UNCONSTRAINEDSIZE
;
483 nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState
* aState
,
484 PRBool aAssumeHScroll
,
485 PRBool aAssumeVScroll
,
486 nsHTMLReflowMetrics
* aMetrics
,
489 // these could be NS_UNCONSTRAINEDSIZE ... PR_MIN arithmetic should
491 nscoord paddingLR
= aState
->mReflowState
.mComputedPadding
.LeftRight();
493 nscoord availWidth
= aState
->mReflowState
.ComputedWidth() + paddingLR
;
495 nscoord computedHeight
= aState
->mReflowState
.ComputedHeight();
496 nscoord computedMinHeight
= aState
->mReflowState
.mComputedMinHeight
;
497 nscoord computedMaxHeight
= aState
->mReflowState
.mComputedMaxHeight
;
498 if (!ShouldPropagateComputedHeightToScrolledContent()) {
499 computedHeight
= NS_UNCONSTRAINEDSIZE
;
500 computedMinHeight
= 0;
501 computedMaxHeight
= NS_UNCONSTRAINEDSIZE
;
503 if (aAssumeHScroll
) {
504 nsSize hScrollbarPrefSize
=
505 mInner
.mHScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
506 if (computedHeight
!= NS_UNCONSTRAINEDSIZE
)
507 computedHeight
= PR_MAX(0, computedHeight
- hScrollbarPrefSize
.height
);
508 computedMinHeight
= PR_MAX(0, computedMinHeight
- hScrollbarPrefSize
.height
);
509 if (computedMaxHeight
!= NS_UNCONSTRAINEDSIZE
)
510 computedMaxHeight
= PR_MAX(0, computedMaxHeight
- hScrollbarPrefSize
.height
);
513 if (aAssumeVScroll
) {
514 nsSize vScrollbarPrefSize
=
515 mInner
.mVScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
516 availWidth
= PR_MAX(0, availWidth
- vScrollbarPrefSize
.width
);
519 // We're forcing the padding on our scrolled frame, so let it know what that
521 mInner
.mScrolledFrame
->
522 SetProperty(nsGkAtoms::usedPaddingProperty
,
523 new nsMargin(aState
->mReflowState
.mComputedPadding
),
524 nsCSSOffsetState::DestroyMarginFunc
);
526 nsPresContext
* presContext
= PresContext();
527 // Pass PR_FALSE for aInit so we can pass in the correct padding
528 nsHTMLReflowState
kidReflowState(presContext
, aState
->mReflowState
,
529 mInner
.mScrolledFrame
,
530 nsSize(availWidth
, NS_UNCONSTRAINEDSIZE
),
532 kidReflowState
.Init(presContext
, -1, -1, nsnull
,
533 &aState
->mReflowState
.mComputedPadding
);
534 kidReflowState
.mFlags
.mAssumingHScrollbar
= aAssumeHScroll
;
535 kidReflowState
.mFlags
.mAssumingVScrollbar
= aAssumeVScroll
;
536 kidReflowState
.SetComputedHeight(computedHeight
);
537 kidReflowState
.mComputedMinHeight
= computedMinHeight
;
538 kidReflowState
.mComputedMaxHeight
= computedMaxHeight
;
540 nsReflowStatus status
;
541 nsresult rv
= ReflowChild(mInner
.mScrolledFrame
, presContext
, *aMetrics
,
542 kidReflowState
, 0, 0,
543 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
, status
);
544 // Don't resize or position the view because we're going to resize
545 // it to the correct size anyway in PlaceScrollArea. Allowing it to
546 // resize here would size it to the natural height of the frame,
547 // which will usually be different from the scrollport height;
548 // invalidating the difference will cause unnecessary repainting.
549 FinishReflowChild(mInner
.mScrolledFrame
, presContext
,
550 &kidReflowState
, *aMetrics
, 0, 0,
551 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
| NS_FRAME_NO_SIZE_VIEW
);
553 // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
554 // setting their mOverflowArea. This is wrong because every frame should
555 // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
556 // support the 'outline' property because of this. Rather than fix the world
557 // right now, just fix up the overflow area if necessary. Note that we don't
558 // check HasOverflowRect() because it could be set even though the
559 // overflow area doesn't include the frame bounds.
560 aMetrics
->mOverflowArea
.UnionRect(aMetrics
->mOverflowArea
,
561 nsRect(0, 0, aMetrics
->width
, aMetrics
->height
));
563 aState
->mReflowedContentsWithHScrollbar
= aAssumeHScroll
;
564 aState
->mReflowedContentsWithVScrollbar
= aAssumeVScroll
;
570 nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowState
& aState
)
572 if (aState
.mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_AUTO
)
573 // no guessing required
574 return aState
.mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
;
576 return mInner
.mHasHorizontalScrollbar
;
580 nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowState
& aState
)
582 if (aState
.mStyles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
)
583 // no guessing required
584 return aState
.mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
;
586 // If we've had at least one non-initial reflow, then just assume
587 // the state of the vertical scrollbar will be what we determined
589 if (mInner
.mHadNonInitialReflow
) {
590 return mInner
.mHasVerticalScrollbar
;
593 // If this is the initial reflow, guess PR_FALSE because usually
594 // we have very little content by then.
595 if (InInitialReflow())
598 if (mInner
.mIsRoot
) {
599 // For viewports, try getting a hint from global history
600 // as to whether we had a vertical scrollbar last time.
602 nsresult rv
= mInner
.GetVScrollbarHintFromGlobalHistory(&hint
);
603 if (NS_SUCCEEDED(rv
))
605 // No hint. Assume that there will be a scrollbar; it seems to me
606 // that 'most pages' do have a scrollbar, and anyway, it's cheaper
607 // to do an extra reflow for the pages that *don't* need a
608 // scrollbar (because on average they will have less content).
612 // For non-viewports, just guess that we don't need a scrollbar.
613 // XXX I wonder if statistically this is the right idea; I'm
614 // basically guessing that there are a lot of overflow:auto DIVs
615 // that get their intrinsic size and don't overflow
620 nsHTMLScrollFrame::InInitialReflow() const
622 // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
623 // root scrollframe. In that case we want to skip this clause altogether.
624 // The guess here is that there are lots of overflow:auto divs out there that
625 // end up auto-sizing so they don't overflow, and that the root basically
626 // always needs a scrollbar if it did last time we loaded this page (good
627 // assumption, because our initial reflow is no longer synchronous).
628 return !mInner
.mIsRoot
&& (GetStateBits() & NS_FRAME_FIRST_REFLOW
);
632 nsHTMLScrollFrame::ReflowContents(ScrollReflowState
* aState
,
633 const nsHTMLReflowMetrics
& aDesiredSize
)
635 nsHTMLReflowMetrics
kidDesiredSize(aDesiredSize
.mFlags
);
636 nsresult rv
= ReflowScrolledFrame(aState
, GuessHScrollbarNeeded(*aState
),
637 GuessVScrollbarNeeded(*aState
), &kidDesiredSize
, PR_TRUE
);
638 NS_ENSURE_SUCCESS(rv
, rv
);
640 // There's an important special case ... if the child appears to fit
641 // in the inside-border rect (but overflows the scrollport), we
642 // should try laying it out without a vertical scrollbar. It will
643 // usually fit because making the available-width wider will not
644 // normally make the child taller. (The only situation I can think
645 // of is when you have a line containing %-width inline replaced
646 // elements whose percentages sum to more than 100%, so increasing
647 // the available width makes the line break where it was fitting
648 // before.) If we don't treat this case specially, then we will
649 // decide that showing scrollbars is OK because the content
650 // overflows when we're showing scrollbars and we won't try to
651 // remove the vertical scrollbar.
653 // Detecting when we enter this special case is important for when
654 // people design layouts that exactly fit the container "most of the
657 // XXX Is this check really sufficient to catch all the incremental cases
658 // where the ideal case doesn't have a scrollbar?
659 if ((aState
->mReflowedContentsWithHScrollbar
|| aState
->mReflowedContentsWithVScrollbar
) &&
660 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
&&
661 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
) {
662 nsSize insideBorderSize
=
663 ComputeInsideBorderSize(aState
,
664 nsSize(kidDesiredSize
.width
, kidDesiredSize
.height
));
665 nsRect scrolledRect
= mInner
.GetScrolledRect(insideBorderSize
);
666 if (nsRect(nsPoint(0, 0), insideBorderSize
).Contains(scrolledRect
)) {
667 // Let's pretend we had no scrollbars coming in here
668 rv
= ReflowScrolledFrame(aState
, PR_FALSE
, PR_FALSE
,
669 &kidDesiredSize
, PR_FALSE
);
670 NS_ENSURE_SUCCESS(rv
, rv
);
674 // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
675 // Do this first because changing the vertical scrollbar setting is expensive,
676 // forcing a reflow always.
678 // Try leaving the horizontal scrollbar unchanged first. This will be more
680 if (TryLayout(aState
, &kidDesiredSize
, aState
->mReflowedContentsWithHScrollbar
,
681 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
683 if (TryLayout(aState
, &kidDesiredSize
, !aState
->mReflowedContentsWithHScrollbar
,
684 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
687 // OK, now try toggling the vertical scrollbar. The performance advantage
688 // of trying the status-quo horizontal scrollbar state
689 // does not exist here (we'll have to reflow due to the vertical scrollbar
690 // change), so always try no horizontal scrollbar first.
691 PRBool newVScrollbarState
= !aState
->mReflowedContentsWithVScrollbar
;
692 if (TryLayout(aState
, &kidDesiredSize
, PR_FALSE
, newVScrollbarState
, PR_FALSE
, &rv
))
694 if (TryLayout(aState
, &kidDesiredSize
, PR_TRUE
, newVScrollbarState
, PR_FALSE
, &rv
))
697 // OK, we're out of ideas. Try again enabling whatever scrollbars we can
698 // enable and force the layout to stick even if it's inconsistent.
699 // This just happens sometimes.
700 TryLayout(aState
, &kidDesiredSize
,
701 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
,
702 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
,
708 nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState
& aState
)
710 nsIView
* scrollView
= mInner
.mScrollableView
->View();
711 nsIViewManager
* vm
= scrollView
->GetViewManager();
712 vm
->MoveViewTo(scrollView
, aState
.mScrollPortRect
.x
, aState
.mScrollPortRect
.y
);
713 vm
->ResizeView(scrollView
, nsRect(nsPoint(0, 0), aState
.mScrollPortRect
.Size()),
716 nsIFrame
*scrolledFrame
= mInner
.mScrolledFrame
;
717 nsIView
*scrolledView
= scrolledFrame
->GetView();
718 // Set the x,y of the scrolled frame to the correct value: the displacement
719 // from its origin to the origin of this frame
720 scrolledFrame
->SetPosition(scrolledView
->GetOffsetTo(GetView()));
723 // Preserve the width or height of empty rects
724 scrolledArea
.UnionRectIncludeEmpty(mInner
.GetScrolledRect(aState
.mScrollPortRect
.Size()),
725 nsRect(nsPoint(0,0), aState
.mScrollPortRect
.Size()));
727 // Store the new overflow area. Note that this changes where an outline
728 // of the scrolled frame would be painted, but scrolled frames can't have
729 // outlines (the outline would go on this scrollframe instead).
730 // Using FinishAndStoreOverflow is needed so the overflow rect
731 // gets set correctly. It also messes with the overflow rect in the
732 // -moz-hidden-unscrollable case, but scrolled frames can't have
733 // 'overflow' either.
734 // This needs to happen before SyncFrameViewAfterReflow so
735 // HasOverflowRect() will return the correct value.
736 scrolledFrame
->FinishAndStoreOverflow(&scrolledArea
,
737 scrolledFrame
->GetSize());
739 // Note that making the view *exactly* the size of the scrolled area
740 // is critical, since the view scrolling code uses the size of the
741 // scrolled view to clamp scroll requests.
742 nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame
->PresContext(),
746 NS_FRAME_NO_MOVE_VIEW
);
750 nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsIRenderingContext
*aRenderingContext
)
752 nsGfxScrollFrameInner::ScrollbarStyles ss
= GetScrollbarStyles();
753 if (ss
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
|| !mInner
.mVScrollbarBox
)
756 // Don't need to worry about reflow depth here since it's
757 // just for scrollbars
758 nsBoxLayoutState
bls(PresContext(), aRenderingContext
, 0);
759 nsSize
vScrollbarPrefSize(0, 0);
760 GetScrollbarMetrics(bls
, mInner
.mVScrollbarBox
,
761 nsnull
, &vScrollbarPrefSize
, PR_TRUE
);
762 return vScrollbarPrefSize
.width
;
765 /* virtual */ nscoord
766 nsHTMLScrollFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
768 nscoord result
= mInner
.mScrolledFrame
->GetMinWidth(aRenderingContext
);
769 DISPLAY_MIN_WIDTH(this, result
);
770 return result
+ GetIntrinsicVScrollbarWidth(aRenderingContext
);
773 /* virtual */ nscoord
774 nsHTMLScrollFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
776 nscoord result
= mInner
.mScrolledFrame
->GetPrefWidth(aRenderingContext
);
777 DISPLAY_PREF_WIDTH(this, result
);
778 return NSCoordSaturatingAdd(result
, GetIntrinsicVScrollbarWidth(aRenderingContext
));
782 nsHTMLScrollFrame::GetPadding(nsMargin
& aMargin
)
784 // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
785 // reaize that. If we're stuck inside a XUL box, we need to claim no
787 // @see also nsXULScrollFrame::GetPadding.
788 aMargin
.SizeTo(0,0,0,0);
793 nsHTMLScrollFrame::IsCollapsed(nsBoxLayoutState
& aBoxLayoutState
)
795 // We're never collapsed in the box sense.
800 nsHTMLScrollFrame::Reflow(nsPresContext
* aPresContext
,
801 nsHTMLReflowMetrics
& aDesiredSize
,
802 const nsHTMLReflowState
& aReflowState
,
803 nsReflowStatus
& aStatus
)
805 DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
806 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
808 ScrollReflowState
state(this, aReflowState
);
809 // sanity check: ensure that if we have no scrollbar, we treat it
811 if (!mInner
.mVScrollbarBox
|| mInner
.mNeverHasVerticalScrollbar
)
812 state
.mStyles
.mVertical
= NS_STYLE_OVERFLOW_HIDDEN
;
813 if (!mInner
.mHScrollbarBox
|| mInner
.mNeverHasHorizontalScrollbar
)
814 state
.mStyles
.mHorizontal
= NS_STYLE_OVERFLOW_HIDDEN
;
816 //------------ Handle Incremental Reflow -----------------
817 PRBool reflowContents
= PR_TRUE
; // XXX Ignored
818 PRBool reflowHScrollbar
= PR_TRUE
;
819 PRBool reflowVScrollbar
= PR_TRUE
;
820 PRBool reflowScrollCorner
= PR_TRUE
;
821 if (!aReflowState
.ShouldReflowAllKids()) {
822 #define NEEDS_REFLOW(frame_) \
823 ((frame_) && NS_SUBTREE_DIRTY(frame_))
825 reflowContents
= NEEDS_REFLOW(mInner
.mScrolledFrame
);
826 reflowHScrollbar
= NEEDS_REFLOW(mInner
.mHScrollbarBox
);
827 reflowVScrollbar
= NEEDS_REFLOW(mInner
.mVScrollbarBox
);
828 reflowScrollCorner
= NEEDS_REFLOW(mInner
.mScrollCornerBox
);
833 nsRect oldScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
834 nsRect oldScrolledAreaBounds
= mInner
.mScrolledFrame
->GetView()->GetBounds();
835 state
.mComputedBorder
= aReflowState
.mComputedBorderPadding
-
836 aReflowState
.mComputedPadding
;
838 nsresult rv
= ReflowContents(&state
, aDesiredSize
);
842 PlaceScrollArea(state
);
843 if (!mInner
.mPostedReflowCallback
) {
844 // Make sure we'll try scrolling to restored position
845 PresContext()->PresShell()->PostReflowCallback(&mInner
);
846 mInner
.mPostedReflowCallback
= PR_TRUE
;
849 PRBool didHaveHScrollbar
= mInner
.mHasHorizontalScrollbar
;
850 PRBool didHaveVScrollbar
= mInner
.mHasVerticalScrollbar
;
851 mInner
.mHasHorizontalScrollbar
= state
.mShowHScrollbar
;
852 mInner
.mHasVerticalScrollbar
= state
.mShowVScrollbar
;
853 nsRect newScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
854 nsRect newScrolledAreaBounds
= mInner
.mScrolledFrame
->GetView()->GetBounds();
855 if (mInner
.mSkippedScrollbarLayout
||
856 reflowHScrollbar
|| reflowVScrollbar
|| reflowScrollCorner
||
857 (GetStateBits() & NS_FRAME_IS_DIRTY
) ||
858 didHaveHScrollbar
!= state
.mShowHScrollbar
||
859 didHaveVScrollbar
!= state
.mShowVScrollbar
||
860 oldScrollAreaBounds
!= newScrollAreaBounds
||
861 oldScrolledAreaBounds
!= newScrolledAreaBounds
) {
862 if (!mInner
.mSupppressScrollbarUpdate
) {
863 mInner
.mSkippedScrollbarLayout
= PR_FALSE
;
864 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, state
.mShowHScrollbar
);
865 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, state
.mShowVScrollbar
);
866 // place and reflow scrollbars
867 nsRect insideBorderArea
=
868 nsRect(nsPoint(state
.mComputedBorder
.left
, state
.mComputedBorder
.top
),
869 state
.mInsideBorderSize
);
870 mInner
.LayoutScrollbars(state
.mBoxState
, insideBorderArea
,
871 oldScrollAreaBounds
, state
.mScrollPortRect
);
873 mInner
.mSkippedScrollbarLayout
= PR_TRUE
;
877 aDesiredSize
.width
= state
.mInsideBorderSize
.width
+
878 state
.mComputedBorder
.LeftRight();
879 aDesiredSize
.height
= state
.mInsideBorderSize
.height
+
880 state
.mComputedBorder
.TopBottom();
882 aDesiredSize
.mOverflowArea
= nsRect(0, 0, aDesiredSize
.width
, aDesiredSize
.height
);
883 FinishAndStoreOverflow(&aDesiredSize
);
885 if (!InInitialReflow() && !mInner
.mHadNonInitialReflow
) {
886 mInner
.mHadNonInitialReflow
= PR_TRUE
;
887 if (mInner
.mIsRoot
) {
888 // For viewports, record whether we needed a vertical scrollbar
889 // after the first non-initial reflow.
890 mInner
.SaveVScrollbarStateToGlobalHistory();
894 aStatus
= NS_FRAME_COMPLETE
;
895 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
896 mInner
.PostOverflowEvent();
902 nsHTMLScrollFrame::GetFrameName(nsAString
& aResult
) const
904 return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult
);
909 NS_IMETHODIMP
nsHTMLScrollFrame::GetAccessible(nsIAccessible
** aAccessible
)
911 *aAccessible
= nsnull
;
912 if (!IsFocusable()) {
915 // Focusable via CSS, so needs to be in accessibility hierarchy
916 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
919 return accService
->CreateHTMLGenericAccessible(static_cast<nsIFrame
*>(this), aAccessible
);
922 return NS_ERROR_FAILURE
;
927 nsHTMLScrollFrame::CurPosAttributeChanged(nsIContent
* aChild
,
930 mInner
.CurPosAttributeChanged(aChild
);
933 NS_QUERYFRAME_HEAD(nsHTMLScrollFrame
)
934 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
935 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
936 NS_QUERYFRAME_ENTRY(nsIScrollableViewProvider
)
937 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
938 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame
)
940 //----------nsXULScrollFrame-------------------------------------------
943 NS_NewXULScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
945 return new (aPresShell
) nsXULScrollFrame(aPresShell
, aContext
, aIsRoot
);
948 nsXULScrollFrame::nsXULScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
949 : nsBoxFrame(aShell
, aContext
, aIsRoot
),
950 mInner(this, aIsRoot
, PR_TRUE
)
952 SetLayoutManager(nsnull
);
956 * Get the view that we are scrolling within the scrolling view.
959 nsIFrame
* nsXULScrollFrame::GetScrolledFrame() const
961 return mInner
.GetScrolledFrame();
964 nsIScrollableView
* nsXULScrollFrame::GetScrollableView()
966 return mInner
.GetScrollableView();
969 nsPoint
nsXULScrollFrame::GetScrollPosition() const
971 nsIScrollableView
* s
= mInner
.GetScrollableView();
972 nsPoint scrollPosition
;
973 s
->GetScrollPosition(scrollPosition
.x
, scrollPosition
.y
);
974 return scrollPosition
;
977 void nsXULScrollFrame::ScrollTo(nsPoint aScrollPosition
, PRUint32 aFlags
)
979 nsIScrollableView
* s
= mInner
.GetScrollableView();
980 s
->ScrollTo(aScrollPosition
.x
, aScrollPosition
.y
, aFlags
);
983 nsGfxScrollFrameInner::ScrollbarStyles
984 nsXULScrollFrame::GetScrollbarStyles() const {
985 return mInner
.GetScrollbarStylesFromFrame();
988 nsMargin
nsXULScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
989 return mInner
.GetDesiredScrollbarSizes(aState
);
992 nsMargin
nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
993 NS_ASSERTION(aState
&& aState
->GetRenderingContext(),
994 "Must have rendering context in layout state for size "
997 nsMargin
result(0, 0, 0, 0);
999 if (mVScrollbarBox
) {
1000 nsSize size
= mVScrollbarBox
->GetPrefSize(*aState
);
1001 nsBox::AddMargin(mVScrollbarBox
, size
);
1002 if (IsScrollbarOnRight())
1003 result
.left
= size
.width
;
1005 result
.right
= size
.width
;
1008 if (mHScrollbarBox
) {
1009 nsSize size
= mHScrollbarBox
->GetPrefSize(*aState
);
1010 nsBox::AddMargin(mHScrollbarBox
, size
);
1011 // We don't currently support any scripts that would require a scrollbar
1012 // at the top. (Are there any?)
1013 result
.bottom
= size
.height
;
1019 void nsXULScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible
, PRBool aHorizontalVisible
)
1021 mInner
.mNeverHasVerticalScrollbar
= !aVerticalVisible
;
1022 mInner
.mNeverHasHorizontalScrollbar
= !aHorizontalVisible
;
1025 nsIBox
* nsXULScrollFrame::GetScrollbarBox(PRBool aVertical
)
1027 return aVertical
? mInner
.mVScrollbarBox
: mInner
.mHScrollbarBox
;
1031 nsXULScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
1033 return mInner
.CreateAnonymousContent(aElements
);
1037 nsXULScrollFrame::Destroy()
1040 nsBoxFrame::Destroy();
1044 nsXULScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
1045 nsFrameList
& aChildList
)
1047 nsresult rv
= nsBoxFrame::SetInitialChildList(aListName
, aChildList
);
1049 mInner
.CreateScrollableView();
1050 mInner
.ReloadChildFrames();
1052 // listen for scroll events.
1053 mInner
.GetScrollableView()->AddScrollPositionListener(&mInner
);
1060 nsXULScrollFrame::AppendFrames(nsIAtom
* aListName
,
1061 nsFrameList
& aFrameList
)
1063 nsresult rv
= nsBoxFrame::AppendFrames(aListName
, aFrameList
);
1064 mInner
.ReloadChildFrames();
1069 nsXULScrollFrame::InsertFrames(nsIAtom
* aListName
,
1070 nsIFrame
* aPrevFrame
,
1071 nsFrameList
& aFrameList
)
1073 nsresult rv
= nsBoxFrame::InsertFrames(aListName
, aPrevFrame
, aFrameList
);
1074 mInner
.ReloadChildFrames();
1079 nsXULScrollFrame::RemoveFrame(nsIAtom
* aListName
,
1080 nsIFrame
* aOldFrame
)
1082 nsresult rv
= nsBoxFrame::RemoveFrame(aListName
, aOldFrame
);
1083 mInner
.ReloadChildFrames();
1088 nsXULScrollFrame::GetSplittableType() const
1090 return NS_FRAME_NOT_SPLITTABLE
;
1094 nsXULScrollFrame::GetPadding(nsMargin
& aMargin
)
1096 aMargin
.SizeTo(0,0,0,0);
1101 nsXULScrollFrame::GetSkipSides() const
1107 nsXULScrollFrame::GetType() const
1109 return nsGkAtoms::scrollFrame
;
1113 nsXULScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
1114 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
1117 if (aForChild
== mInner
.mScrolledFrame
) {
1118 // restrict aDamageRect to the scrollable view's bounds
1120 if (r
.IntersectRect(aDamageRect
+ nsPoint(aX
, aY
),
1121 mInner
.mScrollableView
->View()->GetBounds())) {
1122 nsBoxFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
1127 nsBoxFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
1131 nsXULScrollFrame::GetBoxAscent(nsBoxLayoutState
& aState
)
1133 if (!mInner
.mScrolledFrame
)
1136 nscoord ascent
= mInner
.mScrolledFrame
->GetBoxAscent(aState
);
1137 nsMargin
m(0,0,0,0);
1138 GetBorderAndPadding(m
);
1147 nsXULScrollFrame::GetPrefSize(nsBoxLayoutState
& aState
)
1150 PropagateDebug(aState
);
1153 nsSize pref
= mInner
.mScrolledFrame
->GetPrefSize(aState
);
1155 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1157 // scrolled frames don't have their own margins
1159 if (mInner
.mVScrollbarBox
&&
1160 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1161 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
1162 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
1163 pref
.width
+= vSize
.width
;
1166 if (mInner
.mHScrollbarBox
&&
1167 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1168 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
1169 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
1170 pref
.height
+= hSize
.height
;
1173 AddBorderAndPadding(pref
);
1174 nsIBox::AddCSSPrefSize(aState
, this, pref
);
1179 nsXULScrollFrame::GetMinSize(nsBoxLayoutState
& aState
)
1182 PropagateDebug(aState
);
1185 nsSize min
= mInner
.mScrolledFrame
->GetMinSizeForScrollArea(aState
);
1187 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1189 if (mInner
.mVScrollbarBox
&&
1190 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1191 nsSize vSize
= mInner
.mVScrollbarBox
->GetMinSize(aState
);
1192 AddMargin(mInner
.mVScrollbarBox
, vSize
);
1193 min
.width
+= vSize
.width
;
1194 if (min
.height
< vSize
.height
)
1195 min
.height
= vSize
.height
;
1198 if (mInner
.mHScrollbarBox
&&
1199 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1200 nsSize hSize
= mInner
.mHScrollbarBox
->GetMinSize(aState
);
1201 AddMargin(mInner
.mHScrollbarBox
, hSize
);
1202 min
.height
+= hSize
.height
;
1203 if (min
.width
< hSize
.width
)
1204 min
.width
= hSize
.width
;
1207 AddBorderAndPadding(min
);
1208 nsIBox::AddCSSMinSize(aState
, this, min
);
1213 nsXULScrollFrame::GetMaxSize(nsBoxLayoutState
& aState
)
1216 PropagateDebug(aState
);
1219 nsSize
maxSize(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
1221 AddBorderAndPadding(maxSize
);
1222 nsIBox::AddCSSMaxSize(aState
, this, maxSize
);
1226 #if 0 // XXXldb I don't think this is even needed
1227 /* virtual */ nscoord
1228 nsXULScrollFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
1230 nsStyleUnit widthUnit
= GetStylePosition()->mWidth
.GetUnit();
1231 if (widthUnit
== eStyleUnit_Percent
|| widthUnit
== eStyleUnit_Auto
) {
1232 nsMargin border
= aReflowState
.mComputedBorderPadding
;
1233 aDesiredSize
.mMaxElementWidth
= border
.right
+ border
.left
;
1234 mMaxElementWidth
= aDesiredSize
.mMaxElementWidth
;
1236 NS_NOTYETIMPLEMENTED("Use the info from the scrolled frame");
1238 // if not set then use the cached size. If set then set it.
1239 if (aDesiredSize
.mMaxElementWidth
== -1)
1240 aDesiredSize
.mMaxElementWidth
= mMaxElementWidth
;
1242 mMaxElementWidth
= aDesiredSize
.mMaxElementWidth
;
1251 nsXULScrollFrame::GetFrameName(nsAString
& aResult
) const
1253 return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult
);
1257 void nsXULScrollFrame::CurPosAttributeChanged(nsIContent
* aChild
, PRInt32 aModType
)
1259 mInner
.CurPosAttributeChanged(aChild
);
1263 nsXULScrollFrame::DoLayout(nsBoxLayoutState
& aState
)
1265 PRUint32 flags
= aState
.LayoutFlags();
1266 nsresult rv
= Layout(aState
);
1267 aState
.SetLayoutFlags(flags
);
1269 nsBox::DoLayout(aState
);
1273 NS_QUERYFRAME_HEAD(nsXULScrollFrame
)
1274 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
1275 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
1276 NS_QUERYFRAME_ENTRY(nsIScrollableViewProvider
)
1277 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
1278 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
)
1280 //-------------------- Inner ----------------------
1282 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame
* aOuter
,
1285 : mScrollableView(nsnull
),
1286 mHScrollbarBox(nsnull
),
1287 mVScrollbarBox(nsnull
),
1288 mScrolledFrame(nsnull
),
1289 mScrollCornerBox(nsnull
),
1291 mRestoreRect(-1, -1, -1, -1),
1293 mNeverHasVerticalScrollbar(PR_FALSE
),
1294 mNeverHasHorizontalScrollbar(PR_FALSE
),
1295 mHasVerticalScrollbar(PR_FALSE
),
1296 mHasHorizontalScrollbar(PR_FALSE
),
1297 mViewInitiatedScroll(PR_FALSE
),
1298 mFrameInitiatedScroll(PR_FALSE
),
1299 mDidHistoryRestore(PR_FALSE
),
1302 mSupppressScrollbarUpdate(PR_FALSE
),
1303 mSkippedScrollbarLayout(PR_FALSE
),
1304 mDidLoadHistoryVScrollbarHint(PR_FALSE
),
1305 mHistoryVScrollbarHint(PR_FALSE
),
1306 mHadNonInitialReflow(PR_FALSE
),
1307 mHorizontalOverflow(PR_FALSE
),
1308 mVerticalOverflow(PR_FALSE
),
1309 mPostedReflowCallback(PR_FALSE
),
1310 mMayHaveDirtyFixedChildren(PR_FALSE
),
1311 mUpdateScrollbarAttributes(PR_FALSE
)
1315 nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
1319 NS_IMPL_QUERY_INTERFACE1(nsGfxScrollFrameInner
, nsIScrollPositionListener
)
1322 nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1323 const nsRect
& aDirtyRect
,
1324 const nsDisplayListSet
& aLists
)
1326 nsresult rv
= mOuter
->DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1327 NS_ENSURE_SUCCESS(rv
, rv
);
1329 if (aBuilder
->GetIgnoreScrollFrame() == mOuter
) {
1330 // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
1331 // The scrolled frame shouldn't have its own background/border, so we
1332 // can just pass aLists directly.
1333 return mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
,
1334 aDirtyRect
, aLists
);
1337 // Now display the scrollbars and scrollcorner. These parts are drawn
1338 // in the border-background layer, on top of our own background and
1339 // borders and underneath borders and backgrounds of later elements
1341 nsIFrame
* kid
= mOuter
->GetFirstChild(nsnull
);
1343 if (kid
!= mScrolledFrame
) {
1344 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
1345 NS_ENSURE_SUCCESS(rv
, rv
);
1347 kid
= kid
->GetNextSibling();
1350 // Overflow clipping can never clip frames outside our subtree, so there
1351 // is no need to worry about whether we are a moving frame that might clip
1352 // non-moving frames.
1353 nsRect frameClip
= mScrollableView
->View()->GetBounds();
1355 // Not all our descendants will be clipped by overflow clipping, but all
1356 // the ones that aren't clipped will be out of flow frames that have already
1357 // had dirty rects saved for them by their parent frames calling
1358 // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
1360 dirtyRect
.IntersectRect(aDirtyRect
, frameClip
);
1362 nsDisplayListCollection set
;
1363 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
, dirtyRect
, set
);
1364 NS_ENSURE_SUCCESS(rv
, rv
);
1365 nsRect clip
= frameClip
+ aBuilder
->ToReferenceFrame(mOuter
);
1366 // mScrolledFrame may have given us a background, e.g., the scrolled canvas
1367 // frame below the viewport. If so, we want it to be clipped. We also want
1368 // to end up on our BorderBackground list.
1369 // If we are the viewport scrollframe, then clip all our descendants (to ensure
1370 // that fixed-pos elements get clipped by us).
1371 rv
= mOuter
->OverflowClip(aBuilder
, set
, aLists
, clip
, PR_TRUE
, mIsRoot
);
1372 NS_ENSURE_SUCCESS(rv
, rv
);
1378 nsGfxScrollFrameInner::CreateScrollableView()
1380 nsIView
* outerView
= mOuter
->GetView();
1381 NS_ASSERTION(outerView
, "scrollframes must have views");
1382 nsIViewManager
* viewManager
= outerView
->GetViewManager();
1383 mScrollableView
= viewManager
->CreateScrollableView(mOuter
->GetRect(), outerView
);
1384 if (!mScrollableView
)
1387 nsIView
* view
= mScrollableView
->View();
1389 // Insert the view into the view hierarchy
1390 viewManager
->InsertChild(outerView
, view
, nsnull
, PR_TRUE
);
1393 static void HandleScrollPref(nsIScrollable
*aScrollable
, PRInt32 aOrientation
,
1397 aScrollable
->GetDefaultScrollbarPreferences(aOrientation
, &pref
);
1399 case nsIScrollable::Scrollbar_Auto
:
1400 // leave |aValue| untouched
1402 case nsIScrollable::Scrollbar_Never
:
1403 aValue
= NS_STYLE_OVERFLOW_HIDDEN
;
1405 case nsIScrollable::Scrollbar_Always
:
1406 aValue
= NS_STYLE_OVERFLOW_SCROLL
;
1412 nsGfxScrollFrameInner::GetParentViewForChildFrame(nsIFrame
* aFrame
) const
1414 if (aFrame
->GetContent() == mOuter
->GetContent()) {
1415 NS_ASSERTION(mScrollableView
, "Scrollable view should have been created by now");
1416 // scrolled frame, put it under our anonymous view
1417 return mScrollableView
->View();
1419 // scrollbars and stuff; put them under our regular view
1420 return mOuter
->GetView();
1423 nsGfxScrollFrameInner::ScrollbarStyles
1424 nsGfxScrollFrameInner::GetScrollbarStylesFromFrame() const
1426 ScrollbarStyles result
;
1428 nsPresContext
* presContext
= mOuter
->PresContext();
1429 if (!presContext
->IsDynamic() &&
1430 !(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
1431 return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN
, NS_STYLE_OVERFLOW_HIDDEN
);
1435 result
= presContext
->GetViewportOverflowOverride();
1437 nsCOMPtr
<nsISupports
> container
= presContext
->GetContainer();
1438 nsCOMPtr
<nsIScrollable
> scrollable
= do_QueryInterface(container
);
1440 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_X
,
1441 result
.mHorizontal
);
1442 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_Y
,
1446 const nsStyleDisplay
*disp
= mOuter
->GetStyleDisplay();
1447 result
.mHorizontal
= disp
->mOverflowX
;
1448 result
.mVertical
= disp
->mOverflowY
;
1451 NS_ASSERTION(result
.mHorizontal
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1452 result
.mHorizontal
!= NS_STYLE_OVERFLOW_CLIP
&&
1453 result
.mVertical
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1454 result
.mVertical
!= NS_STYLE_OVERFLOW_CLIP
,
1455 "scrollbars should not have been created");
1460 * this code is resposible for restoring the scroll position back to some
1461 * saved position. if the user has not moved the scroll position manually
1462 * we keep scrolling down until we get to our original position. keep in
1463 * mind that content could incrementally be coming in. we only want to stop
1464 * when we reach our new position.
1467 nsGfxScrollFrameInner::ScrollToRestoredPosition()
1469 nsIScrollableView
* scrollingView
= GetScrollableView();
1470 if (!scrollingView
) {
1473 if (mRestoreRect
.y
== -1 || mLastPos
.x
== -1 || mLastPos
.y
== -1) {
1476 // make sure our scroll position did not change for where we last put
1477 // it. if it does then the user must have moved it, and we no longer
1481 scrollingView
->GetScrollPosition(x
, y
);
1483 // if we didn't move, we still need to restore
1484 if (x
== mLastPos
.x
&& y
== mLastPos
.y
) {
1485 nsRect
childRect(0, 0, 0, 0);
1486 nsIView
* child
= nsnull
;
1487 nsresult rv
= scrollingView
->GetScrolledView(child
);
1488 if (NS_SUCCEEDED(rv
) && child
)
1489 childRect
= child
->GetBounds();
1491 PRInt32 cx
, cy
, x
, y
;
1492 scrollingView
->GetScrollPosition(cx
,cy
);
1494 x
= (int)mRestoreRect
.x
;
1495 y
= (int)mRestoreRect
.y
;
1497 // if our position is greater than the scroll position, scroll.
1498 // remember that we could be incrementally loading so we may enter
1499 // and scroll many times.
1500 if (y
!= cy
|| x
!= cx
) {
1501 scrollingView
->ScrollTo(x
, y
, 0);
1502 // scrollpostion goes from twips to pixels. this fixes any roundoff
1504 scrollingView
->GetScrollPosition(mLastPos
.x
, mLastPos
.y
);
1506 // if we reached the position then stop
1507 mRestoreRect
.y
= -1;
1512 // user moved the position, so we won't need to restore
1519 nsGfxScrollFrameInner::FireScrollPortEvent()
1521 mAsyncScrollPortEvent
.Forget();
1523 // Keep this in sync with PostOverflowEvent().
1524 nsSize scrollportSize
= GetScrollPortSize();
1525 nsSize childSize
= GetScrolledRect(scrollportSize
).Size();
1527 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
1528 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
1530 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
1531 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
1533 if (!vertChanged
&& !horizChanged
) {
1537 // If both either overflowed or underflowed then we dispatch only one
1539 PRBool both
= vertChanged
&& horizChanged
&&
1540 newVerticalOverflow
== newHorizontalOverflow
;
1541 nsScrollPortEvent::orientType orient
;
1543 orient
= nsScrollPortEvent::both
;
1544 mHorizontalOverflow
= newHorizontalOverflow
;
1545 mVerticalOverflow
= newVerticalOverflow
;
1547 else if (vertChanged
) {
1548 orient
= nsScrollPortEvent::vertical
;
1549 mVerticalOverflow
= newVerticalOverflow
;
1551 // We need to dispatch a separate horizontal DOM event. Do that the next
1552 // time around since dispatching the vertical DOM event might destroy
1554 PostOverflowEvent();
1558 orient
= nsScrollPortEvent::horizontal
;
1559 mHorizontalOverflow
= newHorizontalOverflow
;
1562 nsScrollPortEvent
event(PR_TRUE
,
1563 (orient
== nsScrollPortEvent::horizontal
?
1564 mHorizontalOverflow
: mVerticalOverflow
) ?
1565 NS_SCROLLPORT_OVERFLOW
: NS_SCROLLPORT_UNDERFLOW
,
1567 event
.orient
= orient
;
1568 return nsEventDispatcher::Dispatch(mOuter
->GetContent(),
1569 mOuter
->PresContext(), &event
);
1573 nsGfxScrollFrameInner::ReloadChildFrames()
1575 mScrolledFrame
= nsnull
;
1576 mHScrollbarBox
= nsnull
;
1577 mVScrollbarBox
= nsnull
;
1578 mScrollCornerBox
= nsnull
;
1580 nsIFrame
* frame
= mOuter
->GetFirstChild(nsnull
);
1582 nsIContent
* content
= frame
->GetContent();
1583 if (content
== mOuter
->GetContent()) {
1584 NS_ASSERTION(!mScrolledFrame
, "Already found the scrolled frame");
1585 mScrolledFrame
= frame
;
1588 content
->GetAttr(kNameSpaceID_None
, nsGkAtoms::orient
, value
);
1589 if (!value
.IsEmpty()) {
1590 // probably a scrollbar then
1591 if (value
.LowerCaseEqualsLiteral("horizontal")) {
1592 NS_ASSERTION(!mHScrollbarBox
, "Found multiple horizontal scrollbars?");
1593 mHScrollbarBox
= frame
;
1595 NS_ASSERTION(!mVScrollbarBox
, "Found multiple vertical scrollbars?");
1596 mVScrollbarBox
= frame
;
1599 // probably a scrollcorner
1600 NS_ASSERTION(!mScrollCornerBox
, "Found multiple scrollcorners");
1601 mScrollCornerBox
= frame
;
1605 frame
= frame
->GetNextSibling();
1610 nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
1612 nsPresContext
* presContext
= mOuter
->PresContext();
1613 nsIFrame
* parent
= mOuter
->GetParent();
1615 // Don't create scrollbars if we're printing/print previewing
1616 // Get rid of this code when printing moves to its own presentation
1617 if (!presContext
->IsDynamic()) {
1618 // allow scrollbars if this is the child of the viewport, because
1619 // we must be the scrollbars for the print preview window
1620 if (!(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
1621 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
1626 nsIScrollableFrame
*scrollable
= do_QueryFrame(mOuter
);
1628 // At this stage in frame construction, the document element and/or
1629 // BODY overflow styles have not yet been propagated to the
1630 // viewport. So GetScrollbarStylesFromFrame called here will only
1631 // take into account the scrollbar preferences set on the docshell.
1632 // Thus if no scrollbar preferences are set on the docshell, we will
1633 // always create scrollbars, which means later dynamic changes to
1634 // propagated overflow styles will show or hide scrollbars on the
1635 // viewport without requiring frame reconstruction of the viewport
1638 // XXX On the other hand, if scrolling="no" is set on the container
1639 // we won't create scrollbars here so no scrollbars will ever be
1640 // created even if the container's scrolling attribute is later
1641 // changed. However, this has never been supported.
1642 ScrollbarStyles styles
= scrollable
->GetScrollbarStyles();
1643 PRBool canHaveHorizontal
= styles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
;
1644 PRBool canHaveVertical
= styles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
;
1645 if (!canHaveHorizontal
&& !canHaveVertical
) {
1650 // The anonymous <div> used by <inputs> never gets scrollbars.
1651 nsITextControlFrame
* textFrame
= do_QueryFrame(parent
);
1653 // Make sure we are not a text area.
1654 nsCOMPtr
<nsIDOMHTMLTextAreaElement
> textAreaElement(do_QueryInterface(parent
->GetContent()));
1655 if (!textAreaElement
) {
1656 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
1663 nsNodeInfoManager
*nodeInfoManager
=
1664 presContext
->Document()->NodeInfoManager();
1665 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
1666 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollbar
, nsnull
,
1668 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
1670 if (canHaveHorizontal
) {
1671 rv
= NS_NewElement(getter_AddRefs(mHScrollbarContent
),
1672 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1673 NS_ENSURE_SUCCESS(rv
, rv
);
1674 mHScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
1675 NS_LITERAL_STRING("horizontal"), PR_FALSE
);
1676 if (!aElements
.AppendElement(mHScrollbarContent
))
1677 return NS_ERROR_OUT_OF_MEMORY
;
1680 if (canHaveVertical
) {
1681 rv
= NS_NewElement(getter_AddRefs(mVScrollbarContent
),
1682 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1683 NS_ENSURE_SUCCESS(rv
, rv
);
1684 mVScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
1685 NS_LITERAL_STRING("vertical"), PR_FALSE
);
1686 if (!aElements
.AppendElement(mVScrollbarContent
))
1687 return NS_ERROR_OUT_OF_MEMORY
;
1690 if (canHaveHorizontal
&& canHaveVertical
) {
1691 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollcorner
, nsnull
,
1693 rv
= NS_NewElement(getter_AddRefs(mScrollCornerContent
),
1694 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1695 NS_ENSURE_SUCCESS(rv
, rv
);
1696 if (!aElements
.AppendElement(mScrollCornerContent
))
1697 return NS_ERROR_OUT_OF_MEMORY
;
1704 nsGfxScrollFrameInner::Destroy()
1706 // Unbind any content created in CreateAnonymousContent from the tree
1707 nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent
);
1708 nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent
);
1709 nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent
);
1711 mScrollEvent
.Revoke();
1712 mAsyncScrollPortEvent
.Revoke();
1713 if (mPostedReflowCallback
) {
1714 mOuter
->PresContext()->PresShell()->CancelReflowCallback(this);
1715 mPostedReflowCallback
= PR_FALSE
;
1717 nsIScrollableView
*view
= GetScrollableView();
1718 NS_ASSERTION(view
, "unexpected null pointer");
1720 view
->RemoveScrollPositionListener(this);
1724 nsGfxScrollFrameInner::ScrollPositionWillChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
1731 * Called when we want to update the scrollbar position, either because scrolling happened
1732 * or the user moved the scrollbar position and we need to undo that (e.g., when the user
1733 * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
1734 * to its initial position for the start of the smooth sequence).
1737 nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX
, nscoord aY
)
1740 SetCoordAttribute(mVScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
1741 aY
- GetScrolledRect(GetScrollPortSize()).y
);
1744 SetCoordAttribute(mHScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
1745 aX
- GetScrolledRect(GetScrollPortSize()).x
);
1749 nsGfxScrollFrameInner::ViewPositionDidChange(nsIScrollableView
* aScrollable
,
1750 nsTArray
<nsIWidget::Configuration
>* aConfigurations
)
1752 // Update frame position to match view offsets
1753 nsPoint childOffset
= mScrolledFrame
->GetView()->GetOffsetTo(mOuter
->GetView());
1754 mScrolledFrame
->SetPosition(childOffset
);
1756 mOuter
->PresContext()->RootPresContext()->
1757 GetPluginGeometryUpdates(mOuter
, aConfigurations
);
1761 * Called whenever actual scrolling happens for any reason.
1764 nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
1766 NS_ASSERTION(!mViewInitiatedScroll
, "Cannot reenter ScrollPositionDidChange");
1768 mViewInitiatedScroll
= PR_TRUE
;
1769 InternalScrollPositionDidChange(aX
, aY
);
1770 mViewInitiatedScroll
= PR_FALSE
;
1774 // Notify that the display has changed
1775 mOuter
->InvalidateWithFlags(nsRect(nsPoint(0, 0), mOuter
->GetSize()),
1776 nsIFrame::INVALIDATE_NOTIFY_ONLY
);
1781 void nsGfxScrollFrameInner::CurPosAttributeChanged(nsIContent
* aContent
)
1783 NS_ASSERTION(aContent
, "aContent must not be null");
1784 NS_ASSERTION((mHScrollbarBox
&& mHScrollbarBox
->GetContent() == aContent
) ||
1785 (mVScrollbarBox
&& mVScrollbarBox
->GetContent() == aContent
),
1786 "unexpected child");
1788 // Attribute changes on the scrollbars happen in one of three ways:
1789 // 1) The scrollbar changed the attribute in response to some user event
1790 // 2) We changed the attribute in response to a ScrollPositionDidChange
1791 // callback from the scrolling view
1792 // 3) We changed the attribute to adjust the scrollbars for the start
1793 // of a smooth scroll operation
1795 // In case 2), we don't need to scroll the view because the scrolling
1796 // has already happened. In case 3) we don't need to scroll because
1797 // we're just adjusting the scrollbars back to the correct setting
1800 // Cases 1) and 3) do not indicate that actual scrolling has happened. Only
1801 // case 2) indicates actual scrolling. Therefore we do not fire onscroll
1802 // here, but in ScrollPositionDidChange.
1804 // We used to detect this case implicitly because we'd compare the
1805 // scrollbar attributes with the view's current scroll position and
1806 // bail out if they were equal. But that approach is fragile; it can
1807 // fail when, for example, the view scrolls horizontally and
1808 // vertically simultaneously; we'll get here when only the vertical
1809 // attribute has been set, so the attributes and the view scroll
1810 // position don't yet agree, and we'd tell the view to scroll to the
1811 // new vertical position and the old horizontal position! Even worse
1812 // things could happen when smooth scrolling got involved ... crashes
1813 // and other terrors.
1814 if (mViewInitiatedScroll
|| mFrameInitiatedScroll
) return;
1816 nsRect scrolledRect
= GetScrolledRect(GetScrollPortSize());
1818 nscoord x
= GetCoordAttribute(mHScrollbarBox
, nsGkAtoms::curpos
,
1821 nscoord y
= GetCoordAttribute(mVScrollbarBox
, nsGkAtoms::curpos
,
1825 // Make sure the scrollbars indeed moved before firing the event.
1826 // I think it is OK to prevent the call to ScrollbarChanged()
1827 // if we didn't actually move. The following check is the first
1828 // thing ScrollbarChanged() does anyway, before deciding to move
1830 nscoord curPosX
=0, curPosY
=0;
1831 nsIScrollableView
* s
= GetScrollableView();
1833 s
->GetScrollPosition(curPosX
, curPosY
);
1834 if (x
== curPosX
&& y
== curPosY
)
1837 PRBool isSmooth
= aContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::smooth
);
1840 // Make sure an attribute-setting callback occurs even if the view
1841 // didn't actually move yet. We need to make sure other listeners
1842 // see that the scroll position is not (yet) what they thought it
1845 NS_ASSERTION(!mFrameInitiatedScroll
, "Unexpected reentry");
1846 // Make sure we don't do anything in when the view calls us back
1847 // for this scroll operation.
1848 mFrameInitiatedScroll
= PR_TRUE
;
1849 InternalScrollPositionDidChange(curPosX
, curPosY
);
1850 mFrameInitiatedScroll
= PR_FALSE
;
1852 ScrollbarChanged(mOuter
->PresContext(), x
, y
, isSmooth
? NS_VMREFRESH_SMOOTHSCROLL
: 0);
1856 /* ============= Scroll events ========== */
1859 nsGfxScrollFrameInner::ScrollEvent::Run()
1862 mInner
->FireScrollEvent();
1867 nsGfxScrollFrameInner::FireScrollEvent()
1869 mScrollEvent
.Forget();
1871 nsScrollbarEvent
event(PR_TRUE
, NS_SCROLL_EVENT
, nsnull
);
1872 nsEventStatus status
= nsEventStatus_eIgnore
;
1873 nsIContent
* content
= mOuter
->GetContent();
1874 nsPresContext
* prescontext
= mOuter
->PresContext();
1875 // Fire viewport scroll events at the document (where they
1876 // will bubble to the window)
1878 nsIDocument
* doc
= content
->GetCurrentDoc();
1880 nsEventDispatcher::Dispatch(doc
, prescontext
, &event
, nsnull
, &status
);
1883 // scroll events fired at elements don't bubble (although scroll events
1884 // fired at documents do, to the window)
1885 event
.flags
|= NS_EVENT_FLAG_CANT_BUBBLE
;
1886 nsEventDispatcher::Dispatch(content
, prescontext
, &event
, nsnull
, &status
);
1891 nsGfxScrollFrameInner::PostScrollEvent()
1893 if (mScrollEvent
.IsPending())
1896 nsRefPtr
<ScrollEvent
> ev
= new ScrollEvent(this);
1897 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
1898 NS_WARNING("failed to dispatch ScrollEvent");
1905 nsGfxScrollFrameInner::AsyncScrollPortEvent::Run()
1908 mInner
->mOuter
->PresContext()->GetPresShell()->
1909 FlushPendingNotifications(Flush_InterruptibleLayout
);
1911 return mInner
? mInner
->FireScrollPortEvent() : NS_OK
;
1915 nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState
& aState
,
1916 nsRect
& aScrollAreaSize
, PRBool aOnTop
)
1918 if (!mInner
.mHScrollbarBox
)
1921 return AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnTop
, PR_TRUE
, PR_TRUE
);
1925 nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState
& aState
,
1926 nsRect
& aScrollAreaSize
, PRBool aOnRight
)
1928 if (!mInner
.mVScrollbarBox
)
1931 return AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnRight
, PR_FALSE
, PR_TRUE
);
1935 nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState
& aState
,
1936 nsRect
& aScrollAreaSize
, PRBool aOnTop
)
1938 // removing a scrollbar should always fit
1942 AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnTop
, PR_TRUE
, PR_FALSE
);
1943 NS_ASSERTION(result
, "Removing horizontal scrollbar failed to fit??");
1947 nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState
& aState
,
1948 nsRect
& aScrollAreaSize
, PRBool aOnRight
)
1950 // removing a scrollbar should always fit
1954 AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnRight
, PR_FALSE
, PR_FALSE
);
1955 NS_ASSERTION(result
, "Removing vertical scrollbar failed to fit??");
1959 nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState
& aState
, nsRect
& aScrollAreaSize
,
1960 PRBool aOnTop
, PRBool aHorizontal
, PRBool aAdd
)
1963 if (mInner
.mNeverHasHorizontalScrollbar
|| !mInner
.mHScrollbarBox
)
1966 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
1967 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
1969 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, aAdd
);
1971 PRBool hasHorizontalScrollbar
;
1972 PRBool fit
= AddRemoveScrollbar(hasHorizontalScrollbar
, aScrollAreaSize
.y
, aScrollAreaSize
.height
, hSize
.height
, aOnTop
, aAdd
);
1973 mInner
.mHasHorizontalScrollbar
= hasHorizontalScrollbar
; // because mHasHorizontalScrollbar is a PRPackedBool
1975 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, !aAdd
);
1979 if (mInner
.mNeverHasVerticalScrollbar
|| !mInner
.mVScrollbarBox
)
1982 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
1983 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
1985 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, aAdd
);
1987 PRBool hasVerticalScrollbar
;
1988 PRBool fit
= AddRemoveScrollbar(hasVerticalScrollbar
, aScrollAreaSize
.x
, aScrollAreaSize
.width
, vSize
.width
, aOnTop
, aAdd
);
1989 mInner
.mHasVerticalScrollbar
= hasVerticalScrollbar
; // because mHasVerticalScrollbar is a PRPackedBool
1991 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, !aAdd
);
1998 nsXULScrollFrame::AddRemoveScrollbar(PRBool
& aHasScrollbar
, nscoord
& aXY
,
1999 nscoord
& aSize
, nscoord aSbSize
,
2000 PRBool aRightOrBottom
, PRBool aAdd
)
2002 nscoord size
= aSize
;
2005 if (size
!= NS_INTRINSICSIZE
) {
2008 if (!aRightOrBottom
&& size
>= 0)
2012 if (!aRightOrBottom
)
2017 // not enough room? Yes? Return true.
2019 aHasScrollbar
= aAdd
;
2025 aHasScrollbar
= PR_FALSE
;
2030 nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState
& aState
, const nsRect
& aRect
)
2032 nsIView
* scrollView
= mInner
.mScrollableView
->View();
2033 nsIViewManager
* vm
= scrollView
->GetViewManager();
2034 vm
->MoveViewTo(scrollView
, aRect
.x
, aRect
.y
);
2035 vm
->ResizeView(scrollView
, nsRect(nsPoint(0, 0), aRect
.Size()), PR_TRUE
);
2037 PRUint32 oldflags
= aState
.LayoutFlags();
2038 nsPoint childOffset
=
2039 mInner
.mScrolledFrame
->GetView()->GetOffsetTo(GetView());
2040 nsRect childRect
= nsRect(childOffset
, aRect
.Size());
2042 PRInt32 flags
= NS_FRAME_NO_MOVE_VIEW
;
2044 nsSize minSize
= mInner
.mScrolledFrame
->GetMinSize(aState
);
2046 if (minSize
.height
> childRect
.height
)
2047 childRect
.height
= minSize
.height
;
2049 if (minSize
.width
> childRect
.width
)
2050 childRect
.width
= minSize
.width
;
2052 aState
.SetLayoutFlags(flags
);
2053 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2054 mInner
.mScrolledFrame
->Layout(aState
);
2056 childRect
= mInner
.mScrolledFrame
->GetRect();
2058 if (childRect
.width
< aRect
.width
|| childRect
.height
< aRect
.height
)
2060 childRect
.width
= PR_MAX(childRect
.width
, aRect
.width
);
2061 childRect
.height
= PR_MAX(childRect
.height
, aRect
.height
);
2063 // remove overflow area when we update the bounds,
2064 // because we've already accounted for it
2065 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2066 mInner
.mScrolledFrame
->ClearOverflowRect();
2069 aState
.SetLayoutFlags(oldflags
);
2073 void nsGfxScrollFrameInner::PostOverflowEvent()
2075 if (mAsyncScrollPortEvent
.IsPending())
2078 // Keep this in sync with FireScrollPortEvent().
2079 nsSize scrollportSize
= GetScrollPortSize();
2080 nsSize childSize
= GetScrolledRect(scrollportSize
).Size();
2082 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
2083 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
2085 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
2086 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
2088 if (!vertChanged
&& !horizChanged
) {
2092 nsRefPtr
<AsyncScrollPortEvent
> ev
= new AsyncScrollPortEvent(this);
2093 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev
)))
2094 mAsyncScrollPortEvent
= ev
;
2098 nsGfxScrollFrameInner::IsLTR() const
2100 //TODO make bidi code set these from preferences
2102 nsIFrame
*frame
= mOuter
;
2103 // XXX This is a bit on the slow side.
2105 // If we're the root scrollframe, we need the root element's style data.
2106 nsPresContext
*presContext
= mOuter
->PresContext();
2107 nsIDocument
*document
= presContext
->Document();
2108 nsIContent
*root
= document
->GetRootContent();
2110 // But for HTML we want the body element.
2111 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
2113 nsIContent
*bodyContent
= htmlDoc
->GetBodyContentExternal();
2115 root
= bodyContent
; // we can trust the document to hold on to it
2119 nsIFrame
*rootsFrame
=
2120 presContext
->PresShell()->GetPrimaryFrameFor(root
);
2126 return frame
->GetStyleVisibility()->mDirection
!= NS_STYLE_DIRECTION_RTL
;
2130 nsGfxScrollFrameInner::IsScrollbarOnRight() const
2132 nsPresContext
*presContext
= mOuter
->PresContext();
2133 switch (presContext
->GetCachedIntPref(kPresContext_ScrollbarSide
)) {
2135 case 0: // UI directionality
2136 return presContext
->GetCachedIntPref(kPresContext_BidiDirection
)
2137 == IBMBIDI_TEXTDIRECTION_LTR
;
2138 case 1: // Document / content directionality
2140 case 2: // Always right
2142 case 3: // Always left
2148 * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
2149 * cause any of the scrollbars to need to be reflowed.
2152 nsXULScrollFrame::Layout(nsBoxLayoutState
& aState
)
2154 PRBool scrollbarRight
= mInner
.IsScrollbarOnRight();
2155 PRBool scrollbarBottom
= PR_TRUE
;
2157 // get the content rect
2158 nsRect
clientRect(0,0,0,0);
2159 GetClientRect(clientRect
);
2161 // the scroll area size starts off as big as our content area
2162 nsRect
scrollAreaRect(clientRect
);
2165 Our basic strategy here is to first try laying out the content with
2166 the scrollbars in their current state. We're hoping that that will
2167 just "work"; the content will overflow wherever there's a scrollbar
2168 already visible. If that does work, then there's no need to lay out
2169 the scrollarea. Otherwise we fix up the scrollbars; first we add a
2170 vertical one to scroll the content if necessary, or remove it if
2171 it's not needed. Then we reflow the content if the scrollbar
2172 changed. Then we add a horizontal scrollbar if necessary (or
2173 remove if not needed), and if that changed, we reflow the content
2174 again. At this point, any scrollbars that are needed to scroll the
2175 content have been added.
2177 In the second phase we check to see if any scrollbars are too small
2178 to display, and if so, we remove them. We check the horizontal
2179 scrollbar first; removing it might make room for the vertical
2180 scrollbar, and if we have room for just one scrollbar we'll save
2183 Finally we position and size the scrollbars and scrollcorner (the
2184 square that is needed in the corner of the window when two
2185 scrollbars are visible), and reflow any fixed position views
2186 (if we're the viewport and we added or removed a scrollbar).
2189 ScrollbarStyles styles
= GetScrollbarStyles();
2191 // Look at our style do we always have vertical or horizontal scrollbars?
2192 if (styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
)
2193 mInner
.mHasHorizontalScrollbar
= PR_TRUE
;
2194 if (styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
)
2195 mInner
.mHasVerticalScrollbar
= PR_TRUE
;
2197 if (mInner
.mHasHorizontalScrollbar
)
2198 AddHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2200 if (mInner
.mHasVerticalScrollbar
)
2201 AddVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2203 nsRect oldScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
2205 // layout our the scroll area
2206 LayoutScrollArea(aState
, scrollAreaRect
);
2208 // now look at the content area and see if we need scrollbars or not
2209 PRBool needsLayout
= PR_FALSE
;
2211 // if we have 'auto' scrollbars look at the vertical case
2212 if (styles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
) {
2213 // These are only good until the call to LayoutScrollArea.
2214 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollAreaRect
.Size());
2215 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2217 // There are two cases to consider
2218 if (scrolledContentSize
.height
<= scrollAreaRect
.height
2219 || styles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
) {
2220 if (mInner
.mHasVerticalScrollbar
) {
2221 // We left room for the vertical scrollbar, but it's not needed;
2223 RemoveVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2224 needsLayout
= PR_TRUE
;
2227 if (!mInner
.mHasVerticalScrollbar
) {
2228 // We didn't leave room for the vertical scrollbar, but it turns
2230 if (AddVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
))
2231 needsLayout
= PR_TRUE
;
2235 // ok layout at the right size
2237 nsBoxLayoutState
resizeState(aState
);
2238 LayoutScrollArea(resizeState
, scrollAreaRect
);
2239 needsLayout
= PR_FALSE
;
2244 // if scrollbars are auto look at the horizontal case
2245 if (styles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
)
2247 // These are only good until the call to LayoutScrollArea.
2248 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollAreaRect
.Size());
2249 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2251 // if the child is wider that the scroll area
2252 // and we don't have a scrollbar add one.
2253 if (scrolledContentSize
.width
> scrollAreaRect
.width
2254 && styles
.mHorizontal
== NS_STYLE_OVERFLOW_AUTO
) {
2256 if (!mInner
.mHasHorizontalScrollbar
) {
2258 if (AddHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
))
2259 needsLayout
= PR_TRUE
;
2261 // if we added a horizontal scrollbar and we did not have a vertical
2262 // there is a chance that by adding the horizontal scrollbar we will
2263 // suddenly need a vertical scrollbar. Is a special case but its
2265 //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
2266 // printf("****Gfx Scrollbar Special case hit!!*****\n");
2270 // if the area is smaller or equal to and we have a scrollbar then
2272 if (mInner
.mHasHorizontalScrollbar
) {
2273 RemoveHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2274 needsLayout
= PR_TRUE
;
2279 // we only need to set the rect. The inner child stays the same size.
2281 nsBoxLayoutState
resizeState(aState
);
2282 LayoutScrollArea(resizeState
, scrollAreaRect
);
2283 needsLayout
= PR_FALSE
;
2286 // get the preferred size of the scrollbars
2287 nsSize
hMinSize(0, 0);
2288 if (mInner
.mHScrollbarBox
&& mInner
.mHasHorizontalScrollbar
) {
2289 GetScrollbarMetrics(aState
, mInner
.mHScrollbarBox
, &hMinSize
, nsnull
, PR_FALSE
);
2291 nsSize
vMinSize(0, 0);
2292 if (mInner
.mVScrollbarBox
&& mInner
.mHasVerticalScrollbar
) {
2293 GetScrollbarMetrics(aState
, mInner
.mVScrollbarBox
, &vMinSize
, nsnull
, PR_TRUE
);
2296 // Disable scrollbars that are too small
2297 // Disable horizontal scrollbar first. If we have to disable only one
2298 // scrollbar, we'd rather keep the vertical scrollbar.
2299 // Note that we always give horizontal scrollbars their preferred height,
2300 // never their min-height. So check that there's room for the preferred height.
2301 if (mInner
.mHasHorizontalScrollbar
&&
2302 (hMinSize
.width
> clientRect
.width
- vMinSize
.width
2303 || hMinSize
.height
> clientRect
.height
)) {
2304 RemoveHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2305 needsLayout
= PR_TRUE
;
2307 // Now disable vertical scrollbar if necessary
2308 if (mInner
.mHasVerticalScrollbar
&&
2309 (vMinSize
.height
> clientRect
.height
- hMinSize
.height
2310 || vMinSize
.width
> clientRect
.width
)) {
2311 RemoveVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2312 needsLayout
= PR_TRUE
;
2315 // we only need to set the rect. The inner child stays the same size.
2317 nsBoxLayoutState
resizeState(aState
);
2318 LayoutScrollArea(resizeState
, scrollAreaRect
);
2321 if (!mInner
.mSupppressScrollbarUpdate
) {
2322 mInner
.LayoutScrollbars(aState
, clientRect
, oldScrollAreaBounds
, scrollAreaRect
);
2324 if (!mInner
.mPostedReflowCallback
) {
2325 // Make sure we'll try scrolling to restored position
2326 PresContext()->PresShell()->PostReflowCallback(&mInner
);
2327 mInner
.mPostedReflowCallback
= PR_TRUE
;
2329 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
2330 mInner
.mHadNonInitialReflow
= PR_TRUE
;
2333 mInner
.PostOverflowEvent();
2338 nsGfxScrollFrameInner::FinishReflowForScrollbar(nsIContent
* aContent
,
2339 nscoord aMinXY
, nscoord aMaxXY
,
2341 nscoord aPageIncrement
,
2344 // Scrollbars assume zero is the minimum position, so translate for them.
2345 SetCoordAttribute(aContent
, nsGkAtoms::curpos
, aCurPosXY
- aMinXY
);
2346 SetScrollbarEnabled(aContent
, aMaxXY
- aMinXY
);
2347 SetCoordAttribute(aContent
, nsGkAtoms::maxpos
, aMaxXY
- aMinXY
);
2348 SetCoordAttribute(aContent
, nsGkAtoms::pageincrement
, aPageIncrement
);
2349 SetCoordAttribute(aContent
, nsGkAtoms::increment
, aIncrement
);
2353 nsGfxScrollFrameInner::ReflowFinished()
2355 mPostedReflowCallback
= PR_FALSE
;
2357 ScrollToRestoredPosition();
2359 if (NS_SUBTREE_DIRTY(mOuter
) || !mUpdateScrollbarAttributes
)
2362 mUpdateScrollbarAttributes
= PR_FALSE
;
2364 // Update scrollbar attributes.
2365 nsPresContext
* presContext
= mOuter
->PresContext();
2367 if (mMayHaveDirtyFixedChildren
) {
2368 mMayHaveDirtyFixedChildren
= PR_FALSE
;
2369 nsIFrame
* parentFrame
= mOuter
->GetParent();
2370 for (nsIFrame
* fixedChild
=
2371 parentFrame
->GetFirstChild(nsGkAtoms::fixedList
);
2372 fixedChild
; fixedChild
= fixedChild
->GetNextSibling()) {
2373 // force a reflow of the fixed child
2374 presContext
->PresShell()->
2375 FrameNeedsReflow(fixedChild
, nsIPresShell::eResize
,
2376 NS_FRAME_HAS_DIRTY_CHILDREN
);
2380 nsIScrollableView
* scrollable
= GetScrollableView();
2381 nsRect scrollArea
= scrollable
->View()->GetBounds();
2383 const nsStyleFont
* font
= mOuter
->GetStyleFont();
2384 const nsFont
& f
= font
->mFont
;
2385 nsCOMPtr
<nsIFontMetrics
> fm
= presContext
->GetMetricsFor(f
);
2386 nscoord fontHeight
= 1;
2387 NS_ASSERTION(fm
,"FontMetrics is null assuming fontHeight == 1");
2389 fm
->GetHeight(fontHeight
);
2390 scrollable
->SetLineHeight(fontHeight
);
2392 nsRect scrolledContentRect
= GetScrolledRect(scrollArea
.Size());
2393 nscoord minX
= scrolledContentRect
.x
;
2394 nscoord maxX
= scrolledContentRect
.XMost() - scrollArea
.width
;
2395 nscoord minY
= scrolledContentRect
.y
;
2396 nscoord maxY
= scrolledContentRect
.YMost() - scrollArea
.height
;
2398 // Suppress handling of the curpos attribute changes we make here.
2399 NS_ASSERTION(!mFrameInitiatedScroll
, "We shouldn't be reentering here");
2400 mFrameInitiatedScroll
= PR_TRUE
;
2402 nsCOMPtr
<nsIContent
> vScroll
=
2403 mVScrollbarBox
? mVScrollbarBox
->GetContent() : nsnull
;
2404 nsCOMPtr
<nsIContent
> hScroll
=
2405 mHScrollbarBox
? mHScrollbarBox
->GetContent() : nsnull
;
2407 // Note, in some cases mOuter may get deleted while finishing reflow
2409 if (vScroll
|| hScroll
) {
2410 nsWeakFrame
weakFrame(mOuter
);
2411 nscoord curPosX
, curPosY
;
2412 scrollable
->GetScrollPosition(curPosX
, curPosY
);
2414 // We normally use (scrollArea.height - fontHeight) for height
2415 // of page scrolling. However, it is too small when
2416 // fontHeight is very large. (If fontHeight is larger than
2417 // scrollArea.height, direction of scrolling will be opposite).
2418 // To avoid it, we use (float(scrollArea.height) * 0.8) as
2419 // lower bound value of height of page scrolling. (bug 383267)
2420 nscoord pageincrement
= nscoord(scrollArea
.height
- fontHeight
);
2421 nscoord pageincrementMin
= nscoord(float(scrollArea
.height
) * 0.8);
2422 FinishReflowForScrollbar(vScroll
, minY
, maxY
, curPosY
,
2423 PR_MAX(pageincrement
,pageincrementMin
),
2427 FinishReflowForScrollbar(hScroll
, minX
, maxX
, curPosX
,
2428 nscoord(float(scrollArea
.width
) * 0.8),
2429 nsPresContext::CSSPixelsToAppUnits(10));
2431 NS_ENSURE_TRUE(weakFrame
.IsAlive(), PR_FALSE
);
2434 mFrameInitiatedScroll
= PR_FALSE
;
2435 // We used to rely on the curpos attribute changes above to scroll the
2436 // view. However, for scrolling to the left of the viewport, we
2437 // rescale the curpos attribute, which means that operations like
2438 // resizing the window while it is scrolled all the way to the left
2439 // hold the curpos attribute constant at 0 while still requiring
2440 // scrolling. So we suppress the effect of the changes above with
2441 // mFrameInitiatedScroll and call CurPosAttributeChanged here.
2442 // (It actually even works some of the time without this, thanks to
2443 // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
2444 // we hide the scrollbar on a large size change, such as
2446 if (!mHScrollbarBox
&& !mVScrollbarBox
)
2448 CurPosAttributeChanged(mVScrollbarBox
? mVScrollbarBox
->GetContent()
2449 : mHScrollbarBox
->GetContent());
2454 nsGfxScrollFrameInner::ReflowCallbackCanceled()
2456 mPostedReflowCallback
= PR_FALSE
;
2459 static void LayoutAndInvalidate(nsBoxLayoutState
& aState
,
2460 nsIFrame
* aBox
, const nsRect
& aRect
)
2462 // When a child box changes shape of position, the parent
2463 // is responsible for invalidation; the overflow rect must be invalidated
2464 // to make sure to catch any overflow
2465 PRBool rectChanged
= aBox
->GetRect() != aRect
;
2467 aBox
->Invalidate(aBox
->GetOverflowRect());
2468 nsBoxFrame::LayoutChildAt(aState
, aBox
, aRect
);
2470 aBox
->Invalidate(aBox
->GetOverflowRect());
2473 static void AdjustScrollbarRect(nsIView
* aView
, nsPresContext
* aPresContext
,
2474 nsRect
& aRect
, PRBool aVertical
)
2476 if ((aVertical
? aRect
.width
: aRect
.height
) == 0)
2480 nsIWidget
* widget
= aView
->GetNearestWidget(&offset
);
2482 nsIntRect widgetRect
;
2483 if (!widget
->ShowsResizeIndicator(&widgetRect
))
2486 nsRect resizerRect
=
2487 nsRect(aPresContext
->DevPixelsToAppUnits(widgetRect
.x
) - offset
.x
,
2488 aPresContext
->DevPixelsToAppUnits(widgetRect
.y
) - offset
.y
,
2489 aPresContext
->DevPixelsToAppUnits(widgetRect
.width
),
2490 aPresContext
->DevPixelsToAppUnits(widgetRect
.height
));
2492 if (!aRect
.Intersects(resizerRect
))
2496 aRect
.height
= PR_MAX(0, resizerRect
.y
- aRect
.y
);
2498 aRect
.width
= PR_MAX(0, resizerRect
.x
- aRect
.x
);
2502 nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState
& aState
,
2503 const nsRect
& aContentArea
,
2504 const nsRect
& aOldScrollArea
,
2505 const nsRect
& aScrollArea
)
2507 NS_ASSERTION(!mSupppressScrollbarUpdate
,
2508 "This should have been suppressed");
2510 nsIView
* view
= mOuter
->GetView();
2511 nsPresContext
* presContext
= mScrolledFrame
->PresContext();
2512 if (mVScrollbarBox
) {
2513 NS_PRECONDITION(mVScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
2514 nsRect
vRect(aScrollArea
);
2515 vRect
.width
= aContentArea
.width
- aScrollArea
.width
;
2516 vRect
.x
= IsScrollbarOnRight() ? aScrollArea
.XMost() : aContentArea
.x
;
2518 mVScrollbarBox
->GetMargin(margin
);
2519 vRect
.Deflate(margin
);
2520 AdjustScrollbarRect(view
, presContext
, vRect
, PR_TRUE
);
2521 LayoutAndInvalidate(aState
, mVScrollbarBox
, vRect
);
2524 if (mHScrollbarBox
) {
2525 NS_PRECONDITION(mHScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
2526 nsRect
hRect(aScrollArea
);
2527 hRect
.height
= aContentArea
.height
- aScrollArea
.height
;
2528 hRect
.y
= PR_TRUE
? aScrollArea
.YMost() : aContentArea
.y
;
2530 mHScrollbarBox
->GetMargin(margin
);
2531 hRect
.Deflate(margin
);
2532 AdjustScrollbarRect(view
, presContext
, hRect
, PR_FALSE
);
2533 LayoutAndInvalidate(aState
, mHScrollbarBox
, hRect
);
2536 // place the scrollcorner
2537 if (mScrollCornerBox
) {
2538 NS_PRECONDITION(mScrollCornerBox
->IsBoxFrame(), "Must be a box frame!");
2539 nsRect
r(0, 0, 0, 0);
2540 if (aContentArea
.x
!= aScrollArea
.x
) {
2541 // scrollbar (if any) on left
2542 r
.x
= aContentArea
.x
;
2543 r
.width
= aScrollArea
.x
- aContentArea
.x
;
2544 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
2546 // scrollbar (if any) on right
2547 r
.x
= aScrollArea
.XMost();
2548 r
.width
= aContentArea
.XMost() - aScrollArea
.XMost();
2549 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
2551 if (aContentArea
.y
!= aScrollArea
.y
) {
2552 // scrollbar (if any) on top
2553 r
.y
= aContentArea
.y
;
2554 r
.height
= aScrollArea
.y
- aContentArea
.y
;
2555 NS_ASSERTION(r
.height
>= 0, "Scroll area should be inside client rect");
2557 // scrollbar (if any) on bottom
2558 r
.y
= aScrollArea
.YMost();
2559 r
.height
= aContentArea
.YMost() - aScrollArea
.YMost();
2560 NS_ASSERTION(r
.height
>= 0, "Scroll area should be inside client rect");
2562 LayoutAndInvalidate(aState
, mScrollCornerBox
, r
);
2565 // may need to update fixed position children of the viewport,
2566 // if the client area changed size because of an incremental
2567 // reflow of a descendant. (If the outer frame is dirty, the fixed
2568 // children will be re-laid out anyway)
2569 if (aOldScrollArea
.Size() != aScrollArea
.Size() &&
2570 !(mOuter
->GetStateBits() & NS_FRAME_IS_DIRTY
) &&
2572 mMayHaveDirtyFixedChildren
= PR_TRUE
;
2575 // post reflow callback to modify scrollbar attributes
2576 mUpdateScrollbarAttributes
= PR_TRUE
;
2577 if (!mPostedReflowCallback
) {
2578 aState
.PresContext()->PresShell()->PostReflowCallback(this);
2579 mPostedReflowCallback
= PR_TRUE
;
2584 nsGfxScrollFrameInner::ScrollbarChanged(nsPresContext
* aPresContext
, nscoord aX
, nscoord aY
, PRUint32 aFlags
)
2586 nsIScrollableView
* scrollable
= GetScrollableView();
2587 scrollable
->ScrollTo(aX
, aY
, aFlags
);
2588 // printf("scrolling to: %d, %d\n", aX, aY);
2592 nsGfxScrollFrameInner::SetScrollbarEnabled(nsIContent
* aContent
, nscoord aMaxPos
)
2595 aContent
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
, PR_TRUE
);
2597 aContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
,
2598 NS_LITERAL_STRING("true"), PR_TRUE
);
2603 nsGfxScrollFrameInner::SetCoordAttribute(nsIContent
* aContent
, nsIAtom
* aAtom
,
2606 // convert to pixels
2607 aSize
= nsPresContext::AppUnitsToIntCSSPixels(aSize
);
2609 // only set the attribute if it changed.
2611 nsAutoString newValue
;
2612 newValue
.AppendInt(aSize
);
2614 if (aContent
->AttrValueIs(kNameSpaceID_None
, aAtom
, newValue
, eCaseMatters
))
2617 aContent
->SetAttr(kNameSpaceID_None
, aAtom
, newValue
, PR_TRUE
);
2621 nsGfxScrollFrameInner::GetScrolledRect(const nsSize
& aScrollPortSize
) const
2623 nsRect result
= mScrolledFrame
->GetOverflowRect();
2624 nscoord x1
= result
.x
, x2
= result
.XMost(),
2625 y1
= result
.y
, y2
= result
.YMost();
2628 if (IsLTR() || mIsXUL
) {
2632 if (x2
> aScrollPortSize
.width
)
2633 x2
= aScrollPortSize
.width
;
2634 // When the scrolled frame chooses a size larger than its available width (because
2635 // its padding alone is larger than the available width), we need to keep the
2636 // start-edge of the scroll frame anchored to the start-edge of the scrollport.
2637 // When the scrolled frame is RTL, this means moving it in our left-based
2638 // coordinate system, so we need to compensate for its extra width here by
2639 // effectively repositioning the frame.
2640 nscoord extraWidth
= PR_MAX(0, mScrolledFrame
->GetSize().width
- aScrollPortSize
.width
);
2644 return nsRect(x1
, y1
, x2
- x1
, y2
- y1
);
2648 nsGfxScrollFrameInner::GetActualScrollbarSizes() const {
2650 mOuter
->GetBorder(border
);
2651 nsRect
r(nsPoint(0,0), mOuter
->GetSize());
2653 nsRect scrollArea
= mScrollableView
->View()->GetBounds();
2655 return nsMargin(scrollArea
.x
- r
.x
, scrollArea
.y
- r
.y
,
2656 r
.XMost() - scrollArea
.XMost(),
2657 r
.YMost() - scrollArea
.YMost());
2661 nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox
* aScrollbar
, PRBool aVisible
)
2666 nsIScrollbarFrame
* scrollbar
= do_QueryFrame(aScrollbar
);
2668 // See if we have a mediator.
2669 nsIScrollbarMediator
* mediator
= scrollbar
->GetScrollbarMediator();
2671 // Inform the mediator of the visibility change.
2672 mediator
->VisibilityChanged(aVisible
);
2678 nsGfxScrollFrameInner::GetCoordAttribute(nsIBox
* aBox
, nsIAtom
* atom
, PRInt32 defaultValue
)
2681 nsIContent
* content
= aBox
->GetContent();
2684 content
->GetAttr(kNameSpaceID_None
, atom
, value
);
2685 if (!value
.IsEmpty())
2689 // convert it to an integer
2690 defaultValue
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
2694 return defaultValue
;
2697 static nsIURI
* GetDocURI(nsIFrame
* aFrame
)
2699 nsIPresShell
* shell
= aFrame
->PresContext()->GetPresShell();
2702 nsIDocument
* doc
= shell
->GetDocument();
2705 return doc
->GetDocumentURI();
2709 nsGfxScrollFrameInner::SaveVScrollbarStateToGlobalHistory()
2711 NS_ASSERTION(mIsRoot
, "Only use this on viewports");
2713 // If the hint is the same as the one we loaded, don't bother
2715 if (mDidLoadHistoryVScrollbarHint
&&
2716 (mHistoryVScrollbarHint
== mHasVerticalScrollbar
))
2719 nsIURI
* uri
= GetDocURI(mOuter
);
2723 nsCOMPtr
<nsIGlobalHistory3
> history(do_GetService(NS_GLOBALHISTORY2_CONTRACTID
));
2728 if (mHasVerticalScrollbar
) {
2729 flags
|= NS_GECKO_FLAG_NEEDS_VERTICAL_SCROLLBAR
;
2731 history
->SetURIGeckoFlags(uri
, flags
);
2732 // if it fails, we don't care
2736 nsGfxScrollFrameInner::GetVScrollbarHintFromGlobalHistory(PRBool
* aVScrollbarNeeded
)
2738 NS_ASSERTION(mIsRoot
, "Only use this on viewports");
2739 NS_ASSERTION(!mDidLoadHistoryVScrollbarHint
,
2740 "Should only load a hint once, it can be expensive");
2742 nsIURI
* uri
= GetDocURI(mOuter
);
2744 return NS_ERROR_FAILURE
;
2746 nsCOMPtr
<nsIGlobalHistory3
> history(do_GetService(NS_GLOBALHISTORY2_CONTRACTID
));
2748 return NS_ERROR_FAILURE
;
2751 nsresult rv
= history
->GetURIGeckoFlags(uri
, &flags
);
2755 *aVScrollbarNeeded
= (flags
& NS_GECKO_FLAG_NEEDS_VERTICAL_SCROLLBAR
) != 0;
2756 mDidLoadHistoryVScrollbarHint
= PR_TRUE
;
2757 mHistoryVScrollbarHint
= *aVScrollbarNeeded
;
2762 nsGfxScrollFrameInner::SaveState(nsIStatefulFrame::SpecialStateID aStateID
)
2764 // Don't save "normal" state for the root scrollframe; that's
2765 // handled via the eDocumentScrollState state id
2766 if (mIsRoot
&& aStateID
== nsIStatefulFrame::eNoID
) {
2770 nsIScrollbarMediator
* mediator
= do_QueryFrame(GetScrolledFrame());
2772 // child handles its own scroll state, so don't bother saving state here
2776 nsIScrollableView
* scrollingView
= GetScrollableView();
2778 scrollingView
->GetScrollPosition(x
,y
);
2779 // Don't save scroll position if we are at (0,0)
2784 nsIView
* child
= nsnull
;
2785 scrollingView
->GetScrolledView(child
);
2790 nsRect childRect
= child
->GetBounds();
2793 nsPresState
* state
= new nsPresState();
2798 state
->SetScrollState(childRect
);
2804 nsGfxScrollFrameInner::RestoreState(nsPresState
* aState
)
2806 mRestoreRect
= aState
->GetScrollState();
2809 mDidHistoryRestore
= PR_TRUE
;
2810 nsIScrollableView
* scrollingView
= GetScrollableView();
2811 if (scrollingView
) {
2812 scrollingView
->GetScrollPosition(mLastPos
.x
, mLastPos
.y
);
2814 mLastPos
= nsPoint(0, 0);