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