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"
82 #include "nsFrameManager.h"
84 //----------------------------------------------------------------------
86 //----------nsHTMLScrollFrame-------------------------------------------
89 NS_NewHTMLScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
91 return new (aPresShell
) nsHTMLScrollFrame(aPresShell
, aContext
, aIsRoot
);
94 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLScrollFrame
)
96 nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
97 : nsHTMLContainerFrame(aContext
),
98 mInner(this, aIsRoot
, PR_FALSE
)
103 * Get the view that we are scrolling within the scrolling view.
106 nsIFrame
* nsHTMLScrollFrame::GetScrolledFrame() const
108 return mInner
.GetScrolledFrame();
111 nsIScrollableView
* nsHTMLScrollFrame::GetScrollableView()
113 return mInner
.GetScrollableView();
116 nsPoint
nsHTMLScrollFrame::GetScrollPosition() const
118 nsIScrollableView
* s
= mInner
.GetScrollableView();
119 nsPoint scrollPosition
;
120 s
->GetScrollPosition(scrollPosition
.x
, scrollPosition
.y
);
121 return scrollPosition
;
124 void nsHTMLScrollFrame::ScrollTo(nsPoint aScrollPosition
, PRUint32 aFlags
)
126 nsIScrollableView
* s
= mInner
.GetScrollableView();
127 s
->ScrollTo(aScrollPosition
.x
, aScrollPosition
.y
, aFlags
);
130 nsGfxScrollFrameInner::ScrollbarStyles
131 nsHTMLScrollFrame::GetScrollbarStyles() const {
132 return mInner
.GetScrollbarStylesFromFrame();
135 nsMargin
nsHTMLScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
136 return mInner
.GetDesiredScrollbarSizes(aState
);
139 void nsHTMLScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible
, PRBool aHorizontalVisible
)
141 mInner
.mNeverHasVerticalScrollbar
= !aVerticalVisible
;
142 mInner
.mNeverHasHorizontalScrollbar
= !aHorizontalVisible
;
145 nsIBox
* nsHTMLScrollFrame::GetScrollbarBox(PRBool aVertical
)
147 return aVertical
? mInner
.mVScrollbarBox
: mInner
.mHScrollbarBox
;
151 nsHTMLScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
153 return mInner
.CreateAnonymousContent(aElements
);
157 nsHTMLScrollFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
160 nsHTMLContainerFrame::DestroyFrom(aDestructRoot
);
164 nsHTMLScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
165 nsFrameList
& aChildList
)
167 nsresult rv
= nsHTMLContainerFrame::SetInitialChildList(aListName
, aChildList
);
168 mInner
.CreateScrollableView();
169 mInner
.ReloadChildFrames();
171 // listen for scroll events.
172 mInner
.GetScrollableView()->AddScrollPositionListener(&mInner
);
179 nsHTMLScrollFrame::AppendFrames(nsIAtom
* aListName
,
180 nsFrameList
& aFrameList
)
182 NS_ASSERTION(!aListName
, "Only main list supported");
183 mFrames
.AppendFrames(nsnull
, aFrameList
);
184 mInner
.ReloadChildFrames();
189 nsHTMLScrollFrame::InsertFrames(nsIAtom
* aListName
,
190 nsIFrame
* aPrevFrame
,
191 nsFrameList
& aFrameList
)
193 NS_ASSERTION(!aListName
, "Only main list supported");
194 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
195 "inserting after sibling frame with different parent");
196 mFrames
.InsertFrames(nsnull
, aPrevFrame
, aFrameList
);
197 mInner
.ReloadChildFrames();
202 nsHTMLScrollFrame::RemoveFrame(nsIAtom
* aListName
,
205 NS_ASSERTION(!aListName
, "Only main list supported");
206 mFrames
.DestroyFrame(aOldFrame
);
207 mInner
.ReloadChildFrames();
212 nsHTMLScrollFrame::GetSplittableType() const
214 return NS_FRAME_NOT_SPLITTABLE
;
218 nsHTMLScrollFrame::GetSkipSides() const
224 nsHTMLScrollFrame::GetType() const
226 return nsGkAtoms::scrollFrame
;
230 nsHTMLScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
231 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
235 if (aForChild
== mInner
.mScrolledFrame
) {
236 // restrict aDamageRect to the scrollable view's bounds
237 nsRect damage
= aDamageRect
+ nsPoint(aX
, aY
);
239 if (r
.IntersectRect(damage
, mInner
.mScrollableView
->View()->GetBounds())) {
240 nsHTMLContainerFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
242 if (mInner
.mIsRoot
&& r
!= damage
) {
243 // Make sure we notify our prescontext about invalidations outside
244 // viewport clipping.
245 // This is important for things that are snapshotting the viewport,
246 // possibly outside the scrolled bounds.
247 // We don't need to propagate this any further up, though. Anyone who
248 // cares about scrolled-out-of-view invalidates had better be listening
249 // to our window directly.
250 PresContext()->NotifyInvalidation(damage
, aFlags
);
253 } else if (aForChild
== mInner
.mHScrollbarBox
) {
254 if (!mInner
.mHasHorizontalScrollbar
) {
255 // Our scrollbars may send up invalidations even when they're collapsed,
256 // because we just size a collapsed scrollbar to empty and some
257 // descendants may be non-empty. Suppress that invalidation here.
260 } else if (aForChild
== mInner
.mVScrollbarBox
) {
261 if (!mInner
.mHasVerticalScrollbar
) {
262 // Our scrollbars may send up invalidations even when they're collapsed,
263 // because we just size a collapsed scrollbar to empty and some
264 // descendants may be non-empty. Suppress that invalidation here.
270 nsHTMLContainerFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
274 HTML scrolling implementation
276 All other things being equal, we prefer layouts with fewer scrollbars showing.
279 struct ScrollReflowState
{
280 const nsHTMLReflowState
& mReflowState
;
281 nsBoxLayoutState mBoxState
;
282 nsGfxScrollFrameInner::ScrollbarStyles mStyles
;
283 nsMargin mComputedBorder
;
285 // === Filled in by ReflowScrolledFrame ===
286 PRPackedBool mReflowedContentsWithHScrollbar
;
287 PRPackedBool mReflowedContentsWithVScrollbar
;
289 // === Filled in when TryLayout succeeds ===
290 // The area of the scrollport, in coordinates relative to the scrollframe
291 nsRect mScrollPortRect
;
292 // The size of the inside-border area
293 nsSize mInsideBorderSize
;
294 // Whether we decided to show the horizontal scrollbar
295 PRPackedBool mShowHScrollbar
;
296 // Whether we decided to show the vertical scrollbar
297 PRPackedBool mShowVScrollbar
;
299 ScrollReflowState(nsIScrollableFrame
* aFrame
,
300 const nsHTMLReflowState
& aState
) :
301 mReflowState(aState
),
302 // mBoxState is just used for scrollbars so we don't need to
303 // worry about the reflow depth here
304 mBoxState(aState
.frame
->PresContext(), aState
.rendContext
, 0),
305 mStyles(aFrame
->GetScrollbarStyles()) {
309 // XXXldb Can this go away?
310 static nsSize
ComputeInsideBorderSize(ScrollReflowState
* aState
,
311 const nsSize
& aDesiredInsideBorderSize
)
313 // aDesiredInsideBorderSize is the frame size; i.e., it includes
314 // borders and padding (but the scrolled child doesn't have
315 // borders). The scrolled child has the same padding as us.
316 nscoord contentWidth
= aState
->mReflowState
.ComputedWidth();
317 if (contentWidth
== NS_UNCONSTRAINEDSIZE
) {
318 contentWidth
= aDesiredInsideBorderSize
.width
-
319 aState
->mReflowState
.mComputedPadding
.LeftRight();
321 nscoord contentHeight
= aState
->mReflowState
.ComputedHeight();
322 if (contentHeight
== NS_UNCONSTRAINEDSIZE
) {
323 contentHeight
= aDesiredInsideBorderSize
.height
-
324 aState
->mReflowState
.mComputedPadding
.TopBottom();
327 aState
->mReflowState
.ApplyMinMaxConstraints(&contentWidth
, &contentHeight
);
328 return nsSize(contentWidth
+ aState
->mReflowState
.mComputedPadding
.LeftRight(),
329 contentHeight
+ aState
->mReflowState
.mComputedPadding
.TopBottom());
333 GetScrollbarMetrics(nsBoxLayoutState
& aState
, nsIBox
* aBox
, nsSize
* aMin
,
334 nsSize
* aPref
, PRBool aVertical
)
336 NS_ASSERTION(aState
.GetRenderingContext(),
337 "Must have rendering context in layout state for size "
341 *aMin
= aBox
->GetMinSize(aState
);
342 nsBox::AddMargin(aBox
, *aMin
);
346 *aPref
= aBox
->GetPrefSize(aState
);
347 nsBox::AddMargin(aBox
, *aPref
);
352 * Assuming that we know the metrics for our wrapped frame and
353 * whether the horizontal and/or vertical scrollbars are present,
354 * compute the resulting layout and return PR_TRUE if the layout is
355 * consistent. If the layout is consistent then we fill in the
356 * computed fields of the ScrollReflowState.
358 * The layout is consistent when both scrollbars are showing if and only
359 * if they should be showing. A horizontal scrollbar should be showing if all
360 * following conditions are met:
361 * 1) the style is not HIDDEN
362 * 2) our inside-border height is at least the scrollbar height (i.e., the
363 * scrollbar fits vertically)
364 * 3) our scrollport width (the inside-border width minus the width allocated for a
365 * vertical scrollbar, if showing) is at least the scrollbar's min-width
366 * (i.e., the scrollbar fits horizontally)
367 * 4) the style is SCROLL, or the kid's overflow-area XMost is
368 * greater than the scrollport width
370 * @param aForce if PR_TRUE, then we just assume the layout is consistent.
373 nsHTMLScrollFrame::TryLayout(ScrollReflowState
* aState
,
374 nsHTMLReflowMetrics
* aKidMetrics
,
375 PRBool aAssumeHScroll
, PRBool aAssumeVScroll
,
376 PRBool aForce
, nsresult
* aResult
)
380 if ((aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeVScroll
) ||
381 (aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_HIDDEN
&& aAssumeHScroll
)) {
382 NS_ASSERTION(!aForce
, "Shouldn't be forcing a hidden scrollbar to show!");
386 if (aAssumeVScroll
!= aState
->mReflowedContentsWithVScrollbar
||
387 (aAssumeHScroll
!= aState
->mReflowedContentsWithHScrollbar
&&
388 ScrolledContentDependsOnHeight(aState
))) {
389 nsresult rv
= ReflowScrolledFrame(aState
, aAssumeHScroll
, aAssumeVScroll
,
390 aKidMetrics
, PR_FALSE
);
397 nsSize
vScrollbarMinSize(0, 0);
398 nsSize
vScrollbarPrefSize(0, 0);
399 if (mInner
.mVScrollbarBox
) {
400 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mVScrollbarBox
,
402 aAssumeVScroll
? &vScrollbarPrefSize
: nsnull
, PR_TRUE
);
404 nscoord vScrollbarDesiredWidth
= aAssumeVScroll
? vScrollbarPrefSize
.width
: 0;
405 nscoord vScrollbarMinHeight
= aAssumeVScroll
? vScrollbarMinSize
.height
: 0;
407 nsSize
hScrollbarMinSize(0, 0);
408 nsSize
hScrollbarPrefSize(0, 0);
409 if (mInner
.mHScrollbarBox
) {
410 GetScrollbarMetrics(aState
->mBoxState
, mInner
.mHScrollbarBox
,
412 aAssumeHScroll
? &hScrollbarPrefSize
: nsnull
, PR_FALSE
);
414 nscoord hScrollbarDesiredHeight
= aAssumeHScroll
? hScrollbarPrefSize
.height
: 0;
415 nscoord hScrollbarMinWidth
= aAssumeHScroll
? hScrollbarMinSize
.width
: 0;
417 // First, compute our inside-border size and scrollport size
418 // XXXldb Can we depend more on ComputeSize here?
419 nsSize desiredInsideBorderSize
;
420 desiredInsideBorderSize
.width
= vScrollbarDesiredWidth
+
421 NS_MAX(aKidMetrics
->width
, hScrollbarMinWidth
);
422 desiredInsideBorderSize
.height
= hScrollbarDesiredHeight
+
423 NS_MAX(aKidMetrics
->height
, vScrollbarMinHeight
);
424 aState
->mInsideBorderSize
=
425 ComputeInsideBorderSize(aState
, desiredInsideBorderSize
);
426 nsSize scrollPortSize
= nsSize(NS_MAX(0, aState
->mInsideBorderSize
.width
- vScrollbarDesiredWidth
),
427 NS_MAX(0, aState
->mInsideBorderSize
.height
- hScrollbarDesiredHeight
));
430 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollPortSize
);
431 nscoord oneDevPixel
= aState
->mBoxState
.PresContext()->DevPixelsToAppUnits(1);
433 // If the style is HIDDEN then we already know that aAssumeHScroll is PR_FALSE
434 if (aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
) {
435 PRBool wantHScrollbar
=
436 aState
->mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
||
437 scrolledRect
.XMost() >= scrollPortSize
.width
+ oneDevPixel
||
438 scrolledRect
.x
<= -oneDevPixel
;
439 if (aState
->mInsideBorderSize
.height
< hScrollbarMinSize
.height
||
440 scrollPortSize
.width
< hScrollbarMinSize
.width
)
441 wantHScrollbar
= PR_FALSE
;
442 if (wantHScrollbar
!= aAssumeHScroll
)
446 // If the style is HIDDEN then we already know that aAssumeVScroll is PR_FALSE
447 if (aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
) {
448 PRBool wantVScrollbar
=
449 aState
->mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
||
450 scrolledRect
.YMost() >= scrollPortSize
.height
+ oneDevPixel
||
451 scrolledRect
.y
<= -oneDevPixel
;
452 if (aState
->mInsideBorderSize
.width
< vScrollbarMinSize
.width
||
453 scrollPortSize
.height
< vScrollbarMinSize
.height
)
454 wantVScrollbar
= PR_FALSE
;
455 if (wantVScrollbar
!= aAssumeVScroll
)
460 nscoord vScrollbarActualWidth
= aState
->mInsideBorderSize
.width
- scrollPortSize
.width
;
462 aState
->mShowHScrollbar
= aAssumeHScroll
;
463 aState
->mShowVScrollbar
= aAssumeVScroll
;
464 nsPoint
scrollPortOrigin(aState
->mComputedBorder
.left
,
465 aState
->mComputedBorder
.top
);
466 if (!mInner
.IsScrollbarOnRight()) {
467 scrollPortOrigin
.x
+= vScrollbarActualWidth
;
469 aState
->mScrollPortRect
= nsRect(scrollPortOrigin
, scrollPortSize
);
474 nsHTMLScrollFrame::ScrolledContentDependsOnHeight(ScrollReflowState
* aState
)
476 // Return true if ReflowScrolledFrame is going to do something different
477 // based on the presence of a horizontal scrollbar.
478 return (mInner
.mScrolledFrame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
) ||
479 aState
->mReflowState
.ComputedHeight() != NS_UNCONSTRAINEDSIZE
||
480 aState
->mReflowState
.mComputedMinHeight
> 0 ||
481 aState
->mReflowState
.mComputedMaxHeight
!= NS_UNCONSTRAINEDSIZE
;
485 nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState
* aState
,
486 PRBool aAssumeHScroll
,
487 PRBool aAssumeVScroll
,
488 nsHTMLReflowMetrics
* aMetrics
,
491 // these could be NS_UNCONSTRAINEDSIZE ... NS_MIN arithmetic should
493 nscoord paddingLR
= aState
->mReflowState
.mComputedPadding
.LeftRight();
495 nscoord availWidth
= aState
->mReflowState
.ComputedWidth() + paddingLR
;
497 nscoord computedHeight
= aState
->mReflowState
.ComputedHeight();
498 nscoord computedMinHeight
= aState
->mReflowState
.mComputedMinHeight
;
499 nscoord computedMaxHeight
= aState
->mReflowState
.mComputedMaxHeight
;
500 if (!ShouldPropagateComputedHeightToScrolledContent()) {
501 computedHeight
= NS_UNCONSTRAINEDSIZE
;
502 computedMinHeight
= 0;
503 computedMaxHeight
= NS_UNCONSTRAINEDSIZE
;
505 if (aAssumeHScroll
) {
506 nsSize hScrollbarPrefSize
=
507 mInner
.mHScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
508 if (computedHeight
!= NS_UNCONSTRAINEDSIZE
)
509 computedHeight
= NS_MAX(0, computedHeight
- hScrollbarPrefSize
.height
);
510 computedMinHeight
= NS_MAX(0, computedMinHeight
- hScrollbarPrefSize
.height
);
511 if (computedMaxHeight
!= NS_UNCONSTRAINEDSIZE
)
512 computedMaxHeight
= NS_MAX(0, computedMaxHeight
- hScrollbarPrefSize
.height
);
515 if (aAssumeVScroll
) {
516 nsSize vScrollbarPrefSize
=
517 mInner
.mVScrollbarBox
->GetPrefSize(const_cast<nsBoxLayoutState
&>(aState
->mBoxState
));
518 availWidth
= NS_MAX(0, availWidth
- vScrollbarPrefSize
.width
);
521 // We're forcing the padding on our scrolled frame, so let it know what that
523 mInner
.mScrolledFrame
->
524 SetProperty(nsGkAtoms::usedPaddingProperty
,
525 new nsMargin(aState
->mReflowState
.mComputedPadding
),
526 nsCSSOffsetState::DestroyMarginFunc
);
528 nsPresContext
* presContext
= PresContext();
529 // Pass PR_FALSE for aInit so we can pass in the correct padding
530 nsHTMLReflowState
kidReflowState(presContext
, aState
->mReflowState
,
531 mInner
.mScrolledFrame
,
532 nsSize(availWidth
, NS_UNCONSTRAINEDSIZE
),
534 kidReflowState
.Init(presContext
, -1, -1, nsnull
,
535 &aState
->mReflowState
.mComputedPadding
);
536 kidReflowState
.mFlags
.mAssumingHScrollbar
= aAssumeHScroll
;
537 kidReflowState
.mFlags
.mAssumingVScrollbar
= aAssumeVScroll
;
538 kidReflowState
.SetComputedHeight(computedHeight
);
539 kidReflowState
.mComputedMinHeight
= computedMinHeight
;
540 kidReflowState
.mComputedMaxHeight
= computedMaxHeight
;
542 nsReflowStatus status
;
543 nsresult rv
= ReflowChild(mInner
.mScrolledFrame
, presContext
, *aMetrics
,
544 kidReflowState
, 0, 0,
545 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
, status
);
546 // Don't resize or position the view because we're going to resize
547 // it to the correct size anyway in PlaceScrollArea. Allowing it to
548 // resize here would size it to the natural height of the frame,
549 // which will usually be different from the scrollport height;
550 // invalidating the difference will cause unnecessary repainting.
551 FinishReflowChild(mInner
.mScrolledFrame
, presContext
,
552 &kidReflowState
, *aMetrics
, 0, 0,
553 NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
| NS_FRAME_NO_SIZE_VIEW
);
555 // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
556 // setting their mOverflowArea. This is wrong because every frame should
557 // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
558 // support the 'outline' property because of this. Rather than fix the world
559 // right now, just fix up the overflow area if necessary. Note that we don't
560 // check HasOverflowRect() because it could be set even though the
561 // overflow area doesn't include the frame bounds.
562 aMetrics
->mOverflowArea
.UnionRect(aMetrics
->mOverflowArea
,
563 nsRect(0, 0, aMetrics
->width
, aMetrics
->height
));
565 aState
->mReflowedContentsWithHScrollbar
= aAssumeHScroll
;
566 aState
->mReflowedContentsWithVScrollbar
= aAssumeVScroll
;
572 nsHTMLScrollFrame::GuessHScrollbarNeeded(const ScrollReflowState
& aState
)
574 if (aState
.mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_AUTO
)
575 // no guessing required
576 return aState
.mStyles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
;
578 return mInner
.mHasHorizontalScrollbar
;
582 nsHTMLScrollFrame::GuessVScrollbarNeeded(const ScrollReflowState
& aState
)
584 if (aState
.mStyles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
)
585 // no guessing required
586 return aState
.mStyles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
;
588 // If we've had at least one non-initial reflow, then just assume
589 // the state of the vertical scrollbar will be what we determined
591 if (mInner
.mHadNonInitialReflow
) {
592 return mInner
.mHasVerticalScrollbar
;
595 // If this is the initial reflow, guess PR_FALSE because usually
596 // we have very little content by then.
597 if (InInitialReflow())
600 if (mInner
.mIsRoot
) {
601 // For viewports, try getting a hint from global history
602 // as to whether we had a vertical scrollbar last time.
604 nsresult rv
= mInner
.GetVScrollbarHintFromGlobalHistory(&hint
);
605 if (NS_SUCCEEDED(rv
))
607 // No hint. Assume that there will be a scrollbar; it seems to me
608 // that 'most pages' do have a scrollbar, and anyway, it's cheaper
609 // to do an extra reflow for the pages that *don't* need a
610 // scrollbar (because on average they will have less content).
614 // For non-viewports, just guess that we don't need a scrollbar.
615 // XXX I wonder if statistically this is the right idea; I'm
616 // basically guessing that there are a lot of overflow:auto DIVs
617 // that get their intrinsic size and don't overflow
622 nsHTMLScrollFrame::InInitialReflow() const
624 // We're in an initial reflow if NS_FRAME_FIRST_REFLOW is set, unless we're a
625 // root scrollframe. In that case we want to skip this clause altogether.
626 // The guess here is that there are lots of overflow:auto divs out there that
627 // end up auto-sizing so they don't overflow, and that the root basically
628 // always needs a scrollbar if it did last time we loaded this page (good
629 // assumption, because our initial reflow is no longer synchronous).
630 return !mInner
.mIsRoot
&& (GetStateBits() & NS_FRAME_FIRST_REFLOW
);
634 nsHTMLScrollFrame::ReflowContents(ScrollReflowState
* aState
,
635 const nsHTMLReflowMetrics
& aDesiredSize
)
637 nsHTMLReflowMetrics
kidDesiredSize(aDesiredSize
.mFlags
);
638 nsresult rv
= ReflowScrolledFrame(aState
, GuessHScrollbarNeeded(*aState
),
639 GuessVScrollbarNeeded(*aState
), &kidDesiredSize
, PR_TRUE
);
640 NS_ENSURE_SUCCESS(rv
, rv
);
642 // There's an important special case ... if the child appears to fit
643 // in the inside-border rect (but overflows the scrollport), we
644 // should try laying it out without a vertical scrollbar. It will
645 // usually fit because making the available-width wider will not
646 // normally make the child taller. (The only situation I can think
647 // of is when you have a line containing %-width inline replaced
648 // elements whose percentages sum to more than 100%, so increasing
649 // the available width makes the line break where it was fitting
650 // before.) If we don't treat this case specially, then we will
651 // decide that showing scrollbars is OK because the content
652 // overflows when we're showing scrollbars and we won't try to
653 // remove the vertical scrollbar.
655 // Detecting when we enter this special case is important for when
656 // people design layouts that exactly fit the container "most of the
659 // XXX Is this check really sufficient to catch all the incremental cases
660 // where the ideal case doesn't have a scrollbar?
661 if ((aState
->mReflowedContentsWithHScrollbar
|| aState
->mReflowedContentsWithVScrollbar
) &&
662 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
&&
663 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
) {
664 nsSize insideBorderSize
=
665 ComputeInsideBorderSize(aState
,
666 nsSize(kidDesiredSize
.width
, kidDesiredSize
.height
));
667 nsRect scrolledRect
= mInner
.GetScrolledRect(insideBorderSize
);
668 if (nsRect(nsPoint(0, 0), insideBorderSize
).Contains(scrolledRect
)) {
669 // Let's pretend we had no scrollbars coming in here
670 rv
= ReflowScrolledFrame(aState
, PR_FALSE
, PR_FALSE
,
671 &kidDesiredSize
, PR_FALSE
);
672 NS_ENSURE_SUCCESS(rv
, rv
);
676 // Try vertical scrollbar settings that leave the vertical scrollbar unchanged.
677 // Do this first because changing the vertical scrollbar setting is expensive,
678 // forcing a reflow always.
680 // Try leaving the horizontal scrollbar unchanged first. This will be more
682 if (TryLayout(aState
, &kidDesiredSize
, aState
->mReflowedContentsWithHScrollbar
,
683 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
685 if (TryLayout(aState
, &kidDesiredSize
, !aState
->mReflowedContentsWithHScrollbar
,
686 aState
->mReflowedContentsWithVScrollbar
, PR_FALSE
, &rv
))
689 // OK, now try toggling the vertical scrollbar. The performance advantage
690 // of trying the status-quo horizontal scrollbar state
691 // does not exist here (we'll have to reflow due to the vertical scrollbar
692 // change), so always try no horizontal scrollbar first.
693 PRBool newVScrollbarState
= !aState
->mReflowedContentsWithVScrollbar
;
694 if (TryLayout(aState
, &kidDesiredSize
, PR_FALSE
, newVScrollbarState
, PR_FALSE
, &rv
))
696 if (TryLayout(aState
, &kidDesiredSize
, PR_TRUE
, newVScrollbarState
, PR_FALSE
, &rv
))
699 // OK, we're out of ideas. Try again enabling whatever scrollbars we can
700 // enable and force the layout to stick even if it's inconsistent.
701 // This just happens sometimes.
702 TryLayout(aState
, &kidDesiredSize
,
703 aState
->mStyles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
,
704 aState
->mStyles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
,
710 nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState
& aState
)
712 nsIView
* scrollView
= mInner
.mScrollableView
->View();
713 nsIViewManager
* vm
= scrollView
->GetViewManager();
714 vm
->MoveViewTo(scrollView
, aState
.mScrollPortRect
.x
, aState
.mScrollPortRect
.y
);
715 vm
->ResizeView(scrollView
, nsRect(nsPoint(0, 0), aState
.mScrollPortRect
.Size()),
718 nsIFrame
*scrolledFrame
= mInner
.mScrolledFrame
;
719 nsIView
*scrolledView
= scrolledFrame
->GetView();
720 // Set the x,y of the scrolled frame to the correct value: the displacement
721 // from its origin to the origin of this frame
722 scrolledFrame
->SetPosition(scrolledView
->GetOffsetTo(GetView()));
725 // Preserve the width or height of empty rects
726 scrolledArea
.UnionRectIncludeEmpty(mInner
.GetScrolledRect(aState
.mScrollPortRect
.Size()),
727 nsRect(nsPoint(0,0), aState
.mScrollPortRect
.Size()));
729 // Store the new overflow area. Note that this changes where an outline
730 // of the scrolled frame would be painted, but scrolled frames can't have
731 // outlines (the outline would go on this scrollframe instead).
732 // Using FinishAndStoreOverflow is needed so the overflow rect
733 // gets set correctly. It also messes with the overflow rect in the
734 // -moz-hidden-unscrollable case, but scrolled frames can't have
735 // 'overflow' either.
736 // This needs to happen before SyncFrameViewAfterReflow so
737 // HasOverflowRect() will return the correct value.
738 scrolledFrame
->FinishAndStoreOverflow(&scrolledArea
,
739 scrolledFrame
->GetSize());
741 // Note that making the view *exactly* the size of the scrolled area
742 // is critical, since the view scrolling code uses the size of the
743 // scrolled view to clamp scroll requests.
744 nsContainerFrame::SyncFrameViewAfterReflow(scrolledFrame
->PresContext(),
748 NS_FRAME_NO_MOVE_VIEW
);
752 nsHTMLScrollFrame::GetIntrinsicVScrollbarWidth(nsIRenderingContext
*aRenderingContext
)
754 nsGfxScrollFrameInner::ScrollbarStyles ss
= GetScrollbarStyles();
755 if (ss
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
|| !mInner
.mVScrollbarBox
)
758 // Don't need to worry about reflow depth here since it's
759 // just for scrollbars
760 nsBoxLayoutState
bls(PresContext(), aRenderingContext
, 0);
761 nsSize
vScrollbarPrefSize(0, 0);
762 GetScrollbarMetrics(bls
, mInner
.mVScrollbarBox
,
763 nsnull
, &vScrollbarPrefSize
, PR_TRUE
);
764 return vScrollbarPrefSize
.width
;
767 /* virtual */ nscoord
768 nsHTMLScrollFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
770 nscoord result
= mInner
.mScrolledFrame
->GetMinWidth(aRenderingContext
);
771 DISPLAY_MIN_WIDTH(this, result
);
772 return result
+ GetIntrinsicVScrollbarWidth(aRenderingContext
);
775 /* virtual */ nscoord
776 nsHTMLScrollFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
778 nscoord result
= mInner
.mScrolledFrame
->GetPrefWidth(aRenderingContext
);
779 DISPLAY_PREF_WIDTH(this, result
);
780 return NSCoordSaturatingAdd(result
, GetIntrinsicVScrollbarWidth(aRenderingContext
));
784 nsHTMLScrollFrame::GetPadding(nsMargin
& aMargin
)
786 // Our padding hangs out on the inside of the scrollframe, but XUL doesn't
787 // reaize that. If we're stuck inside a XUL box, we need to claim no
789 // @see also nsXULScrollFrame::GetPadding.
790 aMargin
.SizeTo(0,0,0,0);
795 nsHTMLScrollFrame::IsCollapsed(nsBoxLayoutState
& aBoxLayoutState
)
797 // We're never collapsed in the box sense.
802 nsHTMLScrollFrame::Reflow(nsPresContext
* aPresContext
,
803 nsHTMLReflowMetrics
& aDesiredSize
,
804 const nsHTMLReflowState
& aReflowState
,
805 nsReflowStatus
& aStatus
)
807 DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
808 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
810 ScrollReflowState
state(this, aReflowState
);
811 // sanity check: ensure that if we have no scrollbar, we treat it
813 if (!mInner
.mVScrollbarBox
|| mInner
.mNeverHasVerticalScrollbar
)
814 state
.mStyles
.mVertical
= NS_STYLE_OVERFLOW_HIDDEN
;
815 if (!mInner
.mHScrollbarBox
|| mInner
.mNeverHasHorizontalScrollbar
)
816 state
.mStyles
.mHorizontal
= NS_STYLE_OVERFLOW_HIDDEN
;
818 //------------ Handle Incremental Reflow -----------------
819 PRBool reflowContents
= PR_TRUE
; // XXX Ignored
820 PRBool reflowHScrollbar
= PR_TRUE
;
821 PRBool reflowVScrollbar
= PR_TRUE
;
822 PRBool reflowScrollCorner
= PR_TRUE
;
823 if (!aReflowState
.ShouldReflowAllKids()) {
824 #define NEEDS_REFLOW(frame_) \
825 ((frame_) && NS_SUBTREE_DIRTY(frame_))
827 reflowContents
= NEEDS_REFLOW(mInner
.mScrolledFrame
);
828 reflowHScrollbar
= NEEDS_REFLOW(mInner
.mHScrollbarBox
);
829 reflowVScrollbar
= NEEDS_REFLOW(mInner
.mVScrollbarBox
);
830 reflowScrollCorner
= NEEDS_REFLOW(mInner
.mScrollCornerBox
);
835 nsRect oldScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
836 nsRect oldScrolledAreaBounds
= mInner
.mScrolledFrame
->GetView()->GetBounds();
837 state
.mComputedBorder
= aReflowState
.mComputedBorderPadding
-
838 aReflowState
.mComputedPadding
;
840 nsresult rv
= ReflowContents(&state
, aDesiredSize
);
844 PlaceScrollArea(state
);
845 if (!mInner
.mPostedReflowCallback
) {
846 // Make sure we'll try scrolling to restored position
847 PresContext()->PresShell()->PostReflowCallback(&mInner
);
848 mInner
.mPostedReflowCallback
= PR_TRUE
;
851 PRBool didHaveHScrollbar
= mInner
.mHasHorizontalScrollbar
;
852 PRBool didHaveVScrollbar
= mInner
.mHasVerticalScrollbar
;
853 mInner
.mHasHorizontalScrollbar
= state
.mShowHScrollbar
;
854 mInner
.mHasVerticalScrollbar
= state
.mShowVScrollbar
;
855 nsRect newScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
856 nsRect newScrolledAreaBounds
= mInner
.mScrolledFrame
->GetView()->GetBounds();
857 if (mInner
.mSkippedScrollbarLayout
||
858 reflowHScrollbar
|| reflowVScrollbar
|| reflowScrollCorner
||
859 (GetStateBits() & NS_FRAME_IS_DIRTY
) ||
860 didHaveHScrollbar
!= state
.mShowHScrollbar
||
861 didHaveVScrollbar
!= state
.mShowVScrollbar
||
862 oldScrollAreaBounds
!= newScrollAreaBounds
||
863 oldScrolledAreaBounds
!= newScrolledAreaBounds
) {
864 if (!mInner
.mSupppressScrollbarUpdate
) {
865 mInner
.mSkippedScrollbarLayout
= PR_FALSE
;
866 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, state
.mShowHScrollbar
);
867 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, state
.mShowVScrollbar
);
868 // place and reflow scrollbars
869 nsRect insideBorderArea
=
870 nsRect(nsPoint(state
.mComputedBorder
.left
, state
.mComputedBorder
.top
),
871 state
.mInsideBorderSize
);
872 mInner
.LayoutScrollbars(state
.mBoxState
, insideBorderArea
,
873 oldScrollAreaBounds
, state
.mScrollPortRect
);
875 mInner
.mSkippedScrollbarLayout
= PR_TRUE
;
879 aDesiredSize
.width
= state
.mInsideBorderSize
.width
+
880 state
.mComputedBorder
.LeftRight();
881 aDesiredSize
.height
= state
.mInsideBorderSize
.height
+
882 state
.mComputedBorder
.TopBottom();
884 aDesiredSize
.mOverflowArea
= nsRect(0, 0, aDesiredSize
.width
, aDesiredSize
.height
);
886 CheckInvalidateSizeChange(aDesiredSize
);
888 FinishAndStoreOverflow(&aDesiredSize
);
890 if (!InInitialReflow() && !mInner
.mHadNonInitialReflow
) {
891 mInner
.mHadNonInitialReflow
= PR_TRUE
;
892 if (mInner
.mIsRoot
) {
893 // For viewports, record whether we needed a vertical scrollbar
894 // after the first non-initial reflow.
895 mInner
.SaveVScrollbarStateToGlobalHistory();
899 if (mInner
.mIsRoot
&& oldScrolledAreaBounds
!= newScrolledAreaBounds
) {
900 mInner
.PostScrolledAreaEvent(newScrolledAreaBounds
);
903 aStatus
= NS_FRAME_COMPLETE
;
904 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
905 mInner
.PostOverflowEvent();
910 ////////////////////////////////////////////////////////////////////////////////
914 nsHTMLScrollFrame::GetFrameName(nsAString
& aResult
) const
916 return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult
);
921 NS_IMETHODIMP
nsHTMLScrollFrame::GetAccessible(nsIAccessible
** aAccessible
)
923 *aAccessible
= nsnull
;
924 if (!IsFocusable()) {
927 // Focusable via CSS, so needs to be in accessibility hierarchy
928 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
931 return accService
->CreateHTMLGenericAccessible(static_cast<nsIFrame
*>(this), aAccessible
);
934 return NS_ERROR_FAILURE
;
939 nsHTMLScrollFrame::CurPosAttributeChanged(nsIContent
* aChild
,
942 mInner
.CurPosAttributeChanged(aChild
);
946 nsHTMLScrollFrame::PostScrolledAreaEventForCurrentArea()
948 nsRect currentScrolledArea
= mInner
.mScrolledFrame
->GetView()->GetBounds();
949 mInner
.PostScrolledAreaEvent(currentScrolledArea
);
954 NS_QUERYFRAME_HEAD(nsHTMLScrollFrame
)
955 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
956 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
957 NS_QUERYFRAME_ENTRY(nsIScrollableViewProvider
)
958 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
959 NS_QUERYFRAME_TAIL_INHERITING(nsHTMLContainerFrame
)
961 //----------nsXULScrollFrame-------------------------------------------
964 NS_NewXULScrollFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
966 return new (aPresShell
) nsXULScrollFrame(aPresShell
, aContext
, aIsRoot
);
969 NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame
)
971 nsXULScrollFrame::nsXULScrollFrame(nsIPresShell
* aShell
, nsStyleContext
* aContext
, PRBool aIsRoot
)
972 : nsBoxFrame(aShell
, aContext
, aIsRoot
),
973 mInner(this, aIsRoot
, PR_TRUE
)
975 SetLayoutManager(nsnull
);
979 * Get the view that we are scrolling within the scrolling view.
982 nsIFrame
* nsXULScrollFrame::GetScrolledFrame() const
984 return mInner
.GetScrolledFrame();
987 nsIScrollableView
* nsXULScrollFrame::GetScrollableView()
989 return mInner
.GetScrollableView();
992 nsPoint
nsXULScrollFrame::GetScrollPosition() const
994 nsIScrollableView
* s
= mInner
.GetScrollableView();
995 nsPoint scrollPosition
;
996 s
->GetScrollPosition(scrollPosition
.x
, scrollPosition
.y
);
997 return scrollPosition
;
1000 void nsXULScrollFrame::ScrollTo(nsPoint aScrollPosition
, PRUint32 aFlags
)
1002 nsIScrollableView
* s
= mInner
.GetScrollableView();
1003 s
->ScrollTo(aScrollPosition
.x
, aScrollPosition
.y
, aFlags
);
1006 nsGfxScrollFrameInner::ScrollbarStyles
1007 nsXULScrollFrame::GetScrollbarStyles() const {
1008 return mInner
.GetScrollbarStylesFromFrame();
1011 nsMargin
nsXULScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
1012 return mInner
.GetDesiredScrollbarSizes(aState
);
1015 nsMargin
nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState
* aState
) {
1016 NS_ASSERTION(aState
&& aState
->GetRenderingContext(),
1017 "Must have rendering context in layout state for size "
1020 nsMargin
result(0, 0, 0, 0);
1022 if (mVScrollbarBox
) {
1023 nsSize size
= mVScrollbarBox
->GetPrefSize(*aState
);
1024 nsBox::AddMargin(mVScrollbarBox
, size
);
1025 if (IsScrollbarOnRight())
1026 result
.left
= size
.width
;
1028 result
.right
= size
.width
;
1031 if (mHScrollbarBox
) {
1032 nsSize size
= mHScrollbarBox
->GetPrefSize(*aState
);
1033 nsBox::AddMargin(mHScrollbarBox
, size
);
1034 // We don't currently support any scripts that would require a scrollbar
1035 // at the top. (Are there any?)
1036 result
.bottom
= size
.height
;
1042 void nsXULScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible
, PRBool aHorizontalVisible
)
1044 mInner
.mNeverHasVerticalScrollbar
= !aVerticalVisible
;
1045 mInner
.mNeverHasHorizontalScrollbar
= !aHorizontalVisible
;
1048 nsIBox
* nsXULScrollFrame::GetScrollbarBox(PRBool aVertical
)
1050 return aVertical
? mInner
.mVScrollbarBox
: mInner
.mHScrollbarBox
;
1054 nsXULScrollFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
1056 return mInner
.CreateAnonymousContent(aElements
);
1060 nsXULScrollFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
1063 nsBoxFrame::DestroyFrom(aDestructRoot
);
1067 nsXULScrollFrame::SetInitialChildList(nsIAtom
* aListName
,
1068 nsFrameList
& aChildList
)
1070 nsresult rv
= nsBoxFrame::SetInitialChildList(aListName
, aChildList
);
1072 mInner
.CreateScrollableView();
1073 mInner
.ReloadChildFrames();
1075 // listen for scroll events.
1076 mInner
.GetScrollableView()->AddScrollPositionListener(&mInner
);
1083 nsXULScrollFrame::AppendFrames(nsIAtom
* aListName
,
1084 nsFrameList
& aFrameList
)
1086 nsresult rv
= nsBoxFrame::AppendFrames(aListName
, aFrameList
);
1087 mInner
.ReloadChildFrames();
1092 nsXULScrollFrame::InsertFrames(nsIAtom
* aListName
,
1093 nsIFrame
* aPrevFrame
,
1094 nsFrameList
& aFrameList
)
1096 nsresult rv
= nsBoxFrame::InsertFrames(aListName
, aPrevFrame
, aFrameList
);
1097 mInner
.ReloadChildFrames();
1102 nsXULScrollFrame::RemoveFrame(nsIAtom
* aListName
,
1103 nsIFrame
* aOldFrame
)
1105 nsresult rv
= nsBoxFrame::RemoveFrame(aListName
, aOldFrame
);
1106 mInner
.ReloadChildFrames();
1111 nsXULScrollFrame::GetSplittableType() const
1113 return NS_FRAME_NOT_SPLITTABLE
;
1117 nsXULScrollFrame::GetPadding(nsMargin
& aMargin
)
1119 aMargin
.SizeTo(0,0,0,0);
1124 nsXULScrollFrame::GetSkipSides() const
1130 nsXULScrollFrame::GetType() const
1132 return nsGkAtoms::scrollFrame
;
1136 nsXULScrollFrame::InvalidateInternal(const nsRect
& aDamageRect
,
1137 nscoord aX
, nscoord aY
, nsIFrame
* aForChild
,
1140 if (aForChild
== mInner
.mScrolledFrame
) {
1141 // restrict aDamageRect to the scrollable view's bounds
1143 if (r
.IntersectRect(aDamageRect
+ nsPoint(aX
, aY
),
1144 mInner
.mScrollableView
->View()->GetBounds())) {
1145 nsBoxFrame::InvalidateInternal(r
, 0, 0, aForChild
, aFlags
);
1150 nsBoxFrame::InvalidateInternal(aDamageRect
, aX
, aY
, aForChild
, aFlags
);
1154 nsXULScrollFrame::GetBoxAscent(nsBoxLayoutState
& aState
)
1156 if (!mInner
.mScrolledFrame
)
1159 nscoord ascent
= mInner
.mScrolledFrame
->GetBoxAscent(aState
);
1160 nsMargin
m(0,0,0,0);
1161 GetBorderAndPadding(m
);
1170 nsXULScrollFrame::GetPrefSize(nsBoxLayoutState
& aState
)
1173 PropagateDebug(aState
);
1176 nsSize pref
= mInner
.mScrolledFrame
->GetPrefSize(aState
);
1178 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1180 // scrolled frames don't have their own margins
1182 if (mInner
.mVScrollbarBox
&&
1183 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1184 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
1185 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
1186 pref
.width
+= vSize
.width
;
1189 if (mInner
.mHScrollbarBox
&&
1190 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1191 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
1192 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
1193 pref
.height
+= hSize
.height
;
1196 AddBorderAndPadding(pref
);
1197 nsIBox::AddCSSPrefSize(aState
, this, pref
);
1202 nsXULScrollFrame::GetMinSize(nsBoxLayoutState
& aState
)
1205 PropagateDebug(aState
);
1208 nsSize min
= mInner
.mScrolledFrame
->GetMinSizeForScrollArea(aState
);
1210 nsGfxScrollFrameInner::ScrollbarStyles styles
= GetScrollbarStyles();
1212 if (mInner
.mVScrollbarBox
&&
1213 styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
) {
1214 nsSize vSize
= mInner
.mVScrollbarBox
->GetMinSize(aState
);
1215 AddMargin(mInner
.mVScrollbarBox
, vSize
);
1216 min
.width
+= vSize
.width
;
1217 if (min
.height
< vSize
.height
)
1218 min
.height
= vSize
.height
;
1221 if (mInner
.mHScrollbarBox
&&
1222 styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
) {
1223 nsSize hSize
= mInner
.mHScrollbarBox
->GetMinSize(aState
);
1224 AddMargin(mInner
.mHScrollbarBox
, hSize
);
1225 min
.height
+= hSize
.height
;
1226 if (min
.width
< hSize
.width
)
1227 min
.width
= hSize
.width
;
1230 AddBorderAndPadding(min
);
1231 nsIBox::AddCSSMinSize(aState
, this, min
);
1236 nsXULScrollFrame::GetMaxSize(nsBoxLayoutState
& aState
)
1239 PropagateDebug(aState
);
1242 nsSize
maxSize(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
1244 AddBorderAndPadding(maxSize
);
1245 nsIBox::AddCSSMaxSize(aState
, this, maxSize
);
1249 #if 0 // XXXldb I don't think this is even needed
1250 /* virtual */ nscoord
1251 nsXULScrollFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
1253 nsStyleUnit widthUnit
= GetStylePosition()->mWidth
.GetUnit();
1254 if (widthUnit
== eStyleUnit_Percent
|| widthUnit
== eStyleUnit_Auto
) {
1255 nsMargin border
= aReflowState
.mComputedBorderPadding
;
1256 aDesiredSize
.mMaxElementWidth
= border
.right
+ border
.left
;
1257 mMaxElementWidth
= aDesiredSize
.mMaxElementWidth
;
1259 NS_NOTYETIMPLEMENTED("Use the info from the scrolled frame");
1261 // if not set then use the cached size. If set then set it.
1262 if (aDesiredSize
.mMaxElementWidth
== -1)
1263 aDesiredSize
.mMaxElementWidth
= mMaxElementWidth
;
1265 mMaxElementWidth
= aDesiredSize
.mMaxElementWidth
;
1274 nsXULScrollFrame::GetFrameName(nsAString
& aResult
) const
1276 return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult
);
1280 void nsXULScrollFrame::CurPosAttributeChanged(nsIContent
* aChild
, PRInt32 aModType
)
1282 mInner
.CurPosAttributeChanged(aChild
);
1286 nsXULScrollFrame::DoLayout(nsBoxLayoutState
& aState
)
1288 PRUint32 flags
= aState
.LayoutFlags();
1289 nsresult rv
= Layout(aState
);
1290 aState
.SetLayoutFlags(flags
);
1292 nsBox::DoLayout(aState
);
1297 nsXULScrollFrame::PostScrolledAreaEventForCurrentArea()
1299 nsRect currentScrolledArea
= mInner
.mScrolledFrame
->GetView()->GetBounds();
1300 mInner
.PostScrolledAreaEvent(currentScrolledArea
);
1305 NS_QUERYFRAME_HEAD(nsXULScrollFrame
)
1306 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
1307 NS_QUERYFRAME_ENTRY(nsIScrollableFrame
)
1308 NS_QUERYFRAME_ENTRY(nsIScrollableViewProvider
)
1309 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
1310 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame
)
1312 //-------------------- Inner ----------------------
1314 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame
* aOuter
,
1317 : mScrollableView(nsnull
),
1318 mHScrollbarBox(nsnull
),
1319 mVScrollbarBox(nsnull
),
1320 mScrolledFrame(nsnull
),
1321 mScrollCornerBox(nsnull
),
1323 mRestoreRect(-1, -1, -1, -1),
1325 mNeverHasVerticalScrollbar(PR_FALSE
),
1326 mNeverHasHorizontalScrollbar(PR_FALSE
),
1327 mHasVerticalScrollbar(PR_FALSE
),
1328 mHasHorizontalScrollbar(PR_FALSE
),
1329 mViewInitiatedScroll(PR_FALSE
),
1330 mFrameInitiatedScroll(PR_FALSE
),
1331 mDidHistoryRestore(PR_FALSE
),
1334 mSupppressScrollbarUpdate(PR_FALSE
),
1335 mSkippedScrollbarLayout(PR_FALSE
),
1336 mDidLoadHistoryVScrollbarHint(PR_FALSE
),
1337 mHistoryVScrollbarHint(PR_FALSE
),
1338 mHadNonInitialReflow(PR_FALSE
),
1339 mHorizontalOverflow(PR_FALSE
),
1340 mVerticalOverflow(PR_FALSE
),
1341 mPostedReflowCallback(PR_FALSE
),
1342 mMayHaveDirtyFixedChildren(PR_FALSE
),
1343 mUpdateScrollbarAttributes(PR_FALSE
)
1347 nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
1351 NS_IMPL_QUERY_INTERFACE1(nsGfxScrollFrameInner
, nsIScrollPositionListener
)
1354 nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1355 const nsRect
& aDirtyRect
,
1356 const nsDisplayListSet
& aLists
)
1358 nsresult rv
= mOuter
->DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1359 NS_ENSURE_SUCCESS(rv
, rv
);
1361 if (aBuilder
->GetIgnoreScrollFrame() == mOuter
) {
1362 // Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
1363 // The scrolled frame shouldn't have its own background/border, so we
1364 // can just pass aLists directly.
1365 return mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
,
1366 aDirtyRect
, aLists
);
1369 // Now display the scrollbars and scrollcorner. These parts are drawn
1370 // in the border-background layer, on top of our own background and
1371 // borders and underneath borders and backgrounds of later elements
1373 nsIFrame
* kid
= mOuter
->GetFirstChild(nsnull
);
1375 if (kid
!= mScrolledFrame
) {
1376 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, kid
, aDirtyRect
, aLists
);
1377 NS_ENSURE_SUCCESS(rv
, rv
);
1379 kid
= kid
->GetNextSibling();
1382 // Overflow clipping can never clip frames outside our subtree, so there
1383 // is no need to worry about whether we are a moving frame that might clip
1384 // non-moving frames.
1385 nsRect frameClip
= mScrollableView
->View()->GetBounds();
1387 // Not all our descendants will be clipped by overflow clipping, but all
1388 // the ones that aren't clipped will be out of flow frames that have already
1389 // had dirty rects saved for them by their parent frames calling
1390 // MarkOutOfFlowChildrenForDisplayList, so it's safe to restrict our
1392 dirtyRect
.IntersectRect(aDirtyRect
, frameClip
);
1394 nsDisplayListCollection set
;
1395 rv
= mOuter
->BuildDisplayListForChild(aBuilder
, mScrolledFrame
, dirtyRect
, set
);
1396 NS_ENSURE_SUCCESS(rv
, rv
);
1397 nsRect clip
= frameClip
+ aBuilder
->ToReferenceFrame(mOuter
);
1398 // mScrolledFrame may have given us a background, e.g., the scrolled canvas
1399 // frame below the viewport. If so, we want it to be clipped. We also want
1400 // to end up on our BorderBackground list.
1401 // If we are the viewport scrollframe, then clip all our descendants (to ensure
1402 // that fixed-pos elements get clipped by us).
1403 rv
= mOuter
->OverflowClip(aBuilder
, set
, aLists
, clip
, PR_TRUE
, mIsRoot
);
1404 NS_ENSURE_SUCCESS(rv
, rv
);
1410 nsGfxScrollFrameInner::CreateScrollableView()
1412 nsIView
* outerView
= mOuter
->GetView();
1413 NS_ASSERTION(outerView
, "scrollframes must have views");
1414 nsIViewManager
* viewManager
= outerView
->GetViewManager();
1415 mScrollableView
= viewManager
->CreateScrollableView(mOuter
->GetRect(), outerView
);
1416 if (!mScrollableView
)
1419 nsIView
* view
= mScrollableView
->View();
1421 // Insert the view into the view hierarchy
1422 viewManager
->InsertChild(outerView
, view
, nsnull
, PR_TRUE
);
1425 static void HandleScrollPref(nsIScrollable
*aScrollable
, PRInt32 aOrientation
,
1429 aScrollable
->GetDefaultScrollbarPreferences(aOrientation
, &pref
);
1431 case nsIScrollable::Scrollbar_Auto
:
1432 // leave |aValue| untouched
1434 case nsIScrollable::Scrollbar_Never
:
1435 aValue
= NS_STYLE_OVERFLOW_HIDDEN
;
1437 case nsIScrollable::Scrollbar_Always
:
1438 aValue
= NS_STYLE_OVERFLOW_SCROLL
;
1444 nsGfxScrollFrameInner::GetParentViewForChildFrame(nsIFrame
* aFrame
) const
1446 if (aFrame
->GetContent() == mOuter
->GetContent()) {
1447 NS_ASSERTION(mScrollableView
, "Scrollable view should have been created by now");
1448 // scrolled frame, put it under our anonymous view
1449 return mScrollableView
->View();
1451 // scrollbars and stuff; put them under our regular view
1452 return mOuter
->GetView();
1455 nsGfxScrollFrameInner::ScrollbarStyles
1456 nsGfxScrollFrameInner::GetScrollbarStylesFromFrame() const
1458 ScrollbarStyles result
;
1460 nsPresContext
* presContext
= mOuter
->PresContext();
1461 if (!presContext
->IsDynamic() &&
1462 !(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
1463 return ScrollbarStyles(NS_STYLE_OVERFLOW_HIDDEN
, NS_STYLE_OVERFLOW_HIDDEN
);
1467 result
= presContext
->GetViewportOverflowOverride();
1469 nsCOMPtr
<nsISupports
> container
= presContext
->GetContainer();
1470 nsCOMPtr
<nsIScrollable
> scrollable
= do_QueryInterface(container
);
1472 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_X
,
1473 result
.mHorizontal
);
1474 HandleScrollPref(scrollable
, nsIScrollable::ScrollOrientation_Y
,
1478 const nsStyleDisplay
*disp
= mOuter
->GetStyleDisplay();
1479 result
.mHorizontal
= disp
->mOverflowX
;
1480 result
.mVertical
= disp
->mOverflowY
;
1483 NS_ASSERTION(result
.mHorizontal
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1484 result
.mHorizontal
!= NS_STYLE_OVERFLOW_CLIP
&&
1485 result
.mVertical
!= NS_STYLE_OVERFLOW_VISIBLE
&&
1486 result
.mVertical
!= NS_STYLE_OVERFLOW_CLIP
,
1487 "scrollbars should not have been created");
1492 * this code is resposible for restoring the scroll position back to some
1493 * saved position. if the user has not moved the scroll position manually
1494 * we keep scrolling down until we get to our original position. keep in
1495 * mind that content could incrementally be coming in. we only want to stop
1496 * when we reach our new position.
1499 nsGfxScrollFrameInner::ScrollToRestoredPosition()
1501 nsIScrollableView
* scrollingView
= GetScrollableView();
1502 if (!scrollingView
) {
1505 if (mRestoreRect
.y
== -1 || mLastPos
.x
== -1 || mLastPos
.y
== -1) {
1508 // make sure our scroll position did not change for where we last put
1509 // it. if it does then the user must have moved it, and we no longer
1513 scrollingView
->GetScrollPosition(x
, y
);
1515 // if we didn't move, we still need to restore
1516 if (x
== mLastPos
.x
&& y
== mLastPos
.y
) {
1517 nsRect
childRect(0, 0, 0, 0);
1518 nsIView
* child
= nsnull
;
1519 nsresult rv
= scrollingView
->GetScrolledView(child
);
1520 if (NS_SUCCEEDED(rv
) && child
)
1521 childRect
= child
->GetBounds();
1523 PRInt32 cx
, cy
, x
, y
;
1524 scrollingView
->GetScrollPosition(cx
,cy
);
1526 x
= (int)mRestoreRect
.x
;
1527 y
= (int)mRestoreRect
.y
;
1529 // if our position is greater than the scroll position, scroll.
1530 // remember that we could be incrementally loading so we may enter
1531 // and scroll many times.
1532 if (y
!= cy
|| x
!= cx
) {
1533 scrollingView
->ScrollTo(x
, y
, 0);
1534 // scrollpostion goes from twips to pixels. this fixes any roundoff
1536 scrollingView
->GetScrollPosition(mLastPos
.x
, mLastPos
.y
);
1538 // if we reached the position then stop
1539 mRestoreRect
.y
= -1;
1544 // user moved the position, so we won't need to restore
1551 nsGfxScrollFrameInner::FireScrollPortEvent()
1553 mAsyncScrollPortEvent
.Forget();
1555 // Keep this in sync with PostOverflowEvent().
1556 nsSize scrollportSize
= GetScrollPortSize();
1557 nsSize childSize
= GetScrolledRect(scrollportSize
).Size();
1559 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
1560 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
1562 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
1563 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
1565 if (!vertChanged
&& !horizChanged
) {
1569 // If both either overflowed or underflowed then we dispatch only one
1571 PRBool both
= vertChanged
&& horizChanged
&&
1572 newVerticalOverflow
== newHorizontalOverflow
;
1573 nsScrollPortEvent::orientType orient
;
1575 orient
= nsScrollPortEvent::both
;
1576 mHorizontalOverflow
= newHorizontalOverflow
;
1577 mVerticalOverflow
= newVerticalOverflow
;
1579 else if (vertChanged
) {
1580 orient
= nsScrollPortEvent::vertical
;
1581 mVerticalOverflow
= newVerticalOverflow
;
1583 // We need to dispatch a separate horizontal DOM event. Do that the next
1584 // time around since dispatching the vertical DOM event might destroy
1586 PostOverflowEvent();
1590 orient
= nsScrollPortEvent::horizontal
;
1591 mHorizontalOverflow
= newHorizontalOverflow
;
1594 nsScrollPortEvent
event(PR_TRUE
,
1595 (orient
== nsScrollPortEvent::horizontal
?
1596 mHorizontalOverflow
: mVerticalOverflow
) ?
1597 NS_SCROLLPORT_OVERFLOW
: NS_SCROLLPORT_UNDERFLOW
,
1599 event
.orient
= orient
;
1600 return nsEventDispatcher::Dispatch(mOuter
->GetContent(),
1601 mOuter
->PresContext(), &event
);
1605 nsGfxScrollFrameInner::ReloadChildFrames()
1607 mScrolledFrame
= nsnull
;
1608 mHScrollbarBox
= nsnull
;
1609 mVScrollbarBox
= nsnull
;
1610 mScrollCornerBox
= nsnull
;
1612 nsIFrame
* frame
= mOuter
->GetFirstChild(nsnull
);
1614 nsIContent
* content
= frame
->GetContent();
1615 if (content
== mOuter
->GetContent()) {
1616 NS_ASSERTION(!mScrolledFrame
, "Already found the scrolled frame");
1617 mScrolledFrame
= frame
;
1620 content
->GetAttr(kNameSpaceID_None
, nsGkAtoms::orient
, value
);
1621 if (!value
.IsEmpty()) {
1622 // probably a scrollbar then
1623 if (value
.LowerCaseEqualsLiteral("horizontal")) {
1624 NS_ASSERTION(!mHScrollbarBox
, "Found multiple horizontal scrollbars?");
1625 mHScrollbarBox
= frame
;
1627 NS_ASSERTION(!mVScrollbarBox
, "Found multiple vertical scrollbars?");
1628 mVScrollbarBox
= frame
;
1631 // probably a scrollcorner
1632 NS_ASSERTION(!mScrollCornerBox
, "Found multiple scrollcorners");
1633 mScrollCornerBox
= frame
;
1637 frame
= frame
->GetNextSibling();
1642 nsGfxScrollFrameInner::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
1644 nsPresContext
* presContext
= mOuter
->PresContext();
1645 nsIFrame
* parent
= mOuter
->GetParent();
1647 // Don't create scrollbars if we're printing/print previewing
1648 // Get rid of this code when printing moves to its own presentation
1649 if (!presContext
->IsDynamic()) {
1650 // allow scrollbars if this is the child of the viewport, because
1651 // we must be the scrollbars for the print preview window
1652 if (!(mIsRoot
&& presContext
->HasPaginatedScrolling())) {
1653 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
1658 nsIScrollableFrame
*scrollable
= do_QueryFrame(mOuter
);
1660 // At this stage in frame construction, the document element and/or
1661 // BODY overflow styles have not yet been propagated to the
1662 // viewport. So GetScrollbarStylesFromFrame called here will only
1663 // take into account the scrollbar preferences set on the docshell.
1664 // Thus if no scrollbar preferences are set on the docshell, we will
1665 // always create scrollbars, which means later dynamic changes to
1666 // propagated overflow styles will show or hide scrollbars on the
1667 // viewport without requiring frame reconstruction of the viewport
1670 // XXX On the other hand, if scrolling="no" is set on the container
1671 // we won't create scrollbars here so no scrollbars will ever be
1672 // created even if the container's scrolling attribute is later
1673 // changed. However, this has never been supported.
1674 ScrollbarStyles styles
= scrollable
->GetScrollbarStyles();
1675 PRBool canHaveHorizontal
= styles
.mHorizontal
!= NS_STYLE_OVERFLOW_HIDDEN
;
1676 PRBool canHaveVertical
= styles
.mVertical
!= NS_STYLE_OVERFLOW_HIDDEN
;
1677 if (!canHaveHorizontal
&& !canHaveVertical
) {
1682 // The anonymous <div> used by <inputs> never gets scrollbars.
1683 nsITextControlFrame
* textFrame
= do_QueryFrame(parent
);
1685 // Make sure we are not a text area.
1686 nsCOMPtr
<nsIDOMHTMLTextAreaElement
> textAreaElement(do_QueryInterface(parent
->GetContent()));
1687 if (!textAreaElement
) {
1688 mNeverHasVerticalScrollbar
= mNeverHasHorizontalScrollbar
= PR_TRUE
;
1695 nsNodeInfoManager
*nodeInfoManager
=
1696 presContext
->Document()->NodeInfoManager();
1697 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
1698 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollbar
, nsnull
,
1700 NS_ENSURE_TRUE(nodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
1702 if (canHaveHorizontal
) {
1703 rv
= NS_NewElement(getter_AddRefs(mHScrollbarContent
),
1704 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1705 NS_ENSURE_SUCCESS(rv
, rv
);
1706 mHScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
1707 NS_LITERAL_STRING("horizontal"), PR_FALSE
);
1708 if (!aElements
.AppendElement(mHScrollbarContent
))
1709 return NS_ERROR_OUT_OF_MEMORY
;
1712 if (canHaveVertical
) {
1713 rv
= NS_NewElement(getter_AddRefs(mVScrollbarContent
),
1714 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1715 NS_ENSURE_SUCCESS(rv
, rv
);
1716 mVScrollbarContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
1717 NS_LITERAL_STRING("vertical"), PR_FALSE
);
1718 if (!aElements
.AppendElement(mVScrollbarContent
))
1719 return NS_ERROR_OUT_OF_MEMORY
;
1722 if (canHaveHorizontal
&& canHaveVertical
) {
1723 nodeInfo
= nodeInfoManager
->GetNodeInfo(nsGkAtoms::scrollcorner
, nsnull
,
1725 rv
= NS_NewElement(getter_AddRefs(mScrollCornerContent
),
1726 kNameSpaceID_XUL
, nodeInfo
, PR_FALSE
);
1727 NS_ENSURE_SUCCESS(rv
, rv
);
1728 if (!aElements
.AppendElement(mScrollCornerContent
))
1729 return NS_ERROR_OUT_OF_MEMORY
;
1736 nsGfxScrollFrameInner::Destroy()
1738 // Unbind any content created in CreateAnonymousContent from the tree
1739 nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent
);
1740 nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent
);
1741 nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent
);
1743 mScrollEvent
.Revoke();
1744 mAsyncScrollPortEvent
.Revoke();
1745 if (mPostedReflowCallback
) {
1746 mOuter
->PresContext()->PresShell()->CancelReflowCallback(this);
1747 mPostedReflowCallback
= PR_FALSE
;
1749 mScrolledAreaEventDispatcher
.Revoke();
1750 nsIScrollableView
*view
= GetScrollableView();
1751 NS_ASSERTION(view
, "unexpected null pointer");
1753 view
->RemoveScrollPositionListener(this);
1757 nsGfxScrollFrameInner::ScrollPositionWillChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
1764 * Called when we want to update the scrollbar position, either because scrolling happened
1765 * or the user moved the scrollbar position and we need to undo that (e.g., when the user
1766 * clicks to scroll and we're using smooth scrolling, so we need to put the thumb back
1767 * to its initial position for the start of the smooth sequence).
1770 nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX
, nscoord aY
)
1773 SetCoordAttribute(mVScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
1774 aY
- GetScrolledRect(GetScrollPortSize()).y
);
1777 SetCoordAttribute(mHScrollbarBox
->GetContent(), nsGkAtoms::curpos
,
1778 aX
- GetScrolledRect(GetScrollPortSize()).x
);
1782 nsGfxScrollFrameInner::ViewPositionDidChange(nsIScrollableView
* aScrollable
,
1783 nsTArray
<nsIWidget::Configuration
>* aConfigurations
)
1785 // Update frame position to match view offsets
1786 nsPoint childOffset
= mScrolledFrame
->GetView()->GetOffsetTo(mOuter
->GetView());
1787 mScrolledFrame
->SetPosition(childOffset
);
1789 nsRootPresContext
* rootPresContext
= mOuter
->PresContext()->RootPresContext();
1790 // Only update plugin geometry if we're scrolling in the root widget.
1791 // In particular if we're scrolling inside a popup widget, we don't
1792 // want to update plugins since they don't belong to this widget (we
1793 // don't display windowed plugins in popups).
1794 if (mOuter
->GetWindow() ==
1795 rootPresContext
->FrameManager()->GetRootFrame()->GetWindow()) {
1796 rootPresContext
->GetPluginGeometryUpdates(mOuter
, aConfigurations
);
1801 * Called whenever actual scrolling happens for any reason.
1804 nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView
* aScrollable
, nscoord aX
, nscoord aY
)
1806 NS_ASSERTION(!mViewInitiatedScroll
, "Cannot reenter ScrollPositionDidChange");
1808 mViewInitiatedScroll
= PR_TRUE
;
1809 InternalScrollPositionDidChange(aX
, aY
);
1810 mViewInitiatedScroll
= PR_FALSE
;
1817 void nsGfxScrollFrameInner::CurPosAttributeChanged(nsIContent
* aContent
)
1819 NS_ASSERTION(aContent
, "aContent must not be null");
1820 NS_ASSERTION((mHScrollbarBox
&& mHScrollbarBox
->GetContent() == aContent
) ||
1821 (mVScrollbarBox
&& mVScrollbarBox
->GetContent() == aContent
),
1822 "unexpected child");
1824 // Attribute changes on the scrollbars happen in one of three ways:
1825 // 1) The scrollbar changed the attribute in response to some user event
1826 // 2) We changed the attribute in response to a ScrollPositionDidChange
1827 // callback from the scrolling view
1828 // 3) We changed the attribute to adjust the scrollbars for the start
1829 // of a smooth scroll operation
1831 // In case 2), we don't need to scroll the view because the scrolling
1832 // has already happened. In case 3) we don't need to scroll because
1833 // we're just adjusting the scrollbars back to the correct setting
1836 // Cases 1) and 3) do not indicate that actual scrolling has happened. Only
1837 // case 2) indicates actual scrolling. Therefore we do not fire onscroll
1838 // here, but in ScrollPositionDidChange.
1840 // We used to detect this case implicitly because we'd compare the
1841 // scrollbar attributes with the view's current scroll position and
1842 // bail out if they were equal. But that approach is fragile; it can
1843 // fail when, for example, the view scrolls horizontally and
1844 // vertically simultaneously; we'll get here when only the vertical
1845 // attribute has been set, so the attributes and the view scroll
1846 // position don't yet agree, and we'd tell the view to scroll to the
1847 // new vertical position and the old horizontal position! Even worse
1848 // things could happen when smooth scrolling got involved ... crashes
1849 // and other terrors.
1850 if (mViewInitiatedScroll
|| mFrameInitiatedScroll
) return;
1852 nsRect scrolledRect
= GetScrolledRect(GetScrollPortSize());
1854 nscoord x
= GetCoordAttribute(mHScrollbarBox
, nsGkAtoms::curpos
,
1857 nscoord y
= GetCoordAttribute(mVScrollbarBox
, nsGkAtoms::curpos
,
1861 // Make sure the scrollbars indeed moved before firing the event.
1862 // I think it is OK to prevent the call to ScrollbarChanged()
1863 // if we didn't actually move. The following check is the first
1864 // thing ScrollbarChanged() does anyway, before deciding to move
1866 nscoord curPosX
=0, curPosY
=0;
1867 nsIScrollableView
* s
= GetScrollableView();
1869 s
->GetScrollPosition(curPosX
, curPosY
);
1870 if (x
== curPosX
&& y
== curPosY
)
1873 PRBool isSmooth
= aContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::smooth
);
1876 // Make sure an attribute-setting callback occurs even if the view
1877 // didn't actually move yet. We need to make sure other listeners
1878 // see that the scroll position is not (yet) what they thought it
1881 NS_ASSERTION(!mFrameInitiatedScroll
, "Unexpected reentry");
1882 // Make sure we don't do anything in when the view calls us back
1883 // for this scroll operation.
1884 mFrameInitiatedScroll
= PR_TRUE
;
1885 InternalScrollPositionDidChange(curPosX
, curPosY
);
1886 mFrameInitiatedScroll
= PR_FALSE
;
1888 ScrollbarChanged(mOuter
->PresContext(), x
, y
, isSmooth
? NS_VMREFRESH_SMOOTHSCROLL
: 0);
1892 /* ============= Scroll events ========== */
1895 nsGfxScrollFrameInner::ScrollEvent::Run()
1898 mInner
->FireScrollEvent();
1903 nsGfxScrollFrameInner::FireScrollEvent()
1905 mScrollEvent
.Forget();
1907 nsScrollbarEvent
event(PR_TRUE
, NS_SCROLL_EVENT
, nsnull
);
1908 nsEventStatus status
= nsEventStatus_eIgnore
;
1909 nsIContent
* content
= mOuter
->GetContent();
1910 nsPresContext
* prescontext
= mOuter
->PresContext();
1911 // Fire viewport scroll events at the document (where they
1912 // will bubble to the window)
1914 nsIDocument
* doc
= content
->GetCurrentDoc();
1916 nsEventDispatcher::Dispatch(doc
, prescontext
, &event
, nsnull
, &status
);
1919 // scroll events fired at elements don't bubble (although scroll events
1920 // fired at documents do, to the window)
1921 event
.flags
|= NS_EVENT_FLAG_CANT_BUBBLE
;
1922 nsEventDispatcher::Dispatch(content
, prescontext
, &event
, nsnull
, &status
);
1927 nsGfxScrollFrameInner::PostScrollEvent()
1929 if (mScrollEvent
.IsPending())
1932 nsRefPtr
<ScrollEvent
> ev
= new ScrollEvent(this);
1933 if (NS_FAILED(NS_DispatchToCurrentThread(ev
))) {
1934 NS_WARNING("failed to dispatch ScrollEvent");
1941 nsGfxScrollFrameInner::AsyncScrollPortEvent::Run()
1944 mInner
->mOuter
->PresContext()->GetPresShell()->
1945 FlushPendingNotifications(Flush_InterruptibleLayout
);
1947 return mInner
? mInner
->FireScrollPortEvent() : NS_OK
;
1951 nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState
& aState
,
1952 nsRect
& aScrollAreaSize
, PRBool aOnTop
)
1954 if (!mInner
.mHScrollbarBox
)
1957 return AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnTop
, PR_TRUE
, PR_TRUE
);
1961 nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState
& aState
,
1962 nsRect
& aScrollAreaSize
, PRBool aOnRight
)
1964 if (!mInner
.mVScrollbarBox
)
1967 return AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnRight
, PR_FALSE
, PR_TRUE
);
1971 nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState
& aState
,
1972 nsRect
& aScrollAreaSize
, PRBool aOnTop
)
1974 // removing a scrollbar should always fit
1978 AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnTop
, PR_TRUE
, PR_FALSE
);
1979 NS_ASSERTION(result
, "Removing horizontal scrollbar failed to fit??");
1983 nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState
& aState
,
1984 nsRect
& aScrollAreaSize
, PRBool aOnRight
)
1986 // removing a scrollbar should always fit
1990 AddRemoveScrollbar(aState
, aScrollAreaSize
, aOnRight
, PR_FALSE
, PR_FALSE
);
1991 NS_ASSERTION(result
, "Removing vertical scrollbar failed to fit??");
1995 nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState
& aState
, nsRect
& aScrollAreaSize
,
1996 PRBool aOnTop
, PRBool aHorizontal
, PRBool aAdd
)
1999 if (mInner
.mNeverHasHorizontalScrollbar
|| !mInner
.mHScrollbarBox
)
2002 nsSize hSize
= mInner
.mHScrollbarBox
->GetPrefSize(aState
);
2003 nsBox::AddMargin(mInner
.mHScrollbarBox
, hSize
);
2005 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, aAdd
);
2007 PRBool hasHorizontalScrollbar
;
2008 PRBool fit
= AddRemoveScrollbar(hasHorizontalScrollbar
, aScrollAreaSize
.y
, aScrollAreaSize
.height
, hSize
.height
, aOnTop
, aAdd
);
2009 mInner
.mHasHorizontalScrollbar
= hasHorizontalScrollbar
; // because mHasHorizontalScrollbar is a PRPackedBool
2011 mInner
.SetScrollbarVisibility(mInner
.mHScrollbarBox
, !aAdd
);
2015 if (mInner
.mNeverHasVerticalScrollbar
|| !mInner
.mVScrollbarBox
)
2018 nsSize vSize
= mInner
.mVScrollbarBox
->GetPrefSize(aState
);
2019 nsBox::AddMargin(mInner
.mVScrollbarBox
, vSize
);
2021 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, aAdd
);
2023 PRBool hasVerticalScrollbar
;
2024 PRBool fit
= AddRemoveScrollbar(hasVerticalScrollbar
, aScrollAreaSize
.x
, aScrollAreaSize
.width
, vSize
.width
, aOnTop
, aAdd
);
2025 mInner
.mHasVerticalScrollbar
= hasVerticalScrollbar
; // because mHasVerticalScrollbar is a PRPackedBool
2027 mInner
.SetScrollbarVisibility(mInner
.mVScrollbarBox
, !aAdd
);
2034 nsXULScrollFrame::AddRemoveScrollbar(PRBool
& aHasScrollbar
, nscoord
& aXY
,
2035 nscoord
& aSize
, nscoord aSbSize
,
2036 PRBool aRightOrBottom
, PRBool aAdd
)
2038 nscoord size
= aSize
;
2041 if (size
!= NS_INTRINSICSIZE
) {
2044 if (!aRightOrBottom
&& size
>= 0)
2048 if (!aRightOrBottom
)
2053 // not enough room? Yes? Return true.
2055 aHasScrollbar
= aAdd
;
2061 aHasScrollbar
= PR_FALSE
;
2066 nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState
& aState
, const nsRect
& aRect
)
2068 nsIView
* scrollView
= mInner
.mScrollableView
->View();
2069 nsIViewManager
* vm
= scrollView
->GetViewManager();
2070 vm
->MoveViewTo(scrollView
, aRect
.x
, aRect
.y
);
2071 vm
->ResizeView(scrollView
, nsRect(nsPoint(0, 0), aRect
.Size()), PR_TRUE
);
2073 PRUint32 oldflags
= aState
.LayoutFlags();
2074 nsPoint childOffset
=
2075 mInner
.mScrolledFrame
->GetView()->GetOffsetTo(GetView());
2076 nsRect childRect
= nsRect(childOffset
, aRect
.Size());
2078 PRInt32 flags
= NS_FRAME_NO_MOVE_VIEW
;
2080 nsSize minSize
= mInner
.mScrolledFrame
->GetMinSize(aState
);
2082 if (minSize
.height
> childRect
.height
)
2083 childRect
.height
= minSize
.height
;
2085 if (minSize
.width
> childRect
.width
)
2086 childRect
.width
= minSize
.width
;
2088 aState
.SetLayoutFlags(flags
);
2089 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2090 mInner
.mScrolledFrame
->Layout(aState
);
2092 childRect
= mInner
.mScrolledFrame
->GetRect();
2094 if (childRect
.width
< aRect
.width
|| childRect
.height
< aRect
.height
)
2096 childRect
.width
= NS_MAX(childRect
.width
, aRect
.width
);
2097 childRect
.height
= NS_MAX(childRect
.height
, aRect
.height
);
2099 // remove overflow area when we update the bounds,
2100 // because we've already accounted for it
2101 mInner
.mScrolledFrame
->SetBounds(aState
, childRect
);
2102 mInner
.mScrolledFrame
->ClearOverflowRect();
2105 aState
.SetLayoutFlags(oldflags
);
2109 void nsGfxScrollFrameInner::PostOverflowEvent()
2111 if (mAsyncScrollPortEvent
.IsPending())
2114 // Keep this in sync with FireScrollPortEvent().
2115 nsSize scrollportSize
= GetScrollPortSize();
2116 nsSize childSize
= GetScrolledRect(scrollportSize
).Size();
2118 PRBool newVerticalOverflow
= childSize
.height
> scrollportSize
.height
;
2119 PRBool vertChanged
= mVerticalOverflow
!= newVerticalOverflow
;
2121 PRBool newHorizontalOverflow
= childSize
.width
> scrollportSize
.width
;
2122 PRBool horizChanged
= mHorizontalOverflow
!= newHorizontalOverflow
;
2124 if (!vertChanged
&& !horizChanged
) {
2128 nsRefPtr
<AsyncScrollPortEvent
> ev
= new AsyncScrollPortEvent(this);
2129 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev
)))
2130 mAsyncScrollPortEvent
= ev
;
2134 nsGfxScrollFrameInner::IsLTR() const
2136 //TODO make bidi code set these from preferences
2138 nsIFrame
*frame
= mOuter
;
2139 // XXX This is a bit on the slow side.
2141 // If we're the root scrollframe, we need the root element's style data.
2142 nsPresContext
*presContext
= mOuter
->PresContext();
2143 nsIDocument
*document
= presContext
->Document();
2144 nsIContent
*root
= document
->GetRootContent();
2146 // But for HTML we want the body element.
2147 nsCOMPtr
<nsIHTMLDocument
> htmlDoc
= do_QueryInterface(document
);
2149 nsIContent
*bodyContent
= htmlDoc
->GetBodyContentExternal();
2151 root
= bodyContent
; // we can trust the document to hold on to it
2155 nsIFrame
*rootsFrame
=
2156 presContext
->PresShell()->GetPrimaryFrameFor(root
);
2162 return frame
->GetStyleVisibility()->mDirection
!= NS_STYLE_DIRECTION_RTL
;
2166 nsGfxScrollFrameInner::IsScrollbarOnRight() const
2168 nsPresContext
*presContext
= mOuter
->PresContext();
2169 switch (presContext
->GetCachedIntPref(kPresContext_ScrollbarSide
)) {
2171 case 0: // UI directionality
2172 return presContext
->GetCachedIntPref(kPresContext_BidiDirection
)
2173 == IBMBIDI_TEXTDIRECTION_LTR
;
2174 case 1: // Document / content directionality
2176 case 2: // Always right
2178 case 3: // Always left
2184 * Reflow the scroll area if it needs it and return its size. Also determine if the reflow will
2185 * cause any of the scrollbars to need to be reflowed.
2188 nsXULScrollFrame::Layout(nsBoxLayoutState
& aState
)
2190 PRBool scrollbarRight
= mInner
.IsScrollbarOnRight();
2191 PRBool scrollbarBottom
= PR_TRUE
;
2193 // get the content rect
2194 nsRect
clientRect(0,0,0,0);
2195 GetClientRect(clientRect
);
2197 // the scroll area size starts off as big as our content area
2198 nsRect
scrollAreaRect(clientRect
);
2201 Our basic strategy here is to first try laying out the content with
2202 the scrollbars in their current state. We're hoping that that will
2203 just "work"; the content will overflow wherever there's a scrollbar
2204 already visible. If that does work, then there's no need to lay out
2205 the scrollarea. Otherwise we fix up the scrollbars; first we add a
2206 vertical one to scroll the content if necessary, or remove it if
2207 it's not needed. Then we reflow the content if the scrollbar
2208 changed. Then we add a horizontal scrollbar if necessary (or
2209 remove if not needed), and if that changed, we reflow the content
2210 again. At this point, any scrollbars that are needed to scroll the
2211 content have been added.
2213 In the second phase we check to see if any scrollbars are too small
2214 to display, and if so, we remove them. We check the horizontal
2215 scrollbar first; removing it might make room for the vertical
2216 scrollbar, and if we have room for just one scrollbar we'll save
2219 Finally we position and size the scrollbars and scrollcorner (the
2220 square that is needed in the corner of the window when two
2221 scrollbars are visible), and reflow any fixed position views
2222 (if we're the viewport and we added or removed a scrollbar).
2225 ScrollbarStyles styles
= GetScrollbarStyles();
2227 // Look at our style do we always have vertical or horizontal scrollbars?
2228 if (styles
.mHorizontal
== NS_STYLE_OVERFLOW_SCROLL
)
2229 mInner
.mHasHorizontalScrollbar
= PR_TRUE
;
2230 if (styles
.mVertical
== NS_STYLE_OVERFLOW_SCROLL
)
2231 mInner
.mHasVerticalScrollbar
= PR_TRUE
;
2233 if (mInner
.mHasHorizontalScrollbar
)
2234 AddHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2236 if (mInner
.mHasVerticalScrollbar
)
2237 AddVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2239 nsRect oldScrollAreaBounds
= mInner
.mScrollableView
->View()->GetBounds();
2241 // layout our the scroll area
2242 LayoutScrollArea(aState
, scrollAreaRect
);
2244 // now look at the content area and see if we need scrollbars or not
2245 PRBool needsLayout
= PR_FALSE
;
2247 // if we have 'auto' scrollbars look at the vertical case
2248 if (styles
.mVertical
!= NS_STYLE_OVERFLOW_SCROLL
) {
2249 // These are only good until the call to LayoutScrollArea.
2250 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollAreaRect
.Size());
2251 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2253 // There are two cases to consider
2254 if (scrolledContentSize
.height
<= scrollAreaRect
.height
2255 || styles
.mVertical
!= NS_STYLE_OVERFLOW_AUTO
) {
2256 if (mInner
.mHasVerticalScrollbar
) {
2257 // We left room for the vertical scrollbar, but it's not needed;
2259 RemoveVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2260 needsLayout
= PR_TRUE
;
2263 if (!mInner
.mHasVerticalScrollbar
) {
2264 // We didn't leave room for the vertical scrollbar, but it turns
2266 if (AddVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
))
2267 needsLayout
= PR_TRUE
;
2271 // ok layout at the right size
2273 nsBoxLayoutState
resizeState(aState
);
2274 LayoutScrollArea(resizeState
, scrollAreaRect
);
2275 needsLayout
= PR_FALSE
;
2280 // if scrollbars are auto look at the horizontal case
2281 if (styles
.mHorizontal
!= NS_STYLE_OVERFLOW_SCROLL
)
2283 // These are only good until the call to LayoutScrollArea.
2284 nsRect scrolledRect
= mInner
.GetScrolledRect(scrollAreaRect
.Size());
2285 nsSize
scrolledContentSize(scrolledRect
.XMost(), scrolledRect
.YMost());
2287 // if the child is wider that the scroll area
2288 // and we don't have a scrollbar add one.
2289 if (scrolledContentSize
.width
> scrollAreaRect
.width
2290 && styles
.mHorizontal
== NS_STYLE_OVERFLOW_AUTO
) {
2292 if (!mInner
.mHasHorizontalScrollbar
) {
2294 if (AddHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
))
2295 needsLayout
= PR_TRUE
;
2297 // if we added a horizontal scrollbar and we did not have a vertical
2298 // there is a chance that by adding the horizontal scrollbar we will
2299 // suddenly need a vertical scrollbar. Is a special case but its
2301 //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
2302 // printf("****Gfx Scrollbar Special case hit!!*****\n");
2306 // if the area is smaller or equal to and we have a scrollbar then
2308 if (mInner
.mHasHorizontalScrollbar
) {
2309 RemoveHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2310 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
);
2319 needsLayout
= PR_FALSE
;
2322 // get the preferred size of the scrollbars
2323 nsSize
hMinSize(0, 0);
2324 if (mInner
.mHScrollbarBox
&& mInner
.mHasHorizontalScrollbar
) {
2325 GetScrollbarMetrics(aState
, mInner
.mHScrollbarBox
, &hMinSize
, nsnull
, PR_FALSE
);
2327 nsSize
vMinSize(0, 0);
2328 if (mInner
.mVScrollbarBox
&& mInner
.mHasVerticalScrollbar
) {
2329 GetScrollbarMetrics(aState
, mInner
.mVScrollbarBox
, &vMinSize
, nsnull
, PR_TRUE
);
2332 // Disable scrollbars that are too small
2333 // Disable horizontal scrollbar first. If we have to disable only one
2334 // scrollbar, we'd rather keep the vertical scrollbar.
2335 // Note that we always give horizontal scrollbars their preferred height,
2336 // never their min-height. So check that there's room for the preferred height.
2337 if (mInner
.mHasHorizontalScrollbar
&&
2338 (hMinSize
.width
> clientRect
.width
- vMinSize
.width
2339 || hMinSize
.height
> clientRect
.height
)) {
2340 RemoveHorizontalScrollbar(aState
, scrollAreaRect
, scrollbarBottom
);
2341 needsLayout
= PR_TRUE
;
2343 // Now disable vertical scrollbar if necessary
2344 if (mInner
.mHasVerticalScrollbar
&&
2345 (vMinSize
.height
> clientRect
.height
- hMinSize
.height
2346 || vMinSize
.width
> clientRect
.width
)) {
2347 RemoveVerticalScrollbar(aState
, scrollAreaRect
, scrollbarRight
);
2348 needsLayout
= PR_TRUE
;
2351 // we only need to set the rect. The inner child stays the same size.
2353 nsBoxLayoutState
resizeState(aState
);
2354 LayoutScrollArea(resizeState
, scrollAreaRect
);
2357 if (!mInner
.mSupppressScrollbarUpdate
) {
2358 mInner
.LayoutScrollbars(aState
, clientRect
, oldScrollAreaBounds
, scrollAreaRect
);
2360 if (!mInner
.mPostedReflowCallback
) {
2361 // Make sure we'll try scrolling to restored position
2362 PresContext()->PresShell()->PostReflowCallback(&mInner
);
2363 mInner
.mPostedReflowCallback
= PR_TRUE
;
2365 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
2366 mInner
.mHadNonInitialReflow
= PR_TRUE
;
2369 mInner
.PostOverflowEvent();
2374 nsGfxScrollFrameInner::FinishReflowForScrollbar(nsIContent
* aContent
,
2375 nscoord aMinXY
, nscoord aMaxXY
,
2377 nscoord aPageIncrement
,
2380 // Scrollbars assume zero is the minimum position, so translate for them.
2381 SetCoordAttribute(aContent
, nsGkAtoms::curpos
, aCurPosXY
- aMinXY
);
2382 SetScrollbarEnabled(aContent
, aMaxXY
- aMinXY
);
2383 SetCoordAttribute(aContent
, nsGkAtoms::maxpos
, aMaxXY
- aMinXY
);
2384 SetCoordAttribute(aContent
, nsGkAtoms::pageincrement
, aPageIncrement
);
2385 SetCoordAttribute(aContent
, nsGkAtoms::increment
, aIncrement
);
2389 nsGfxScrollFrameInner::ReflowFinished()
2391 mPostedReflowCallback
= PR_FALSE
;
2393 ScrollToRestoredPosition();
2395 if (NS_SUBTREE_DIRTY(mOuter
) || !mUpdateScrollbarAttributes
)
2398 mUpdateScrollbarAttributes
= PR_FALSE
;
2400 // Update scrollbar attributes.
2401 nsPresContext
* presContext
= mOuter
->PresContext();
2403 if (mMayHaveDirtyFixedChildren
) {
2404 mMayHaveDirtyFixedChildren
= PR_FALSE
;
2405 nsIFrame
* parentFrame
= mOuter
->GetParent();
2406 for (nsIFrame
* fixedChild
=
2407 parentFrame
->GetFirstChild(nsGkAtoms::fixedList
);
2408 fixedChild
; fixedChild
= fixedChild
->GetNextSibling()) {
2409 // force a reflow of the fixed child
2410 presContext
->PresShell()->
2411 FrameNeedsReflow(fixedChild
, nsIPresShell::eResize
,
2412 NS_FRAME_HAS_DIRTY_CHILDREN
);
2416 nsIScrollableView
* scrollable
= GetScrollableView();
2417 nsRect scrollArea
= scrollable
->View()->GetBounds();
2419 const nsStyleFont
* font
= mOuter
->GetStyleFont();
2420 const nsFont
& f
= font
->mFont
;
2421 nsCOMPtr
<nsIFontMetrics
> fm
= presContext
->GetMetricsFor(f
);
2422 nscoord fontHeight
= 1;
2423 NS_ASSERTION(fm
,"FontMetrics is null assuming fontHeight == 1");
2425 fm
->GetHeight(fontHeight
);
2426 scrollable
->SetLineHeight(fontHeight
);
2428 nsRect scrolledContentRect
= GetScrolledRect(scrollArea
.Size());
2429 nscoord minX
= scrolledContentRect
.x
;
2430 nscoord maxX
= scrolledContentRect
.XMost() - scrollArea
.width
;
2431 nscoord minY
= scrolledContentRect
.y
;
2432 nscoord maxY
= scrolledContentRect
.YMost() - scrollArea
.height
;
2434 // Suppress handling of the curpos attribute changes we make here.
2435 NS_ASSERTION(!mFrameInitiatedScroll
, "We shouldn't be reentering here");
2436 mFrameInitiatedScroll
= PR_TRUE
;
2438 nsCOMPtr
<nsIContent
> vScroll
=
2439 mVScrollbarBox
? mVScrollbarBox
->GetContent() : nsnull
;
2440 nsCOMPtr
<nsIContent
> hScroll
=
2441 mHScrollbarBox
? mHScrollbarBox
->GetContent() : nsnull
;
2443 // Note, in some cases mOuter may get deleted while finishing reflow
2445 if (vScroll
|| hScroll
) {
2446 nsWeakFrame
weakFrame(mOuter
);
2447 nscoord curPosX
, curPosY
;
2448 scrollable
->GetScrollPosition(curPosX
, curPosY
);
2450 // We normally use (scrollArea.height - fontHeight) for height
2451 // of page scrolling. However, it is too small when
2452 // fontHeight is very large. (If fontHeight is larger than
2453 // scrollArea.height, direction of scrolling will be opposite).
2454 // To avoid it, we use (float(scrollArea.height) * 0.8) as
2455 // lower bound value of height of page scrolling. (bug 383267)
2456 nscoord pageincrement
= nscoord(scrollArea
.height
- fontHeight
);
2457 nscoord pageincrementMin
= nscoord(float(scrollArea
.height
) * 0.8);
2458 FinishReflowForScrollbar(vScroll
, minY
, maxY
, curPosY
,
2459 NS_MAX(pageincrement
,pageincrementMin
),
2463 FinishReflowForScrollbar(hScroll
, minX
, maxX
, curPosX
,
2464 nscoord(float(scrollArea
.width
) * 0.8),
2465 nsPresContext::CSSPixelsToAppUnits(10));
2467 NS_ENSURE_TRUE(weakFrame
.IsAlive(), PR_FALSE
);
2470 mFrameInitiatedScroll
= PR_FALSE
;
2471 // We used to rely on the curpos attribute changes above to scroll the
2472 // view. However, for scrolling to the left of the viewport, we
2473 // rescale the curpos attribute, which means that operations like
2474 // resizing the window while it is scrolled all the way to the left
2475 // hold the curpos attribute constant at 0 while still requiring
2476 // scrolling. So we suppress the effect of the changes above with
2477 // mFrameInitiatedScroll and call CurPosAttributeChanged here.
2478 // (It actually even works some of the time without this, thanks to
2479 // nsSliderFrame::AttributeChanged's handling of maxpos, but not when
2480 // we hide the scrollbar on a large size change, such as
2482 if (!mHScrollbarBox
&& !mVScrollbarBox
)
2484 CurPosAttributeChanged(mVScrollbarBox
? mVScrollbarBox
->GetContent()
2485 : mHScrollbarBox
->GetContent());
2490 nsGfxScrollFrameInner::ReflowCallbackCanceled()
2492 mPostedReflowCallback
= PR_FALSE
;
2495 static void LayoutAndInvalidate(nsBoxLayoutState
& aState
,
2496 nsIFrame
* aBox
, const nsRect
& aRect
)
2498 // When a child box changes shape of position, the parent
2499 // is responsible for invalidation; the overflow rect must be invalidated
2500 // to make sure to catch any overflow
2501 PRBool rectChanged
= aBox
->GetRect() != aRect
;
2503 aBox
->Invalidate(aBox
->GetOverflowRect());
2504 nsBoxFrame::LayoutChildAt(aState
, aBox
, aRect
);
2506 aBox
->Invalidate(aBox
->GetOverflowRect());
2509 static void AdjustScrollbarRect(nsIView
* aView
, nsPresContext
* aPresContext
,
2510 nsRect
& aRect
, PRBool aVertical
)
2512 if ((aVertical
? aRect
.width
: aRect
.height
) == 0)
2516 nsIWidget
* widget
= aView
->GetNearestWidget(&offset
);
2518 nsIntRect widgetRect
;
2519 if (!widget
->ShowsResizeIndicator(&widgetRect
))
2522 nsRect resizerRect
=
2523 nsRect(aPresContext
->DevPixelsToAppUnits(widgetRect
.x
) - offset
.x
,
2524 aPresContext
->DevPixelsToAppUnits(widgetRect
.y
) - offset
.y
,
2525 aPresContext
->DevPixelsToAppUnits(widgetRect
.width
),
2526 aPresContext
->DevPixelsToAppUnits(widgetRect
.height
));
2528 if (!resizerRect
.Contains(aRect
.BottomRight() - nsPoint(1, 1)))
2532 aRect
.height
= NS_MAX(0, resizerRect
.y
- aRect
.y
);
2534 aRect
.width
= NS_MAX(0, resizerRect
.x
- aRect
.x
);
2538 nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState
& aState
,
2539 const nsRect
& aContentArea
,
2540 const nsRect
& aOldScrollArea
,
2541 const nsRect
& aScrollArea
)
2543 NS_ASSERTION(!mSupppressScrollbarUpdate
,
2544 "This should have been suppressed");
2546 nsIView
* view
= mOuter
->GetView();
2547 nsPresContext
* presContext
= mScrolledFrame
->PresContext();
2548 if (mVScrollbarBox
) {
2549 NS_PRECONDITION(mVScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
2550 nsRect
vRect(aScrollArea
);
2551 vRect
.width
= aContentArea
.width
- aScrollArea
.width
;
2552 vRect
.x
= IsScrollbarOnRight() ? aScrollArea
.XMost() : aContentArea
.x
;
2554 mVScrollbarBox
->GetMargin(margin
);
2555 vRect
.Deflate(margin
);
2556 AdjustScrollbarRect(view
, presContext
, vRect
, PR_TRUE
);
2557 LayoutAndInvalidate(aState
, mVScrollbarBox
, vRect
);
2560 if (mHScrollbarBox
) {
2561 NS_PRECONDITION(mHScrollbarBox
->IsBoxFrame(), "Must be a box frame!");
2562 nsRect
hRect(aScrollArea
);
2563 hRect
.height
= aContentArea
.height
- aScrollArea
.height
;
2564 hRect
.y
= PR_TRUE
? aScrollArea
.YMost() : aContentArea
.y
;
2566 mHScrollbarBox
->GetMargin(margin
);
2567 hRect
.Deflate(margin
);
2568 AdjustScrollbarRect(view
, presContext
, hRect
, PR_FALSE
);
2569 LayoutAndInvalidate(aState
, mHScrollbarBox
, hRect
);
2572 // place the scrollcorner
2573 if (mScrollCornerBox
) {
2574 NS_PRECONDITION(mScrollCornerBox
->IsBoxFrame(), "Must be a box frame!");
2575 nsRect
r(0, 0, 0, 0);
2576 if (aContentArea
.x
!= aScrollArea
.x
) {
2577 // scrollbar (if any) on left
2578 r
.x
= aContentArea
.x
;
2579 r
.width
= aScrollArea
.x
- aContentArea
.x
;
2580 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
2582 // scrollbar (if any) on right
2583 r
.x
= aScrollArea
.XMost();
2584 r
.width
= aContentArea
.XMost() - aScrollArea
.XMost();
2585 NS_ASSERTION(r
.width
>= 0, "Scroll area should be inside client rect");
2587 if (aContentArea
.y
!= aScrollArea
.y
) {
2588 // scrollbar (if any) on top
2589 r
.y
= aContentArea
.y
;
2590 r
.height
= aScrollArea
.y
- aContentArea
.y
;
2591 NS_ASSERTION(r
.height
>= 0, "Scroll area should be inside client rect");
2593 // scrollbar (if any) on bottom
2594 r
.y
= aScrollArea
.YMost();
2595 r
.height
= aContentArea
.YMost() - aScrollArea
.YMost();
2596 NS_ASSERTION(r
.height
>= 0, "Scroll area should be inside client rect");
2598 LayoutAndInvalidate(aState
, mScrollCornerBox
, r
);
2601 // may need to update fixed position children of the viewport,
2602 // if the client area changed size because of an incremental
2603 // reflow of a descendant. (If the outer frame is dirty, the fixed
2604 // children will be re-laid out anyway)
2605 if (aOldScrollArea
.Size() != aScrollArea
.Size() &&
2606 !(mOuter
->GetStateBits() & NS_FRAME_IS_DIRTY
) &&
2608 mMayHaveDirtyFixedChildren
= PR_TRUE
;
2611 // post reflow callback to modify scrollbar attributes
2612 mUpdateScrollbarAttributes
= PR_TRUE
;
2613 if (!mPostedReflowCallback
) {
2614 aState
.PresContext()->PresShell()->PostReflowCallback(this);
2615 mPostedReflowCallback
= PR_TRUE
;
2620 nsGfxScrollFrameInner::ScrollbarChanged(nsPresContext
* aPresContext
, nscoord aX
, nscoord aY
, PRUint32 aFlags
)
2622 nsIScrollableView
* scrollable
= GetScrollableView();
2623 scrollable
->ScrollTo(aX
, aY
, aFlags
);
2624 // printf("scrolling to: %d, %d\n", aX, aY);
2628 nsGfxScrollFrameInner::SetScrollbarEnabled(nsIContent
* aContent
, nscoord aMaxPos
)
2631 aContent
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
, PR_TRUE
);
2633 aContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::disabled
,
2634 NS_LITERAL_STRING("true"), PR_TRUE
);
2639 nsGfxScrollFrameInner::SetCoordAttribute(nsIContent
* aContent
, nsIAtom
* aAtom
,
2642 // convert to pixels
2643 aSize
= nsPresContext::AppUnitsToIntCSSPixels(aSize
);
2645 // only set the attribute if it changed.
2647 nsAutoString newValue
;
2648 newValue
.AppendInt(aSize
);
2650 if (aContent
->AttrValueIs(kNameSpaceID_None
, aAtom
, newValue
, eCaseMatters
))
2653 aContent
->SetAttr(kNameSpaceID_None
, aAtom
, newValue
, PR_TRUE
);
2657 nsGfxScrollFrameInner::GetScrolledRect(const nsSize
& aScrollPortSize
) const
2659 nsRect result
= mScrolledFrame
->GetOverflowRect();
2660 nscoord x1
= result
.x
, x2
= result
.XMost(),
2661 y1
= result
.y
, y2
= result
.YMost();
2664 if (IsLTR() || mIsXUL
) {
2668 if (x2
> aScrollPortSize
.width
)
2669 x2
= aScrollPortSize
.width
;
2670 // When the scrolled frame chooses a size larger than its available width (because
2671 // its padding alone is larger than the available width), we need to keep the
2672 // start-edge of the scroll frame anchored to the start-edge of the scrollport.
2673 // When the scrolled frame is RTL, this means moving it in our left-based
2674 // coordinate system, so we need to compensate for its extra width here by
2675 // effectively repositioning the frame.
2676 nscoord extraWidth
= NS_MAX(0, mScrolledFrame
->GetSize().width
- aScrollPortSize
.width
);
2680 return nsRect(x1
, y1
, x2
- x1
, y2
- y1
);
2684 nsGfxScrollFrameInner::GetActualScrollbarSizes() const {
2686 mOuter
->GetBorder(border
);
2687 nsRect
r(nsPoint(0,0), mOuter
->GetSize());
2689 nsRect scrollArea
= mScrollableView
->View()->GetBounds();
2691 return nsMargin(scrollArea
.x
- r
.x
, scrollArea
.y
- r
.y
,
2692 r
.XMost() - scrollArea
.XMost(),
2693 r
.YMost() - scrollArea
.YMost());
2697 nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox
* aScrollbar
, PRBool aVisible
)
2702 nsIScrollbarFrame
* scrollbar
= do_QueryFrame(aScrollbar
);
2704 // See if we have a mediator.
2705 nsIScrollbarMediator
* mediator
= scrollbar
->GetScrollbarMediator();
2707 // Inform the mediator of the visibility change.
2708 mediator
->VisibilityChanged(aVisible
);
2714 nsGfxScrollFrameInner::GetCoordAttribute(nsIBox
* aBox
, nsIAtom
* atom
, PRInt32 defaultValue
)
2717 nsIContent
* content
= aBox
->GetContent();
2720 content
->GetAttr(kNameSpaceID_None
, atom
, value
);
2721 if (!value
.IsEmpty())
2725 // convert it to an integer
2726 defaultValue
= nsPresContext::CSSPixelsToAppUnits(value
.ToInteger(&error
));
2730 return defaultValue
;
2733 static nsIURI
* GetDocURI(nsIFrame
* aFrame
)
2735 nsIPresShell
* shell
= aFrame
->PresContext()->GetPresShell();
2738 nsIDocument
* doc
= shell
->GetDocument();
2741 return doc
->GetDocumentURI();
2745 nsGfxScrollFrameInner::SaveVScrollbarStateToGlobalHistory()
2747 NS_ASSERTION(mIsRoot
, "Only use this on viewports");
2749 // If the hint is the same as the one we loaded, don't bother
2751 if (mDidLoadHistoryVScrollbarHint
&&
2752 (mHistoryVScrollbarHint
== mHasVerticalScrollbar
))
2755 nsIURI
* uri
= GetDocURI(mOuter
);
2759 nsCOMPtr
<nsIGlobalHistory3
> history(do_GetService(NS_GLOBALHISTORY2_CONTRACTID
));
2764 if (mHasVerticalScrollbar
) {
2765 flags
|= NS_GECKO_FLAG_NEEDS_VERTICAL_SCROLLBAR
;
2767 history
->SetURIGeckoFlags(uri
, flags
);
2768 // if it fails, we don't care
2772 nsGfxScrollFrameInner::GetVScrollbarHintFromGlobalHistory(PRBool
* aVScrollbarNeeded
)
2774 NS_ASSERTION(mIsRoot
, "Only use this on viewports");
2775 NS_ASSERTION(!mDidLoadHistoryVScrollbarHint
,
2776 "Should only load a hint once, it can be expensive");
2778 nsIURI
* uri
= GetDocURI(mOuter
);
2780 return NS_ERROR_FAILURE
;
2782 nsCOMPtr
<nsIGlobalHistory3
> history(do_GetService(NS_GLOBALHISTORY2_CONTRACTID
));
2784 return NS_ERROR_FAILURE
;
2787 nsresult rv
= history
->GetURIGeckoFlags(uri
, &flags
);
2791 *aVScrollbarNeeded
= (flags
& NS_GECKO_FLAG_NEEDS_VERTICAL_SCROLLBAR
) != 0;
2792 mDidLoadHistoryVScrollbarHint
= PR_TRUE
;
2793 mHistoryVScrollbarHint
= *aVScrollbarNeeded
;
2798 nsGfxScrollFrameInner::SaveState(nsIStatefulFrame::SpecialStateID aStateID
)
2800 // Don't save "normal" state for the root scrollframe; that's
2801 // handled via the eDocumentScrollState state id
2802 if (mIsRoot
&& aStateID
== nsIStatefulFrame::eNoID
) {
2806 nsIScrollbarMediator
* mediator
= do_QueryFrame(GetScrolledFrame());
2808 // child handles its own scroll state, so don't bother saving state here
2812 nsIScrollableView
* scrollingView
= GetScrollableView();
2814 scrollingView
->GetScrollPosition(x
,y
);
2815 // Don't save scroll position if we are at (0,0)
2820 nsIView
* child
= nsnull
;
2821 scrollingView
->GetScrolledView(child
);
2826 nsRect childRect
= child
->GetBounds();
2829 nsPresState
* state
= new nsPresState();
2834 state
->SetScrollState(childRect
);
2840 nsGfxScrollFrameInner::RestoreState(nsPresState
* aState
)
2842 mRestoreRect
= aState
->GetScrollState();
2845 mDidHistoryRestore
= PR_TRUE
;
2846 nsIScrollableView
* scrollingView
= GetScrollableView();
2847 if (scrollingView
) {
2848 scrollingView
->GetScrollPosition(mLastPos
.x
, mLastPos
.y
);
2850 mLastPos
= nsPoint(0, 0);
2855 nsGfxScrollFrameInner::PostScrolledAreaEvent(nsRect
&aScrolledArea
)
2857 if (mScrolledAreaEventDispatcher
.IsPending()) {
2858 mScrolledAreaEventDispatcher
.get()->mScrolledArea
= aScrolledArea
;
2862 nsRefPtr
<ScrolledAreaEventDispatcher
> dp
= new ScrolledAreaEventDispatcher(this);
2864 NS_WARNING("OOM while posting NS_SCROLLEDAREACHANGED");
2868 dp
->mScrolledArea
= aScrolledArea
;
2870 if (NS_FAILED(NS_DispatchToCurrentThread(dp
))) {
2871 NS_WARNING("Failed to dispatch ScrolledAreaEventDispatcher");
2873 mScrolledAreaEventDispatcher
= dp
;
2877 ////////////////////////////////////////////////////////////////////////////////
2878 // ScrolledArea change event dispatch
2881 nsGfxScrollFrameInner::ScrolledAreaEventDispatcher::Run()
2883 if (mScrollFrameInner
)
2884 mScrollFrameInner
->FireScrolledAreaEvent(mScrolledArea
);
2889 nsGfxScrollFrameInner::FireScrolledAreaEvent(nsRect
&aScrolledArea
)
2891 mScrolledAreaEventDispatcher
.Forget();
2893 nsScrollAreaEvent
event(PR_TRUE
, NS_SCROLLEDAREACHANGED
, nsnull
);
2894 nsPresContext
*prescontext
= mOuter
->PresContext();
2895 nsIContent
* content
= mOuter
->GetContent();
2897 event
.mArea
= aScrolledArea
;
2899 nsIDocument
*doc
= content
->GetCurrentDoc();
2901 nsEventDispatcher::Dispatch(doc
, prescontext
, &event
, nsnull
);