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