Back to index

lightning-sunbird  0.9+nobinonly
nsGfxScrollFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 #include "nsCOMPtr.h"
00039 #include "nsHTMLParts.h"
00040 #include "nsPresContext.h"
00041 #include "nsReflowType.h"
00042 #include "nsIDeviceContext.h"
00043 #include "nsPageFrame.h"
00044 #include "nsViewsCID.h"
00045 #include "nsIServiceManager.h"
00046 #include "nsIView.h"
00047 #include "nsIScrollableView.h"
00048 #include "nsIScrollable.h"
00049 #include "nsIViewManager.h"
00050 #include "nsHTMLContainerFrame.h"
00051 #include "nsWidgetsCID.h"
00052 #include "nsGfxScrollFrame.h"
00053 #include "nsLayoutAtoms.h"
00054 #include "nsXULAtoms.h"
00055 #include "nsHTMLAtoms.h"
00056 #include "nsINameSpaceManager.h"
00057 #include "nsISupportsArray.h"
00058 #include "nsIDocument.h"
00059 #include "nsIFontMetrics.h"
00060 #include "nsIDocumentObserver.h"
00061 #include "nsIDocument.h"
00062 #include "nsBoxLayoutState.h"
00063 #include "nsINodeInfo.h"
00064 #include "nsIScrollbarFrame.h"
00065 #include "nsIScrollbarMediator.h"
00066 #include "nsITextControlFrame.h"
00067 #include "nsIDOMHTMLTextAreaElement.h"
00068 #include "nsNodeInfoManager.h"
00069 #include "nsIURI.h"
00070 #include "nsGUIEvent.h"
00071 #include "nsContentCreatorFunctions.h"
00072 #include "nsISupportsPrimitives.h"
00073 #include "nsIPresShell.h"
00074 #include "nsIEventQueueService.h"
00075 #include "nsReflowPath.h"
00076 #include "nsAutoPtr.h"
00077 #include "nsPresState.h"
00078 #ifdef ACCESSIBILITY
00079 #include "nsIAccessibilityService.h"
00080 #endif
00081 
00082 static const char kEventQueueServiceCID[] = NS_EVENTQUEUESERVICE_CONTRACTID;
00083 
00084 //----------------------------------------------------------------------
00085 
00086 //----------nsHTMLScrollFrame-------------------------------------------
00087 
00088 nsresult
00089 NS_NewHTMLScrollFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRBool aIsRoot)
00090 {
00091   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00092   if (nsnull == aNewFrame) {
00093     return NS_ERROR_NULL_POINTER;
00094   }
00095   nsHTMLScrollFrame* it = new (aPresShell) nsHTMLScrollFrame(aPresShell, aIsRoot);
00096   if (nsnull == it) {
00097     return NS_ERROR_OUT_OF_MEMORY;
00098   }
00099   *aNewFrame = it;
00100   return NS_OK;
00101 }
00102 
00103 nsHTMLScrollFrame::nsHTMLScrollFrame(nsIPresShell* aShell, PRBool aIsRoot)
00104   : nsHTMLContainerFrame(),
00105     mInner(this, aIsRoot)
00106 {
00107 }
00108 
00113 nsIFrame* nsHTMLScrollFrame::GetScrolledFrame() const
00114 {
00115   return mInner.GetScrolledFrame();
00116 }
00117 
00118 nsIScrollableView* nsHTMLScrollFrame::GetScrollableView()
00119 {
00120   return mInner.GetScrollableView();
00121 }
00122 
00123 nsPoint nsHTMLScrollFrame::GetScrollPosition() const
00124 {
00125    nsIScrollableView* s = mInner.GetScrollableView();
00126    nsPoint scrollPosition;
00127    s->GetScrollPosition(scrollPosition.x, scrollPosition.y);
00128    return scrollPosition;
00129 }
00130 
00131 void nsHTMLScrollFrame::ScrollTo(nsPoint aScrollPosition, PRUint32 aFlags)
00132 {
00133    nsIScrollableView* s = mInner.GetScrollableView();
00134    s->ScrollTo(aScrollPosition.x, aScrollPosition.y, aFlags);
00135 }
00136 
00137 nsGfxScrollFrameInner::ScrollbarStyles
00138 nsHTMLScrollFrame::GetScrollbarStyles() const {
00139   return mInner.GetScrollbarStylesFromFrame();
00140 }
00141 
00142 nsMargin nsHTMLScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) {
00143   return mInner.GetDesiredScrollbarSizes(aState);
00144 }
00145 
00146 void nsHTMLScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible, PRBool aHorizontalVisible)
00147 {
00148   mInner.mNeverHasVerticalScrollbar = !aVerticalVisible;
00149   mInner.mNeverHasHorizontalScrollbar = !aHorizontalVisible;
00150 }
00151 
00152 nsIBox* nsHTMLScrollFrame::GetScrollbarBox(PRBool aVertical)
00153 {
00154   return aVertical ? mInner.mVScrollbarBox : mInner.mHScrollbarBox;
00155 }
00156 
00157 NS_IMETHODIMP
00158 nsHTMLScrollFrame::CreateAnonymousContent(nsPresContext* aPresContext,
00159                                          nsISupportsArray& aAnonymousChildren)
00160 {
00161   mInner.CreateAnonymousContent(aAnonymousChildren);
00162   return NS_OK;
00163 }
00164 
00165 NS_IMETHODIMP
00166 nsHTMLScrollFrame::Destroy(nsPresContext* aPresContext)
00167 {
00168   nsIScrollableView *view = mInner.GetScrollableView();
00169   NS_ASSERTION(view, "unexpected null pointer");
00170   if (view)
00171     view->RemoveScrollPositionListener(&mInner);
00172   return nsHTMLContainerFrame::Destroy(aPresContext);
00173 }
00174 
00175 NS_IMETHODIMP
00176 nsHTMLScrollFrame::
00177 SetInitialChildList(nsPresContext* aPresContext,
00178                                    nsIAtom*        aListName,
00179                                    nsIFrame*       aChildList)
00180 {
00181   nsresult  rv = nsHTMLContainerFrame::SetInitialChildList(aPresContext, aListName,
00182                                                            aChildList);
00183   mInner.CreateScrollableView();
00184   mInner.ReloadChildFrames();
00185 
00186   // listen for scroll events.
00187   mInner.GetScrollableView()->AddScrollPositionListener(&mInner);
00188 
00189   return rv;
00190 }
00191 
00192 
00193 NS_IMETHODIMP
00194 nsHTMLScrollFrame::AppendFrames(nsIAtom*  aListName,
00195                                 nsIFrame* aFrameList)
00196 {
00197   NS_ASSERTION(!aListName, "Only main list supported");
00198   mFrames.AppendFrames(nsnull, aFrameList);
00199   mInner.ReloadChildFrames();
00200   return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsHTMLScrollFrame::InsertFrames(nsIAtom*  aListName,
00205                                 nsIFrame* aPrevFrame,
00206                                 nsIFrame* aFrameList)
00207 {
00208   NS_ASSERTION(!aListName, "Only main list supported");
00209   mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
00210   mInner.ReloadChildFrames();
00211   return NS_OK;
00212 }
00213 
00214 NS_IMETHODIMP
00215 nsHTMLScrollFrame::RemoveFrame(nsIAtom*  aListName,
00216                                nsIFrame* aOldFrame)
00217 {
00218   NS_ASSERTION(!aListName, "Only main list supported");
00219   mFrames.DestroyFrame(GetPresContext(), aOldFrame);
00220   mInner.ReloadChildFrames();
00221   return NS_OK;
00222 }
00223 
00224 
00225 NS_IMETHODIMP
00226 nsHTMLScrollFrame::ReplaceFrame(nsIAtom*  aListName,
00227                                 nsIFrame* aOldFrame,
00228                                 nsIFrame* aNewFrame)
00229 {
00230   NS_ASSERTION(!aListName, "Only main list supported");
00231   nsresult rv = nsHTMLContainerFrame::ReplaceFrame(aListName, aOldFrame, aNewFrame);
00232   mInner.ReloadChildFrames();
00233   return rv;
00234 }
00235 
00236 PRIntn
00237 nsHTMLScrollFrame::GetSkipSides() const
00238 {
00239   return 0;
00240 }
00241 
00242 nsIAtom*
00243 nsHTMLScrollFrame::GetType() const
00244 {
00245   return nsLayoutAtoms::scrollFrame; 
00246 }
00247 
00257 struct ScrollReflowState {
00258   const nsHTMLReflowState& mReflowState;
00259   nsBoxLayoutState mBoxState;
00260   nsGfxScrollFrameInner::ScrollbarStyles mStyles;
00261   nsReflowReason mNewReason;
00262   nsMargin mComputedBorder;
00263 
00264   // === Filled in when TryLayout succeeds ===
00265   // The area of the scrollport, in coordinates relative to the scrollframe
00266   nsRect mScrollPortRect;
00267   // The size of the inside-border area
00268   nsSize mInsideBorderSize;
00269   // Taken from kid metrics; ascent from the inner-border top edge
00270   nscoord mAscent;
00271   // Taken from kid metrics; does not include our border widths,
00272   // does include vertical scrollbar if present
00273   nscoord mMaxElementWidth;
00274   // Taken from kid metrics; does not include our border widths,
00275   // does include vertical scrollbar if present
00276   nscoord mMaximumWidth;
00277   // Whether we decided to show the horizontal scrollbar
00278   PRPackedBool mShowHScrollbar;
00279   // Whether we decided to show the vertical scrollbar
00280   PRPackedBool mShowVScrollbar;
00281 
00282   ScrollReflowState(nsIScrollableFrame* aFrame,
00283                     const nsHTMLReflowState& aState, nsHTMLReflowMetrics& aMetrics) :
00284     mReflowState(aState),
00285     mBoxState(aState.frame->GetPresContext(), aState, aMetrics),
00286     mStyles(aFrame->GetScrollbarStyles()) {
00287   }
00288 };
00289 
00290 static nsSize ComputeInsideBorderSize(ScrollReflowState* aState,
00291                                       const nsSize& aDesiredInsideBorderSize)
00292 {
00293   // aDesiredInsideBorderSize is the frame size; i.e., it includes
00294   // borders and padding (but the scrolled child doesn't have
00295   // borders). The scrolled child has the same padding as us.
00296   nscoord contentWidth = aState->mReflowState.mComputedWidth;
00297   if (contentWidth == NS_UNCONSTRAINEDSIZE) {
00298     contentWidth = aDesiredInsideBorderSize.width -
00299       aState->mReflowState.mComputedPadding.LeftRight();
00300   }
00301   nscoord contentHeight = aState->mReflowState.mComputedHeight;
00302   if (contentHeight == NS_UNCONSTRAINEDSIZE) {
00303     contentHeight = aDesiredInsideBorderSize.height -
00304       aState->mReflowState.mComputedPadding.TopBottom();
00305   }
00306 
00307   aState->mReflowState.ApplyMinMaxConstraints(&contentWidth, &contentHeight);
00308   return nsSize(contentWidth + aState->mReflowState.mComputedPadding.LeftRight(),
00309                 contentHeight + aState->mReflowState.mComputedPadding.TopBottom());
00310 }
00311 
00312 static void
00313 GetScrollbarMetrics(nsBoxLayoutState& aState, nsIBox* aBox, nsSize* aMin,
00314                     nsSize* aPref, PRBool aVertical)
00315 {
00316   if (aMin) {
00317     aBox->GetMinSize(aState, *aMin);
00318     nsBox::AddMargin(aBox, *aMin);
00319   }
00320  
00321   if (aPref) {
00322     aBox->GetPrefSize(aState, *aPref);
00323     nsBox::AddMargin(aBox, *aPref);
00324   }
00325 }
00326 
00348 PRBool
00349 nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState,
00350                              const nsHTMLReflowMetrics& aKidMetrics,
00351                              PRBool aAssumeVScroll, PRBool aAssumeHScroll,
00352                              PRBool aForce)
00353 {
00354   if ((aState->mStyles.mVertical == NS_STYLE_OVERFLOW_HIDDEN && aAssumeVScroll) ||
00355       (aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN && aAssumeHScroll)) {
00356     NS_ASSERTION(!aForce, "Shouldn't be forcing a hidden scrollbar to show!");
00357     return PR_FALSE;
00358   }
00359   
00360   nsSize vScrollbarMinSize(0, 0);
00361   nsSize vScrollbarPrefSize(0, 0);
00362   if (mInner.mVScrollbarBox) {
00363     GetScrollbarMetrics(aState->mBoxState, mInner.mVScrollbarBox,
00364                         &vScrollbarMinSize,
00365                         aAssumeVScroll ? &vScrollbarPrefSize : nsnull, PR_TRUE);
00366   }
00367   nscoord vScrollbarDesiredWidth = aAssumeVScroll ? vScrollbarPrefSize.width : 0;
00368   nscoord vScrollbarDesiredHeight = aAssumeVScroll ? vScrollbarPrefSize.height : 0;
00369 
00370   nsSize hScrollbarMinSize(0, 0);
00371   nsSize hScrollbarPrefSize(0, 0);
00372   if (mInner.mHScrollbarBox) {
00373     GetScrollbarMetrics(aState->mBoxState, mInner.mHScrollbarBox,
00374                         &hScrollbarMinSize,
00375                         aAssumeHScroll ? &hScrollbarPrefSize : nsnull, PR_FALSE);
00376   }
00377   nscoord hScrollbarDesiredHeight = aAssumeHScroll ? hScrollbarPrefSize.height : 0;
00378   nscoord hScrollbarDesiredWidth = aAssumeHScroll ? hScrollbarPrefSize.width : 0;
00379 
00380   // First, compute our inside-border size and scrollport size
00381   nsSize desiredInsideBorderSize;
00382   desiredInsideBorderSize.width = vScrollbarDesiredWidth +
00383     PR_MAX(aKidMetrics.width, hScrollbarDesiredWidth);
00384   desiredInsideBorderSize.height = hScrollbarDesiredHeight +
00385     PR_MAX(aKidMetrics.height, vScrollbarDesiredHeight);
00386   aState->mInsideBorderSize =
00387     ComputeInsideBorderSize(aState, desiredInsideBorderSize);
00388   nsSize scrollPortSize = nsSize(PR_MAX(0, aState->mInsideBorderSize.width - vScrollbarDesiredWidth),
00389                                  PR_MAX(0, aState->mInsideBorderSize.height - hScrollbarDesiredHeight));
00390                                                                                 
00391   if (!aForce) {
00392     // If the style is HIDDEN then we already know that aAssumeHScroll is PR_FALSE
00393     if (aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
00394       PRBool wantHScrollbar = aState->mStyles.mHorizontal == NS_STYLE_OVERFLOW_AUTO
00395         ? aKidMetrics.mOverflowArea.XMost() > scrollPortSize.width : PR_TRUE;
00396       if (aState->mInsideBorderSize.height < hScrollbarMinSize.height ||
00397           scrollPortSize.width < hScrollbarMinSize.width)
00398         wantHScrollbar = PR_FALSE;
00399       if (wantHScrollbar != aAssumeHScroll)
00400         return PR_FALSE;
00401     }
00402 
00403     // If the style is HIDDEN then we already know that aAssumeVScroll is PR_FALSE
00404     if (aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
00405       PRBool wantVScrollbar = aState->mStyles.mVertical == NS_STYLE_OVERFLOW_AUTO
00406         ? aKidMetrics.mOverflowArea.YMost() > scrollPortSize.height : PR_TRUE;
00407       if (aState->mInsideBorderSize.width < vScrollbarMinSize.width ||
00408           scrollPortSize.height < vScrollbarMinSize.height)
00409         wantVScrollbar = PR_FALSE;
00410       if (wantVScrollbar != aAssumeVScroll)
00411         return PR_FALSE;
00412     }
00413   }
00414 
00415   nscoord vScrollbarActualWidth = aState->mInsideBorderSize.width - scrollPortSize.width;
00416 
00417   aState->mShowHScrollbar = aAssumeHScroll;
00418   aState->mShowVScrollbar = aAssumeVScroll;
00419   nsPoint scrollPortOrigin(aState->mComputedBorder.left,
00420                            aState->mComputedBorder.top);
00421   if (!mInner.IsScrollbarOnRight()) {
00422     scrollPortOrigin.x += vScrollbarActualWidth;
00423   }
00424   aState->mScrollPortRect = nsRect(scrollPortOrigin, scrollPortSize);
00425   aState->mAscent = aKidMetrics.ascent;
00426   if (aKidMetrics.mComputeMEW) {
00427     // XXXBernd the following code is controversial see bug 295459 and bug
00428     // 234593, however to get the main customer of MEW  - tables happy. It
00429     // seems to be necessary
00430     // It looks at the MEW as the minimum width that the parent has to give its
00431     // children so that the childs margin box can layout its content without
00432     // overflowing the parents content box. If the child has a fixed width
00433     // the MEW will be allways this width regardless whether it makes the grand
00434     // children overflow the child. Please notice that fixed widths for table
00435     // related frames are not covered by this as they mean more a min-width.
00436     //
00437     // This means for scrolling boxes that if the width is auto or percent
00438     // their content box can be squeezed down to 0, as they will either create
00439     // a scrollbar so that content of the scrollframe will not leak out or it
00440     // will cut the content at the frame boundaries.
00441     
00442     // The width of the vertical scrollbar comes out of the budget for the
00443     // content width (see above where we include the scrollbar width before
00444     // we call ComputeInsideBorderSize, which overrides the given
00445     // width with the style computed width if there is one). So allow
00446     // the vertical scrollbar width to be overridden by style information
00447     // here, too.
00448     nscoord minContentWidth =
00449       aState->mReflowState.AdjustIntrinsicMinContentWidthForStyle(vScrollbarActualWidth);
00450     aState->mMaxElementWidth = minContentWidth +
00451       aState->mReflowState.mComputedPadding.LeftRight();
00452     // borders get added on the way out of Reflow()
00453   }
00454   if (aKidMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
00455     // We need to do what we did above: include the vertical scrollbar width in the
00456     // content width before applying style.
00457     nscoord kidMaxWidth = aKidMetrics.mMaximumWidth;
00458     if (kidMaxWidth != NS_UNCONSTRAINEDSIZE) {
00459       nscoord kidContentMaxWidth = kidMaxWidth -
00460         aState->mReflowState.mComputedPadding.LeftRight() + vScrollbarActualWidth;
00461       NS_ASSERTION(kidContentMaxWidth >= 0, "max-width didn't include padding?");
00462       kidMaxWidth = aState->mReflowState.mComputedPadding.LeftRight() +
00463         aState->mReflowState.AdjustIntrinsicContentWidthForStyle(kidContentMaxWidth);
00464     }
00465     aState->mMaximumWidth = kidMaxWidth;
00466     // borders get added on the way out of Reflow()
00467   }
00468   return PR_TRUE;
00469 }
00470 
00471 nsresult
00472 nsHTMLScrollFrame::ReflowScrolledFrame(const ScrollReflowState& aState,
00473                                        PRBool aAssumeHScroll,
00474                                        PRBool aAssumeVScroll,
00475                                        nsHTMLReflowMetrics* aMetrics,
00476                                        PRBool aFirstPass)
00477 {
00478   // these could be NS_UNCONSTRAINEDSIZE ... PR_MIN arithmetic should
00479   // be OK
00480   nscoord paddingLR = aState.mReflowState.mComputedPadding.LeftRight();
00481 
00482   nscoord availWidth = aState.mReflowState.availableWidth;
00483   if (aState.mReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) {
00484     availWidth = aState.mReflowState.mComputedWidth + paddingLR;
00485   } else {
00486     if (aState.mReflowState.mComputedMaxWidth != NS_UNCONSTRAINEDSIZE) {
00487       availWidth = PR_MIN(availWidth,
00488                           aState.mReflowState.mComputedMaxWidth + paddingLR);
00489     }
00490     if (aState.mReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) {
00491       availWidth = PR_MIN(availWidth,
00492                           aState.mReflowState.mComputedWidth + paddingLR);
00493     }
00494   }
00495   if (availWidth != NS_UNCONSTRAINEDSIZE && aAssumeVScroll) {
00496     nsSize vScrollbarPrefSize;
00497     mInner.mVScrollbarBox->GetPrefSize(NS_CONST_CAST(nsBoxLayoutState&, aState.mBoxState),
00498                                        vScrollbarPrefSize);
00499     availWidth = PR_MAX(0, availWidth - vScrollbarPrefSize.width);
00500   }
00501 
00502   nsHTMLReflowState kidReflowState(GetPresContext(), aState.mReflowState,
00503                                    mInner.mScrolledFrame,
00504                                    nsSize(availWidth, NS_UNCONSTRAINEDSIZE),
00505                                    aFirstPass ? aState.mNewReason : eReflowReason_Resize);
00506   kidReflowState.mFlags.mAssumingHScrollbar = aAssumeHScroll;
00507   kidReflowState.mFlags.mAssumingVScrollbar = aAssumeVScroll;
00508 
00509   if (IsRTLTextControl()) {
00510     kidReflowState.mRightEdge = mInner.GetScrolledSize().width;
00511   }
00512   nsReflowStatus status;
00513   nsresult rv = ReflowChild(mInner.mScrolledFrame, GetPresContext(), *aMetrics,
00514                             kidReflowState, 0, 0,
00515                             NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW, status);
00516   // Don't resize or position the view because we're going to resize
00517   // it to the correct size anyway in PlaceScrollArea. Allowing it to
00518   // resize here would size it to the natural height of the frame,
00519   // which will usually be different from the scrollport height;
00520   // invalidating the difference will cause unnecessary repainting.
00521   FinishReflowChild(mInner.mScrolledFrame, GetPresContext(),
00522                     &kidReflowState, *aMetrics, 0, 0,
00523                     NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_SIZE_VIEW);
00524 
00525   // XXX Some frames (e.g., nsObjectFrame, nsFrameFrame, nsTextFrame) don't bother
00526   // setting their mOverflowArea. This is wrong because every frame should
00527   // always set mOverflowArea. In fact nsObjectFrame and nsFrameFrame don't
00528   // support the 'outline' property because of this. Rather than fix the world
00529   // right now, just fix up the overflow area if necessary. Note that we don't
00530   // check NS_FRAME_OUTSIDE_CHILDREN because it could be set even though the
00531   // overflow area doesn't include the frame bounds.
00532   aMetrics->mOverflowArea.UnionRect(aMetrics->mOverflowArea,
00533                                     nsRect(0, 0, aMetrics->width, aMetrics->height));
00534 
00535   return rv;
00536 }
00537 
00538 nsresult
00539 nsHTMLScrollFrame::ReflowContents(ScrollReflowState* aState,
00540                                   const nsHTMLReflowMetrics& aDesiredSize)
00541 {
00542   // Try layouts that keep the vertical scrollbar setting the same,
00543   // first. That will minimize the work we have to do.
00544   PRBool currentlyUsingVScrollbar = mInner.mHasVerticalScrollbar;
00545 
00546   if (aState->mReflowState.reason == eReflowReason_Initial) {
00547     // Set initial vertical scrollbar assumption.
00548     if (aState->mStyles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
00549       currentlyUsingVScrollbar = PR_TRUE;
00550     } else {
00551       // If we're the viewport scrollframe, then let's start out assuming that
00552       // there *is* a vertical scrollbar.
00553       // XXX disable this for now so we can see what the Tp impact of the
00554       // big changes is.
00555       // if (mInner.mIsRoot) {
00556       //   currentlyUsingVScrollbar = PR_TRUE;
00557       // }
00558     }
00559   }
00560 
00561   // Don't assume a vertical scrollbar if we're not allowed to have
00562   // one
00563   PRBool canHaveVerticalScrollbar =
00564     aState->mStyles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
00565   if (!canHaveVerticalScrollbar)
00566     currentlyUsingVScrollbar = PR_FALSE;
00567 
00568   nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags);
00569   nsresult rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
00570                                     &kidDesiredSize, PR_TRUE);
00571   if (NS_FAILED(rv))
00572     return rv;
00573   PRBool didUseScrollbar = currentlyUsingVScrollbar;
00574 
00575   // There's an important special case ... if the child appears to fit
00576   // in the inside-border rect (but overflows the scrollport), we
00577   // should try laying it out without a vertical scrollbar. It will
00578   // usually fit because making the available-width wider will not
00579   // normally make the child taller. (The only situation I can think
00580   // of is when you have a line containing %-width inline replaced
00581   // elements whose percentages sum to more than 100%, so increasing
00582   // the available width makes the line break where it was fitting
00583   // before.) If we don't treat this case specially, then we will
00584   // decide that showing scrollbars is OK because the content
00585   // overflows when we're showing scrollbars and we won't try to
00586   // remove the vertical scrollbar.
00587 
00588   // Detecting when we enter this special case is important for when
00589   // people design layouts that exactly fit the container "most of the
00590   // time".
00591   if (currentlyUsingVScrollbar &&
00592       aState->mStyles.mVertical != NS_STYLE_OVERFLOW_SCROLL &&
00593       aState->mStyles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL) {
00594     nsSize insideBorderSize =
00595       ComputeInsideBorderSize(aState,
00596                               nsSize(kidDesiredSize.width, kidDesiredSize.height));
00597     if (kidDesiredSize.mOverflowArea.XMost() <= insideBorderSize.width &&
00598         kidDesiredSize.mOverflowArea.YMost() <= insideBorderSize.height) {
00599       // Let's pretend we had no vertical scrollbar coming in here
00600       currentlyUsingVScrollbar = PR_FALSE;
00601       rv = ReflowScrolledFrame(*aState, PR_FALSE, currentlyUsingVScrollbar,
00602                                &kidDesiredSize, PR_FALSE);
00603       if (NS_FAILED(rv))
00604         return rv;
00605       didUseScrollbar = PR_FALSE;
00606     }
00607   }
00608 
00609   // First try a layout without a horizontal scrollbar, then with.
00610   if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_FALSE, PR_FALSE))
00611     return NS_OK;
00612   // XXX Adding a horizontal scrollbar could cause absolute children positioned
00613   // relative to the bottom padding-edge to need to be reflowed. But we don't,
00614   // because that would be slow.
00615   if (TryLayout(aState, kidDesiredSize, didUseScrollbar, PR_TRUE, PR_FALSE))
00616     return NS_OK;
00617 
00618   // That didn't work. Try the other setting for the vertical scrollbar.
00619   // But don't try to show a scrollbar if we know there can't be one.
00620   if (currentlyUsingVScrollbar || canHaveVerticalScrollbar) {
00621     nsHTMLReflowMetrics kidRetrySize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags);
00622     rv = ReflowScrolledFrame(*aState, PR_FALSE, !currentlyUsingVScrollbar,
00623                              &kidRetrySize, PR_FALSE);
00624     if (NS_FAILED(rv))
00625       return rv;
00626     didUseScrollbar = !currentlyUsingVScrollbar;
00627     if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_FALSE, PR_FALSE))
00628       return NS_OK;
00629     // XXX Adding a horizontal scrollbar could cause absolute children positioned
00630     // relative to the bottom padding-edge to need to be reflowed. But we don't,
00631     // because that would be slow.
00632     if (TryLayout(aState, kidRetrySize, didUseScrollbar, PR_TRUE, PR_FALSE))
00633       return NS_OK;
00634 
00635     NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
00636   } else {
00637     NS_WARNING("Strange content ... we can't find logically consistent scrollbar settings");
00638   }
00639 
00640   // Fall back to no scrollbars --- even if NS_STYLE_OVERFLOW_SCROLL is
00641   // in effect. They might not fit anyway.
00642   if (didUseScrollbar) {
00643     rv = ReflowScrolledFrame(*aState, PR_FALSE, PR_FALSE, &kidDesiredSize, PR_FALSE);
00644     if (NS_FAILED(rv))
00645       return rv;
00646   }
00647   TryLayout(aState, kidDesiredSize, PR_FALSE, PR_FALSE, PR_TRUE);
00648   return NS_OK;
00649 }
00650 
00651 void
00652 nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState)
00653 {
00654   nsIView* scrollView = mInner.mScrollableView->View();
00655   nsIViewManager* vm = scrollView->GetViewManager();
00656   vm->MoveViewTo(scrollView, aState.mScrollPortRect.x, aState.mScrollPortRect.y);
00657   vm->ResizeView(scrollView, nsRect(nsPoint(0, 0), aState.mScrollPortRect.Size()),
00658                  PR_TRUE);
00659 
00660   // set the origin of childRect to (0,0) even though we might have borders or
00661   // a left-hand-side scrollbar. We've accounted for that by positioning the
00662   // anonymous mScrollableView.
00663   nsRect childOverflow = mInner.mScrolledFrame->GetOverflowRect();
00664   nsRect childRect = nsRect(0, 0,
00665                             PR_MAX(childOverflow.XMost(), aState.mScrollPortRect.width),
00666                             PR_MAX(childOverflow.YMost(), aState.mScrollPortRect.height));
00667   mInner.mScrolledFrame->SetRect(childRect);
00668   // XXX hack! force the scrolled frame to think it has overflow
00669   // to avoid problems with incorrect event targeting.
00670   mInner.mScrolledFrame->AddStateBits(NS_FRAME_OUTSIDE_CHILDREN);
00671 
00672   nsContainerFrame::SyncFrameViewAfterReflow(mInner.mScrolledFrame->GetPresContext(),
00673                                              mInner.mScrolledFrame,
00674                                              mInner.mScrolledFrame->GetView(),
00675                                              &childRect,
00676                                              NS_FRAME_NO_MOVE_VIEW);
00677                                              
00678   mInner.PostOverflowEvents();
00679 }
00680 
00681 PRBool
00682 nsHTMLScrollFrame::IsRTLTextControl()
00683 {
00684 #ifdef IBMBIDI
00685   const nsStyleVisibility* ourVis = GetStyleVisibility();
00686   
00687   if (NS_STYLE_DIRECTION_RTL == ourVis->mDirection) {
00688     nsCOMPtr<nsITextControlFrame> textControl = do_QueryInterface(GetParent());
00689     if (textControl) {
00690       return PR_TRUE;
00691     }
00692   }
00693 #endif // IBMBIDI
00694   return PR_FALSE;
00695 }
00696 
00697 NS_IMETHODIMP
00698 nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
00699                           nsHTMLReflowMetrics&     aDesiredSize,
00700                           const nsHTMLReflowState& aReflowState,
00701                           nsReflowStatus&          aStatus)
00702 {
00703   DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame", aReflowState.reason);
00704   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
00705 
00706   ScrollReflowState state(this, aReflowState, aDesiredSize);
00707   // sanity check: ensure that if we have no scrollbar, we treat it
00708   // as hidden.
00709   if (!mInner.mVScrollbarBox || mInner.mNeverHasVerticalScrollbar)
00710     state.mStyles.mVertical = NS_STYLE_OVERFLOW_HIDDEN;
00711   if (!mInner.mHScrollbarBox || mInner.mNeverHasHorizontalScrollbar)
00712     state.mStyles.mHorizontal = NS_STYLE_OVERFLOW_HIDDEN;
00713 
00714   //------------ Handle Incremental Reflow -----------------
00715   PRBool reflowContents = PR_TRUE;
00716   PRBool reflowHScrollbar = PR_TRUE;
00717   PRBool reflowVScrollbar = PR_TRUE;
00718   PRBool reflowScrollCorner = PR_TRUE;
00719   nsReflowReason reason = aReflowState.reason;
00720 
00721   if (reason == eReflowReason_Incremental) {
00722       nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
00723       // See if it's targeted at us
00724       if (command) {
00725         nsReflowType  reflowType;
00726         command->GetType(reflowType);
00727 
00728         switch (reflowType) {
00729           case eReflowType_StyleChanged:
00730             reason = eReflowReason_StyleChange;
00731             break;
00732 
00733           case eReflowType_ReflowDirty: 
00734             reason = eReflowReason_Dirty;
00735             break;
00736 
00737           default:
00738             NS_ERROR("Unexpected Reflow Type");
00739         }
00740       } else {
00741         reflowContents = PR_FALSE;
00742         reflowHScrollbar = PR_FALSE;
00743         reflowVScrollbar = PR_FALSE;
00744         reflowScrollCorner = PR_FALSE;
00745 
00746         nsReflowPath::iterator iter = aReflowState.path->FirstChild();
00747         nsReflowPath::iterator end = aReflowState.path->EndChildren();
00748         
00749         for ( ; iter != end; ++iter) {
00750           if (*iter == mInner.mScrolledFrame)
00751             reflowContents = PR_TRUE;
00752           else if (*iter == mInner.mHScrollbarBox)
00753             reflowHScrollbar = PR_TRUE;
00754           else if (*iter == mInner.mVScrollbarBox)
00755             reflowVScrollbar = PR_TRUE;
00756           else if (*iter == mInner.mScrollCornerBox)
00757             reflowScrollCorner = PR_TRUE;
00758         }
00759       }
00760   }
00761   state.mNewReason = reason;
00762 
00763   nsRect oldScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
00764   nsRect oldScrolledAreaBounds = mInner.mScrolledFrame->GetView()->GetBounds();
00765   state.mComputedBorder = aReflowState.mComputedBorderPadding -
00766     aReflowState.mComputedPadding;
00767 
00768   nsresult rv = ReflowContents(&state, aDesiredSize);
00769   if (NS_FAILED(rv))
00770     return rv;
00771   
00772 #ifdef IBMBIDI
00773   if (IsRTLTextControl()) { 
00774     nscoord newScrolledWidth =
00775       PR_MAX(mInner.mScrolledFrame->GetOverflowRect().XMost(),
00776              state.mScrollPortRect.width);
00777     if (newScrolledWidth != oldScrolledAreaBounds.width) {
00778       // RTL text controls keep their lines flush right, so we need to reflow
00779       // again with the correct right edge
00780       // XXX I'm not sure why this can't be done in a more general way
00781       // XXX the way this works, we can never get narrower even when content
00782       // is deleted, because the XMost of the frame's overflow area is always
00783       // at least the right edge. But it looks like it has always worked this way.
00784       nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mComputeMEW, aDesiredSize.mFlags);
00785       rv = ReflowScrolledFrame(state, state.mShowHScrollbar, state.mShowVScrollbar,
00786                                &kidDesiredSize, PR_FALSE);
00787       if (NS_FAILED(rv))
00788         return rv;
00789     }
00790   }
00791 #endif
00792 
00793   PlaceScrollArea(state);
00794 
00795   if (!mInner.mSupppressScrollbarUpdate) {
00796     PRBool didHaveHScrollbar = mInner.mHasHorizontalScrollbar;
00797     PRBool didHaveVScrollbar = mInner.mHasVerticalScrollbar;
00798     mInner.mHasHorizontalScrollbar = state.mShowHScrollbar;
00799     mInner.mHasVerticalScrollbar = state.mShowVScrollbar;
00800     nsRect newScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
00801     nsRect newScrolledAreaBounds = mInner.mScrolledFrame->GetView()->GetBounds();
00802     if (reflowHScrollbar || reflowVScrollbar || reflowScrollCorner ||
00803         reason != eReflowReason_Incremental ||
00804         didHaveHScrollbar != state.mShowHScrollbar ||
00805         didHaveVScrollbar != state.mShowVScrollbar ||
00806         oldScrollAreaBounds != newScrollAreaBounds ||
00807         oldScrolledAreaBounds != newScrolledAreaBounds) {
00808       if (mInner.mHasHorizontalScrollbar && !didHaveHScrollbar) {
00809         mInner.AdjustHorizontalScrollbar();
00810       }
00811       mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, state.mShowHScrollbar);
00812       mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, state.mShowVScrollbar);
00813       // place and reflow scrollbars
00814       nsRect insideBorderArea =
00815         nsRect(nsPoint(state.mComputedBorder.left, state.mComputedBorder.top),
00816                state.mInsideBorderSize);
00817       mInner.LayoutScrollbars(state.mBoxState, insideBorderArea,
00818                               oldScrollAreaBounds, state.mScrollPortRect);
00819     }
00820   }
00821   ScrollToRestoredPosition();
00822 
00823   aDesiredSize.width = state.mInsideBorderSize.width +
00824     state.mComputedBorder.LeftRight();
00825   aDesiredSize.height = state.mInsideBorderSize.height +
00826     state.mComputedBorder.TopBottom();
00827   aDesiredSize.ascent = state.mAscent + state.mComputedBorder.top;
00828   if (aDesiredSize.mComputeMEW) {
00829     aDesiredSize.mMaxElementWidth = state.mMaxElementWidth +
00830       state.mComputedBorder.LeftRight();
00831   }
00832   if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
00833     aDesiredSize.mMaximumWidth = state.mMaximumWidth;
00834     if (aDesiredSize.mMaximumWidth != NS_UNCONSTRAINEDSIZE) {
00835       aDesiredSize.mMaximumWidth += state.mComputedBorder.LeftRight();
00836     }
00837   }
00838 
00839   aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent;
00840   aDesiredSize.mOverflowArea = nsRect(0, 0, aDesiredSize.width, aDesiredSize.height);
00841   FinishAndStoreOverflow(&aDesiredSize);
00842 
00843   aStatus = NS_FRAME_COMPLETE;
00844   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
00845   return rv;
00846 }
00847 
00848 NS_IMETHODIMP_(nsrefcnt) 
00849 nsHTMLScrollFrame::AddRef(void)
00850 {
00851   return NS_OK;
00852 }
00853 
00854 NS_IMETHODIMP_(nsrefcnt)
00855 nsHTMLScrollFrame::Release(void)
00856 {
00857     return NS_OK;
00858 }
00859 
00860 #ifdef NS_DEBUG
00861 NS_IMETHODIMP
00862 nsHTMLScrollFrame::GetFrameName(nsAString& aResult) const
00863 {
00864   return MakeFrameName(NS_LITERAL_STRING("HTMLScroll"), aResult);
00865 }
00866 #endif
00867 
00868 #ifdef ACCESSIBILITY
00869 NS_IMETHODIMP nsHTMLScrollFrame::GetAccessible(nsIAccessible** aAccessible)
00870 {
00871   *aAccessible = nsnull;
00872   if (!IsFocusable()) {
00873     return NS_OK;
00874   }
00875   // Focusable via CSS, so needs to be in accessibility hierarchy
00876   nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
00877 
00878   if (accService) {
00879     return accService->CreateHTMLGenericAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
00880   }
00881 
00882   return NS_ERROR_FAILURE;
00883 }
00884 #endif
00885 
00886 void
00887 nsHTMLScrollFrame::CurPosAttributeChanged(nsIContent* aChild,
00888                                           PRInt32 aModType)
00889 {
00890   mInner.CurPosAttributeChanged(aChild, aModType);
00891 }
00892 
00893 nsresult 
00894 nsHTMLScrollFrame::GetContentOf(nsIContent** aContent)
00895 {
00896   *aContent = GetContent();
00897   NS_IF_ADDREF(*aContent);
00898   return NS_OK;
00899 }
00900 
00901 NS_INTERFACE_MAP_BEGIN(nsHTMLScrollFrame)
00902   NS_INTERFACE_MAP_ENTRY(nsIAnonymousContentCreator)
00903 #ifdef NS_DEBUG
00904   NS_INTERFACE_MAP_ENTRY(nsIFrameDebug)
00905 #endif
00906   NS_INTERFACE_MAP_ENTRY(nsIScrollableFrame)
00907   NS_INTERFACE_MAP_ENTRY(nsIScrollableViewProvider)
00908   NS_INTERFACE_MAP_ENTRY(nsIStatefulFrame)
00909 NS_INTERFACE_MAP_END_INHERITING(nsHTMLContainerFrame)
00910 
00911 //----------nsXULScrollFrame-------------------------------------------
00912 
00913 nsresult
00914 NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRBool aIsRoot)
00915 {
00916   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00917   if (nsnull == aNewFrame) {
00918     return NS_ERROR_NULL_POINTER;
00919   }
00920   nsXULScrollFrame* it = new (aPresShell) nsXULScrollFrame(aPresShell, aIsRoot);
00921   if (nsnull == it) {
00922     return NS_ERROR_OUT_OF_MEMORY;
00923   }
00924   *aNewFrame = it;
00925   return NS_OK;
00926 }
00927 
00928 nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, PRBool aIsRoot)
00929   : nsBoxFrame(aShell, aIsRoot),
00930     mInner(this, aIsRoot),
00931     mMaxElementWidth(0)
00932 {
00933     SetLayoutManager(nsnull);
00934 }
00935 
00940 nsIFrame* nsXULScrollFrame::GetScrolledFrame() const
00941 {
00942   return mInner.GetScrolledFrame();
00943 }
00944 
00945 nsIScrollableView* nsXULScrollFrame::GetScrollableView()
00946 {
00947   return mInner.GetScrollableView();
00948 }
00949 
00950 nsPoint nsXULScrollFrame::GetScrollPosition() const
00951 {
00952   nsIScrollableView* s = mInner.GetScrollableView();
00953   nsPoint scrollPosition;
00954   s->GetScrollPosition(scrollPosition.x, scrollPosition.y);
00955   return scrollPosition;
00956 }
00957 
00958 void nsXULScrollFrame::ScrollTo(nsPoint aScrollPosition, PRUint32 aFlags)
00959 {
00960   nsIScrollableView* s = mInner.GetScrollableView();
00961   s->ScrollTo(aScrollPosition.x, aScrollPosition.y, aFlags);
00962 }
00963 
00964 nsGfxScrollFrameInner::ScrollbarStyles
00965 nsXULScrollFrame::GetScrollbarStyles() const {
00966   return mInner.GetScrollbarStylesFromFrame();
00967 }
00968 
00969 nsMargin nsXULScrollFrame::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) {
00970   return mInner.GetDesiredScrollbarSizes(aState);
00971 }
00972 
00973 nsMargin nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState* aState) {
00974   nsMargin result(0, 0, 0, 0);
00975 
00976   if (mVScrollbarBox) {
00977     nsSize size;
00978     mVScrollbarBox->GetPrefSize(*aState, size);
00979     nsBox::AddMargin(mVScrollbarBox, size);
00980 #ifdef IBMBIDI
00981     if (IsScrollbarOnRight())
00982       result.left = size.width;
00983     else
00984 #endif
00985       result.right = size.width;
00986   }
00987 
00988   if (mHScrollbarBox) {
00989     nsSize size;
00990     mHScrollbarBox->GetPrefSize(*aState, size);
00991     nsBox::AddMargin(mHScrollbarBox, size);
00992     // We don't currently support any scripts that would require a scrollbar
00993     // at the top. (Are there any?)
00994     result.bottom = size.height;
00995   }
00996 
00997   return result;
00998 }
00999 
01000 void nsXULScrollFrame::SetScrollbarVisibility(PRBool aVerticalVisible, PRBool aHorizontalVisible)
01001 {
01002   mInner.mNeverHasVerticalScrollbar = !aVerticalVisible;
01003   mInner.mNeverHasHorizontalScrollbar = !aHorizontalVisible;
01004 }
01005 
01006 nsIBox* nsXULScrollFrame::GetScrollbarBox(PRBool aVertical)
01007 {
01008   return aVertical ? mInner.mVScrollbarBox : mInner.mHScrollbarBox;
01009 }
01010 
01011 NS_IMETHODIMP
01012 nsXULScrollFrame::CreateAnonymousContent(nsPresContext* aPresContext,
01013                                          nsISupportsArray& aAnonymousChildren)
01014 {
01015   mInner.CreateAnonymousContent(aAnonymousChildren);
01016   return NS_OK;
01017 }
01018 
01019 NS_IMETHODIMP
01020 nsXULScrollFrame::Destroy(nsPresContext* aPresContext)
01021 {
01022   nsIScrollableView *view = mInner.GetScrollableView();
01023   NS_ASSERTION(view, "unexpected null pointer");
01024   if (view)
01025     view->RemoveScrollPositionListener(&mInner);
01026   return nsBoxFrame::Destroy(aPresContext);
01027 }
01028 
01029 NS_IMETHODIMP
01030 nsXULScrollFrame::SetInitialChildList(nsPresContext* aPresContext,
01031                                    nsIAtom*        aListName,
01032                                    nsIFrame*       aChildList)
01033 {
01034   nsresult  rv = nsBoxFrame::SetInitialChildList(aPresContext, aListName,
01035                                                            aChildList);
01036 
01037   mInner.CreateScrollableView();
01038   mInner.ReloadChildFrames();
01039 
01040   // listen for scroll events.
01041   mInner.GetScrollableView()->AddScrollPositionListener(&mInner);
01042 
01043   return rv;
01044 }
01045 
01046 
01047 NS_IMETHODIMP
01048 nsXULScrollFrame::AppendFrames(nsIAtom*        aListName,
01049                                nsIFrame*       aFrameList)
01050 {
01051   nsresult rv = nsBoxFrame::AppendFrames(aListName, aFrameList);
01052   mInner.ReloadChildFrames();
01053   return rv;
01054 }
01055 
01056 NS_IMETHODIMP
01057 nsXULScrollFrame::InsertFrames(nsIAtom*        aListName,
01058                                nsIFrame*       aPrevFrame,
01059                                nsIFrame*       aFrameList)
01060 {
01061   nsresult rv = nsBoxFrame::InsertFrames(aListName, aPrevFrame, aFrameList);
01062   mInner.ReloadChildFrames();
01063   return rv;
01064 }
01065 
01066 NS_IMETHODIMP
01067 nsXULScrollFrame::RemoveFrame(nsIAtom*        aListName,
01068                               nsIFrame*       aOldFrame)
01069 {
01070   nsresult rv = nsBoxFrame::RemoveFrame(aListName, aOldFrame);
01071   mInner.ReloadChildFrames();
01072   return rv;
01073 }
01074 
01075 
01076 NS_IMETHODIMP
01077 nsXULScrollFrame::ReplaceFrame(nsIAtom*        aListName,
01078                                nsIFrame*       aOldFrame,
01079                                nsIFrame*       aNewFrame)
01080 {
01081   nsresult rv = nsBoxFrame::ReplaceFrame(aListName, aOldFrame, aNewFrame);
01082   mInner.ReloadChildFrames();
01083   return rv;
01084 }
01085 
01086 NS_IMETHODIMP
01087 nsXULScrollFrame::GetPadding(nsMargin& aMargin)
01088 {
01089    aMargin.SizeTo(0,0,0,0);
01090    return NS_OK;
01091 }
01092 
01093 PRIntn
01094 nsXULScrollFrame::GetSkipSides() const
01095 {
01096   return 0;
01097 }
01098 
01099 nsIAtom*
01100 nsXULScrollFrame::GetType() const
01101 {
01102   return nsLayoutAtoms::scrollFrame; 
01103 }
01104 
01105 NS_IMETHODIMP
01106 nsXULScrollFrame::GetAscent(nsBoxLayoutState& aState, nscoord& aAscent)
01107 {
01108   aAscent = 0;
01109   if (!mInner.mScrolledFrame)
01110     return NS_OK;
01111 
01112   nsresult rv = mInner.mScrolledFrame->GetAscent(aState, aAscent);
01113   nsMargin m(0,0,0,0);
01114   GetBorderAndPadding(m);
01115   aAscent += m.top;
01116   GetMargin(m);
01117   aAscent += m.top;
01118   GetInset(m);
01119   aAscent += m.top;
01120 
01121   return rv;
01122 }
01123 
01124 NS_IMETHODIMP
01125 nsXULScrollFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize)
01126 {
01127 #ifdef DEBUG_LAYOUT
01128   PropagateDebug(aState);
01129 #endif
01130 
01131   nsGfxScrollFrameInner::ScrollbarStyles styles = GetScrollbarStyles();
01132 
01133   nsSize vSize(0,0);
01134   if (mInner.mVScrollbarBox &&
01135       styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
01136      mInner.mVScrollbarBox->GetPrefSize(aState, vSize);
01137      nsBox::AddMargin(mInner.mVScrollbarBox, vSize);
01138   }
01139    
01140   nsSize hSize(0,0);
01141   if (mInner.mHScrollbarBox &&
01142       styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
01143      mInner.mHScrollbarBox->GetPrefSize(aState, hSize);
01144      nsBox::AddMargin(mInner.mHScrollbarBox, hSize);
01145   }
01146 
01147   nsresult rv = mInner.mScrolledFrame->GetPrefSize(aState, aSize);
01148 
01149   // scrolled frames don't have their own margins
01150 
01151   aSize.width += vSize.width;
01152   aSize.height += hSize.height;
01153 
01154   AddBorderAndPadding(aSize);
01155   AddInset(aSize);
01156   nsIBox::AddCSSPrefSize(aState, this, aSize);
01157 
01158   return rv;
01159 }
01160 
01161 NS_IMETHODIMP
01162 nsXULScrollFrame::GetMinSize(nsBoxLayoutState& aState, nsSize& aSize)
01163 {
01164 #ifdef DEBUG_LAYOUT
01165   PropagateDebug(aState);
01166 #endif
01167 
01168   aSize = mInner.mScrolledFrame->GetMinSizeForScrollArea(aState);
01169 
01170   nsGfxScrollFrameInner::ScrollbarStyles styles = GetScrollbarStyles();
01171      
01172   if (mInner.mVScrollbarBox &&
01173       styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) {
01174     nsSize vSize(0,0);
01175     mInner.mVScrollbarBox->GetMinSize(aState, vSize);
01176      AddMargin(mInner.mVScrollbarBox, vSize);
01177      aSize.width += vSize.width;
01178      if (aSize.height < vSize.height)
01179         aSize.height = vSize.height;
01180   }
01181         
01182   if (mInner.mHScrollbarBox &&
01183       styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) {
01184      nsSize hSize(0,0);
01185      mInner.mHScrollbarBox->GetMinSize(aState, hSize);
01186      AddMargin(mInner.mHScrollbarBox, hSize);
01187      aSize.height += hSize.height;
01188      if (aSize.width < hSize.width)
01189         aSize.width = hSize.width;
01190   }
01191 
01192   AddBorderAndPadding(aSize);
01193   AddInset(aSize);
01194   nsIBox::AddCSSMinSize(aState, this, aSize);
01195   return NS_OK;
01196 }
01197 
01198 NS_IMETHODIMP
01199 nsXULScrollFrame::GetMaxSize(nsBoxLayoutState& aState, nsSize& aSize)
01200 {
01201 #ifdef DEBUG_LAYOUT
01202   PropagateDebug(aState);
01203 #endif
01204 
01205   aSize.width = NS_INTRINSICSIZE;
01206   aSize.height = NS_INTRINSICSIZE;
01207 
01208   AddBorderAndPadding(aSize);
01209   AddInset(aSize);
01210   nsIBox::AddCSSMaxSize(aState, this, aSize);
01211   return NS_OK;
01212 }
01213 
01214 NS_IMETHODIMP
01215 nsXULScrollFrame::Reflow(nsPresContext*      aPresContext,
01216                      nsHTMLReflowMetrics&     aDesiredSize,
01217                      const nsHTMLReflowState& aReflowState,
01218                      nsReflowStatus&          aStatus)
01219 {
01220   DO_GLOBAL_REFLOW_COUNT("nsXULScrollFrame", aReflowState.reason);
01221   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
01222 
01223   // if there is a max element request then set it to -1 so we can see if it gets set
01224   if (aDesiredSize.mComputeMEW)
01225   {
01226     aDesiredSize.mMaxElementWidth = -1;
01227   }
01228 
01229   nsresult rv = nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
01230 
01231   if (aDesiredSize.mComputeMEW)
01232   {
01233     nsStyleUnit widthUnit = GetStylePosition()->mWidth.GetUnit();
01234     if (widthUnit == eStyleUnit_Percent || widthUnit == eStyleUnit_Auto) {
01235       nsMargin border = aReflowState.mComputedBorderPadding;
01236       aDesiredSize.mMaxElementWidth = border.right + border.left;
01237       mMaxElementWidth = aDesiredSize.mMaxElementWidth;
01238     } else {
01239       // if not set then use the cached size. If set then set it.
01240       if (aDesiredSize.mMaxElementWidth == -1)
01241         aDesiredSize.mMaxElementWidth = mMaxElementWidth;
01242       else
01243         mMaxElementWidth = aDesiredSize.mMaxElementWidth;
01244     }
01245   }
01246   
01247   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
01248   return rv;
01249 }
01250 
01251 NS_IMETHODIMP_(nsrefcnt) 
01252 nsXULScrollFrame::AddRef(void)
01253 {
01254   return NS_OK;
01255 }
01256 
01257 NS_IMETHODIMP_(nsrefcnt)
01258 nsXULScrollFrame::Release(void)
01259 {
01260     return NS_OK;
01261 }
01262 
01263 #ifdef NS_DEBUG
01264 NS_IMETHODIMP
01265 nsXULScrollFrame::GetFrameName(nsAString& aResult) const
01266 {
01267   return MakeFrameName(NS_LITERAL_STRING("XULScroll"), aResult);
01268 }
01269 #endif
01270 
01271 void nsXULScrollFrame::CurPosAttributeChanged(nsIContent* aChild, PRInt32 aModType)
01272 {
01273   mInner.CurPosAttributeChanged(aChild, aModType);
01274 }
01275 
01276 NS_IMETHODIMP
01277 nsXULScrollFrame::DoLayout(nsBoxLayoutState& aState)
01278 {
01279   PRUint32 flags = aState.LayoutFlags();
01280   nsresult rv = Layout(aState);
01281   aState.SetLayoutFlags(flags);
01282 
01283   nsBox::DoLayout(aState);
01284   return rv;
01285 }
01286 
01287 
01288 nsresult 
01289 nsXULScrollFrame::GetContentOf(nsIContent** aContent)
01290 {
01291   *aContent = GetContent();
01292   NS_IF_ADDREF(*aContent);
01293   return NS_OK;
01294 }
01295 
01296 NS_INTERFACE_MAP_BEGIN(nsXULScrollFrame)
01297   NS_INTERFACE_MAP_ENTRY(nsIAnonymousContentCreator)
01298 #ifdef NS_DEBUG
01299   NS_INTERFACE_MAP_ENTRY(nsIFrameDebug)
01300 #endif
01301   NS_INTERFACE_MAP_ENTRY(nsIScrollableFrame)
01302   NS_INTERFACE_MAP_ENTRY(nsIScrollableViewProvider)
01303   NS_INTERFACE_MAP_ENTRY(nsIStatefulFrame)
01304 NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
01305 
01306 
01307 
01308 //-------------------- Inner ----------------------
01309 
01310 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter, PRBool aIsRoot)
01311   : mScrollableView(nsnull),
01312     mHScrollbarBox(nsnull),
01313     mVScrollbarBox(nsnull),
01314     mScrolledFrame(nsnull),
01315     mScrollCornerBox(nsnull),
01316     mOuter(aOuter),
01317     mOnePixel(20),
01318     mRestoreRect(-1, -1, -1, -1),
01319     mLastPos(-1, -1),
01320     mLastDir(-1),
01321     mNeverHasVerticalScrollbar(PR_FALSE),
01322     mNeverHasHorizontalScrollbar(PR_FALSE),
01323     mHasVerticalScrollbar(PR_FALSE), 
01324     mHasHorizontalScrollbar(PR_FALSE),
01325     mViewInitiatedScroll(PR_FALSE),
01326     mFrameInitiatedScroll(PR_FALSE),
01327     mDidHistoryRestore(PR_FALSE),
01328     mIsRoot(aIsRoot),
01329     mSupppressScrollbarUpdate(PR_FALSE),
01330     mHorizontalOverflow(PR_FALSE),
01331     mVerticalOverflow(PR_FALSE)
01332 {
01333 }
01334 
01335 nsGfxScrollFrameInner::~nsGfxScrollFrameInner()
01336 {
01337   if (mScrollEventQueue) {
01338     mScrollEventQueue->RevokeEvents(this);
01339   }
01340 }
01341 
01342 NS_IMETHODIMP_(nsrefcnt) nsGfxScrollFrameInner::AddRef(void)
01343 {
01344   return 1;
01345 }
01346 
01347 NS_IMETHODIMP_(nsrefcnt) nsGfxScrollFrameInner::Release(void)
01348 {
01349   return 1;
01350 }
01351 
01352 NS_IMPL_QUERY_INTERFACE1(nsGfxScrollFrameInner, nsIScrollPositionListener)
01353 
01354 PRBool
01355 nsGfxScrollFrameInner::NeedsClipWidget() const
01356 {
01357   // Scrollports contained in form controls (e.g., listboxes) don't get
01358   // widgets.
01359   for (nsIFrame* parentFrame = mOuter; parentFrame;
01360        parentFrame = parentFrame->GetParent()) {
01361     nsIFormControlFrame* fcFrame;
01362     if ((NS_SUCCEEDED(parentFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame)))) {
01363       return PR_FALSE;
01364     }
01365   }
01366 
01367   // Scrollports that don't ever show associated scrollbars don't get
01368   // widgets, because they will seldom actually be scrolled.
01369   nsIScrollableFrame *scrollableFrame;
01370   CallQueryInterface(mOuter, &scrollableFrame);
01371   ScrollbarStyles scrollbars = scrollableFrame->GetScrollbarStyles();
01372   if ((scrollbars.mHorizontal == NS_STYLE_OVERFLOW_HIDDEN
01373        || scrollbars.mHorizontal == NS_STYLE_OVERFLOW_VISIBLE)
01374       && (scrollbars.mVertical == NS_STYLE_OVERFLOW_HIDDEN
01375           || scrollbars.mVertical == NS_STYLE_OVERFLOW_VISIBLE)) {
01376     return PR_FALSE;
01377   }
01378  
01379   return PR_TRUE;
01380 }
01381 
01382 nsresult
01383 nsGfxScrollFrameInner::GetChildContentAndOffsetsFromPoint(nsPresContext* aCX,
01384                                                           const nsPoint&  aPoint,
01385                                                           nsIContent **   aNewContent,
01386                                                           PRInt32&        aContentOffset,
01387                                                           PRInt32&        aContentOffsetEnd,
01388                                                           PRBool&         aBeginFrameContent)
01389 {
01390   // We need to overrride this to ensure that scrollbars are ignored
01391 
01392   // Since we definitely have a view, aPoint is relative to this frame's view. We
01393   // need to make it relative to the scrolled frame.
01394   nsPoint point = aPoint - mScrollableView->View()->GetOffsetTo(mOuter->GetView());
01395 
01396   return mScrolledFrame->GetContentAndOffsetsFromPoint(aCX, point, aNewContent,
01397                                                        aContentOffset, aContentOffsetEnd, 
01398                                                        aBeginFrameContent);
01399 }
01400 
01401 void
01402 nsGfxScrollFrameInner::CreateScrollableView()
01403 {
01404   nsIView* outerView = mOuter->GetView();
01405   NS_ASSERTION(outerView, "scrollframes must have views");
01406   nsIViewManager* viewManager = outerView->GetViewManager();
01407   mScrollableView = viewManager->CreateScrollableView(mOuter->GetRect(), outerView);
01408   if (!mScrollableView)
01409     return;
01410 
01411   nsIView* view = mScrollableView->View();
01412 
01413   // Insert the view into the view hierarchy
01414   viewManager->InsertChild(outerView, view, nsnull, PR_TRUE);
01415 
01416   // Have the scrolling view create its internal widgets
01417   if (NeedsClipWidget()) {
01418     mScrollableView->CreateScrollControls(); 
01419   }
01420 }
01421 
01422 static void HandleScrollPref(nsIScrollable *aScrollable, PRInt32 aOrientation,
01423                              PRUint8& aValue)
01424 {
01425   PRInt32 pref;
01426   aScrollable->GetDefaultScrollbarPreferences(aOrientation, &pref);
01427   switch (pref) {
01428     case nsIScrollable::Scrollbar_Auto:
01429       // leave |aValue| untouched
01430       break;
01431     case nsIScrollable::Scrollbar_Never:
01432       aValue = NS_STYLE_OVERFLOW_HIDDEN;
01433       break;
01434     case nsIScrollable::Scrollbar_Always:
01435       aValue = NS_STYLE_OVERFLOW_SCROLL;
01436       break;
01437   }
01438 }
01439 
01440 nsIView*
01441 nsGfxScrollFrameInner::GetParentViewForChildFrame(nsIFrame* aFrame) const
01442 {
01443   if (aFrame->GetContent() == mOuter->GetContent()) {
01444     NS_ASSERTION(mScrollableView, "Scrollable view should have been created by now");
01445     // scrolled frame, put it under our anonymous view
01446     return mScrollableView->View();
01447   }
01448   // scrollbars and stuff; put them under our regular view
01449   return mOuter->GetView();
01450 }
01451 
01452 nsGfxScrollFrameInner::ScrollbarStyles
01453 nsGfxScrollFrameInner::GetScrollbarStylesFromFrame() const
01454 {
01455   ScrollbarStyles result;
01456   if (mIsRoot) {
01457     nsPresContext *presContext = mOuter->GetPresContext();
01458     result = presContext->GetViewportOverflowOverride();
01459 
01460     nsCOMPtr<nsISupports> container = presContext->GetContainer();
01461     if (container) {
01462       nsCOMPtr<nsIScrollable> scrollable = do_QueryInterface(container);
01463       HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_X,
01464                        result.mHorizontal);
01465       HandleScrollPref(scrollable, nsIScrollable::ScrollOrientation_Y,
01466                        result.mVertical);
01467     }
01468   } else {
01469     const nsStyleDisplay *disp = mOuter->GetStyleDisplay();
01470     result.mHorizontal = disp->mOverflowX;
01471     result.mVertical = disp->mOverflowY;
01472   }
01473 
01474   NS_ASSERTION(result.mHorizontal != NS_STYLE_OVERFLOW_VISIBLE &&
01475                result.mHorizontal != NS_STYLE_OVERFLOW_CLIP &&
01476                result.mVertical != NS_STYLE_OVERFLOW_VISIBLE &&
01477                result.mVertical != NS_STYLE_OVERFLOW_CLIP,
01478                "scrollbars should not have been created");
01479   return result;
01480 }
01481 
01489 void
01490 nsGfxScrollFrameInner::ScrollToRestoredPosition()
01491 {
01492   nsIScrollableView* scrollingView = GetScrollableView();
01493   if (!scrollingView) {
01494     return;
01495   }
01496   if (mRestoreRect.y == -1 || mLastPos.x == -1 || mLastPos.y == -1) {
01497     return;
01498   }
01499   // make sure our scroll position did not change for where we last put
01500   // it. if it does then the user must have moved it, and we no longer
01501   // need to restore.
01502   nscoord x = 0;
01503   nscoord y = 0;
01504   scrollingView->GetScrollPosition(x, y);
01505 
01506   // if we didn't move, we still need to restore
01507   if (x == mLastPos.x && y == mLastPos.y) {
01508     nsRect childRect(0, 0, 0, 0);
01509     nsIView* child = nsnull;
01510     nsresult rv = scrollingView->GetScrolledView(child);
01511     if (NS_SUCCEEDED(rv) && child)
01512       childRect = child->GetBounds();
01513 
01514     PRInt32 cx, cy, x, y;
01515     scrollingView->GetScrollPosition(cx,cy);
01516 
01517     x = (int)
01518       (((float)childRect.width / mRestoreRect.width) * mRestoreRect.x);
01519     y = (int)
01520       (((float)childRect.height / mRestoreRect.height) * mRestoreRect.y);
01521 
01522     // if our position is greater than the scroll position, scroll.
01523     // remember that we could be incrementally loading so we may enter
01524     // and scroll many times.
01525     if (y > cy || x > cx) {
01526       scrollingView->ScrollTo(x, y, 0);
01527       // scrollpostion goes from twips to pixels. this fixes any roundoff
01528       // problems.
01529       scrollingView->GetScrollPosition(mLastPos.x, mLastPos.y);
01530     } else {
01531       // if we reached the position then stop
01532       mRestoreRect.y = -1;
01533       mLastPos.x = -1;
01534       mLastPos.y = -1;
01535     }
01536   } else {
01537     // user moved the position, so we won't need to restore
01538     mLastPos.x = -1;
01539     mLastPos.y = -1;
01540   }
01541 }
01542 
01543 struct nsAsyncScrollPortEvent : public PLEvent
01544 {
01545 public:
01546   // nsAsyncScrollPortEvent owns aEvent.
01547   nsAsyncScrollPortEvent(nsIContent* aTarget, nsPresContext* aPresContext,
01548                          nsScrollPortEvent* aEvent)
01549   : mTarget(aTarget), mPresContext(aPresContext), mEvent(aEvent) {}
01550 
01551   void HandleEvent() {
01552     if (mTarget) {
01553       nsEventStatus status = nsEventStatus_eIgnore;
01554       mTarget->HandleDOMEvent(mPresContext, mEvent, nsnull, NS_EVENT_FLAG_INIT,
01555                               &status);
01556     }
01557   }
01558 private:
01559   nsCOMPtr<nsIContent>         mTarget;
01560   nsRefPtr<nsPresContext>      mPresContext;
01561   nsAutoPtr<nsScrollPortEvent> mEvent;
01562 };
01563 
01564 static void* PR_CALLBACK HandleAsyncScrollPortEvent(PLEvent* aEvent)
01565 {
01566   NS_STATIC_CAST(nsAsyncScrollPortEvent*, aEvent)->HandleEvent();
01567   return nsnull;
01568 }
01569 static void PR_CALLBACK DestroyAsyncScrollPortEvent(PLEvent* aEvent)
01570 {
01571   delete NS_STATIC_CAST(nsAsyncScrollPortEvent*, aEvent);
01572 }
01573 
01574 void
01575 nsGfxScrollFrameInner::PostScrollPortEvent(PRBool aOverflow, nsScrollPortEvent::orientType aType)
01576 {
01577   nsScrollPortEvent* event = new nsScrollPortEvent(PR_TRUE, aOverflow ?
01578                                                    NS_SCROLLPORT_OVERFLOW :
01579                                                    NS_SCROLLPORT_UNDERFLOW,
01580                                                    nsnull);
01581   if (!event) {
01582     return;
01583   }
01584   event->orient = aType;
01585   nsCOMPtr<nsIEventQueueService> eventService =
01586     do_GetService(kEventQueueServiceCID);
01587   if (eventService) {
01588     nsCOMPtr<nsIEventQueue> eventQueue;
01589     eventService->GetThreadEventQueue(PR_GetCurrentThread(),
01590                                       getter_AddRefs(eventQueue));
01591     if (eventQueue) {
01592       nsAsyncScrollPortEvent* scrollPortEvent =
01593         new nsAsyncScrollPortEvent(mOuter->GetContent(), mOuter->GetPresContext(),
01594                                  event);
01595       if (scrollPortEvent) {
01596         PL_InitEvent(scrollPortEvent, nsnull,
01597                      ::HandleAsyncScrollPortEvent,
01598                      ::DestroyAsyncScrollPortEvent);
01599         if (NS_SUCCEEDED(eventQueue->PostEvent(scrollPortEvent))) {
01600           return;
01601         }
01602         PL_DestroyEvent(scrollPortEvent);
01603         return;
01604       }
01605     }
01606   }
01607   delete event;
01608 }
01609 
01610 void
01611 nsGfxScrollFrameInner::ReloadChildFrames()
01612 {
01613   mScrolledFrame = nsnull;
01614   mHScrollbarBox = nsnull;
01615   mVScrollbarBox = nsnull;
01616   mScrollCornerBox = nsnull;
01617 
01618   nsIFrame* frame = mOuter->GetFirstChild(nsnull);
01619   while (frame) {
01620     nsIContent* content = frame->GetContent();
01621     if (content == mOuter->GetContent()) {
01622       NS_ASSERTION(!mScrolledFrame, "Already found the scrolled frame");
01623       mScrolledFrame = frame;
01624     } else {
01625       nsAutoString value;
01626       if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None,
01627                                                         nsXULAtoms::orient, value)) {
01628         // probably a scrollbar then
01629         if (value.LowerCaseEqualsLiteral("horizontal")) {
01630           NS_ASSERTION(!mHScrollbarBox, "Found multiple horizontal scrollbars?");
01631           mHScrollbarBox = frame;
01632         } else {
01633           NS_ASSERTION(!mVScrollbarBox, "Found multiple vertical scrollbars?");
01634           mVScrollbarBox = frame;
01635         }
01636       } else {
01637         // probably a scrollcorner
01638         NS_ASSERTION(!mScrollCornerBox, "Found multiple scrollcorners");
01639         mScrollCornerBox = frame;
01640       }
01641     }
01642 
01643     frame = frame->GetNextSibling();
01644   }
01645 }
01646   
01647 void
01648 nsGfxScrollFrameInner::CreateAnonymousContent(nsISupportsArray& aAnonymousChildren)
01649 {
01650   nsPresContext* presContext = mOuter->GetPresContext();
01651   nsIFrame* parent = mOuter->GetParent();
01652 
01653   // Don't create scrollbars if we're printing/print previewing
01654   // Get rid of this code when printing moves to its own presentation
01655   if (presContext->IsPaginated()) {
01656     // allow scrollbars if this is the child of the viewport, because
01657     // we must be the scrollbars for the print preview window
01658     if (!mIsRoot) {
01659       mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = PR_TRUE;
01660       return;
01661     }
01662   }
01663 
01664   nsIScrollableFrame *scrollable;
01665   CallQueryInterface(mOuter, &scrollable);
01666 
01667   // At this stage in frame construction, the document element and/or
01668   // BODY overflow styles have not yet been propagated to the
01669   // viewport. So GetScrollbarStylesFromFrame called here will only
01670   // take into account the scrollbar preferences set on the docshell.
01671   // Thus if no scrollbar preferences are set on the docshell, we will
01672   // always create scrollbars, which means later dynamic changes to
01673   // propagated overflow styles will show or hide scrollbars on the
01674   // viewport without requiring frame reconstruction of the viewport
01675   // (good!).
01676 
01677   // XXX On the other hand, if scrolling="no" is set on the container
01678   // we won't create scrollbars here so no scrollbars will ever be
01679   // created even if the container's scrolling attribute is later
01680   // changed. However, this has never been supported.
01681   ScrollbarStyles styles = scrollable->GetScrollbarStyles();
01682   PRBool canHaveHorizontal = styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN;
01683   PRBool canHaveVertical = styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN;
01684   if (!canHaveHorizontal && !canHaveVertical)
01685     // Nothing to do.
01686     return;
01687 
01688   // The anonymous <div> used by <inputs> never gets scrollbars.
01689   nsCOMPtr<nsITextControlFrame> textFrame(do_QueryInterface(parent));
01690   if (textFrame) {
01691     // Make sure we are not a text area.
01692     nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement(do_QueryInterface(parent->GetContent()));
01693     if (!textAreaElement) {
01694       mNeverHasVerticalScrollbar = mNeverHasHorizontalScrollbar = PR_TRUE;
01695       return;
01696     }
01697   }
01698 
01699   nsNodeInfoManager *nodeInfoManager =
01700     presContext->GetDocument()->NodeInfoManager();
01701   nsCOMPtr<nsINodeInfo> nodeInfo;
01702   nodeInfoManager->GetNodeInfo(nsXULAtoms::scrollbar, nsnull,
01703                                kNameSpaceID_XUL, getter_AddRefs(nodeInfo));
01704 
01705   nsCOMPtr<nsIContent> content;
01706 
01707   if (canHaveHorizontal) {
01708     NS_NewElement(getter_AddRefs(content), kNameSpaceID_XUL, nodeInfo);
01709     content->SetAttr(kNameSpaceID_None, nsXULAtoms::orient,
01710                      NS_LITERAL_STRING("horizontal"), PR_FALSE);
01711     aAnonymousChildren.AppendElement(content);
01712   }
01713 
01714   if (canHaveVertical) {
01715     NS_NewElement(getter_AddRefs(content), kNameSpaceID_XUL, nodeInfo);
01716     content->SetAttr(kNameSpaceID_None, nsXULAtoms::orient,
01717                      NS_LITERAL_STRING("vertical"), PR_FALSE);
01718     aAnonymousChildren.AppendElement(content);
01719   }
01720 
01721   if (canHaveHorizontal && canHaveVertical) {
01722     nodeInfoManager->GetNodeInfo(nsXULAtoms::scrollcorner, nsnull,
01723                                  kNameSpaceID_XUL, getter_AddRefs(nodeInfo));
01724     NS_NewElement(getter_AddRefs(content), kNameSpaceID_XUL, nodeInfo);
01725     aAnonymousChildren.AppendElement(content);
01726   }
01727 }
01728 
01729 NS_IMETHODIMP
01730 nsGfxScrollFrameInner::ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY)
01731 {
01732    // Do nothing.
01733    return NS_OK;
01734 }
01735 
01742 void
01743 nsGfxScrollFrameInner::InternalScrollPositionDidChange(nscoord aX, nscoord aY)
01744 {
01745   if (mVScrollbarBox)
01746     SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, aY);
01747   
01748   if (mHScrollbarBox)
01749     SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, aX);
01750 }
01751 
01755 NS_IMETHODIMP
01756 nsGfxScrollFrameInner::ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY)
01757 {
01758   NS_ASSERTION(!mViewInitiatedScroll, "Cannot reenter ScrollPositionDidChange");
01759 
01760   mViewInitiatedScroll = PR_TRUE;
01761   InternalScrollPositionDidChange(aX, aY);
01762   mViewInitiatedScroll = PR_FALSE;
01763   
01764   PostScrollEvent();
01765   
01766   return NS_OK;
01767 }
01768 
01769 void nsGfxScrollFrameInner::CurPosAttributeChanged(nsIContent* aContent, PRInt32 aModType)
01770 {
01771   NS_ASSERTION(aContent, "aContent must not be null");
01772 
01773   // Attribute changes on the scrollbars happen in one of three ways:
01774   // 1) The scrollbar changed the attribute in response to some user event
01775   // 2) We changed the attribute in response to a ScrollPositionDidChange
01776   // callback from the scrolling view
01777   // 3) We changed the attribute to adjust the scrollbars for the start
01778   // of a smooth scroll operation
01779   //
01780   // In case 2), we don't need to scroll the view because the scrolling
01781   // has already happened. In case 3) we don't need to scroll because
01782   // we're just adjusting the scrollbars back to the correct setting
01783   // for the view.
01784   //
01785   // Cases 1) and 3) do not indicate that actual scrolling has happened. Only
01786   // case 2) indicates actual scrolling. Therefore we do not fire onscroll
01787   // here, but in ScrollPositionDidChange.
01788   // 
01789   // We used to detect this case implicitly because we'd compare the
01790   // scrollbar attributes with the view's current scroll position and
01791   // bail out if they were equal. But that approach is fragile; it can
01792   // fail when, for example, the view scrolls horizontally and
01793   // vertically simultaneously; we'll get here when only the vertical
01794   // attribute has been set, so the attributes and the view scroll
01795   // position don't yet agree, and we'd tell the view to scroll to the
01796   // new vertical position and the old horizontal position! Even worse
01797   // things could happen when smooth scrolling got involved ... crashes
01798   // and other terrors.
01799   if (mViewInitiatedScroll || mFrameInitiatedScroll) return;
01800 
01801   nsIContent* vcontent = mVScrollbarBox ? mVScrollbarBox->GetContent() : nsnull;
01802   nsIContent* hcontent = mHScrollbarBox ? mHScrollbarBox->GetContent() : nsnull;
01803 
01804   if (hcontent == aContent || vcontent == aContent)
01805   {
01806     nscoord x = 0;
01807     nscoord y = 0;
01808 
01809     nsAutoString value;
01810     if (hcontent && NS_CONTENT_ATTR_HAS_VALUE == hcontent->GetAttr(kNameSpaceID_None, nsXULAtoms::curpos, value))
01811     {
01812       PRInt32 error;
01813 
01814       // convert it to an integer
01815       x = value.ToInteger(&error);
01816     }
01817 
01818     if (vcontent && NS_CONTENT_ATTR_HAS_VALUE == vcontent->GetAttr(kNameSpaceID_None, nsXULAtoms::curpos, value))
01819     {
01820       PRInt32 error;
01821 
01822       // convert it to an integer
01823       y = value.ToInteger(&error);
01824     }
01825 
01826     // Make sure the scrollbars indeed moved before firing the event.
01827     // I think it is OK to prevent the call to ScrollbarChanged()
01828     // if we didn't actually move. The following check is the first
01829     // thing ScrollbarChanged() does anyway, before deciding to move 
01830     // the scrollbars. 
01831     nscoord curPosX=0, curPosY=0;
01832     nsIScrollableView* s = GetScrollableView();
01833     if (s) {
01834       s->GetScrollPosition(curPosX, curPosY);
01835       if ((x*mOnePixel) == curPosX && (y*mOnePixel) == curPosY)
01836         return;
01837 
01838       PRBool isSmooth = aContent->HasAttr(kNameSpaceID_None, nsXULAtoms::smooth);
01839         
01840       if (isSmooth) {
01841         // Make sure an attribute-setting callback occurs even if the view didn't actually move yet
01842         // We need to make sure other listeners see that the scroll position is not (yet)
01843         // what they thought it was.
01844         s->GetScrollPosition(curPosX, curPosY);
01845 
01846         NS_ASSERTION(!mFrameInitiatedScroll, "Unexpected reentry");
01847         // Make sure we don't do anything in when the view calls us back for this
01848         // scroll operation.
01849         mFrameInitiatedScroll = PR_TRUE;
01850         InternalScrollPositionDidChange(curPosX, curPosY);
01851         mFrameInitiatedScroll = PR_FALSE;
01852       }
01853       ScrollbarChanged(mOuter->GetPresContext(), x*mOnePixel, y*mOnePixel, isSmooth ? NS_VMREFRESH_SMOOTHSCROLL : 0);
01854     }
01855   }
01856 }
01857 
01858 /* ============= Scroll events ========== */
01859 PR_STATIC_CALLBACK(void*) HandleScrollEvent(PLEvent* aEvent)
01860 {
01861   NS_ASSERTION(nsnull != aEvent,"Event is null");
01862   nsGfxScrollFrameInner* inner = NS_STATIC_CAST(nsGfxScrollFrameInner*, aEvent->owner);
01863   inner->FireScrollEvent();
01864   return nsnull;
01865 }
01866 
01867 PR_STATIC_CALLBACK(void) DestroyScrollEvent(PLEvent* aEvent)
01868 {
01869   NS_ASSERTION(nsnull != aEvent,"Event is null");
01870   delete aEvent;
01871 }
01872 
01873 void
01874 nsGfxScrollFrameInner::FireScrollEvent()
01875 {
01876   mScrollEventQueue = nsnull;
01877 
01878   nsScrollbarEvent event(PR_TRUE, NS_SCROLL_EVENT, nsnull);
01879   nsEventStatus status = nsEventStatus_eIgnore;
01880   nsIContent* content = mOuter->GetContent();
01881   nsPresContext* prescontext = mOuter->GetPresContext();
01882   // Fire viewport scroll events at the document (where they
01883   // will bubble to the window)
01884   if (mIsRoot) {
01885     nsIDocument* doc = content->GetCurrentDoc();
01886     if (doc) {
01887       doc->HandleDOMEvent(prescontext, &event, nsnull,
01888                           NS_EVENT_FLAG_INIT, &status);
01889     }
01890   } else {
01891     nsCOMPtr<nsIPresShell> shell = prescontext->PresShell();
01892     shell->HandleEventWithTarget(&event, mOuter, content, NS_EVENT_FLAG_INIT,
01893                                  &status);
01894   }
01895 }
01896 
01897 void
01898 nsGfxScrollFrameInner::PostScrollEvent()
01899 {
01900   nsCOMPtr<nsIEventQueueService> service = do_GetService(kEventQueueServiceCID);
01901   NS_ASSERTION(service, "No event service");
01902   nsCOMPtr<nsIEventQueue> eventQueue;
01903   service->GetSpecialEventQueue(
01904     nsIEventQueueService::UI_THREAD_EVENT_QUEUE, getter_AddRefs(eventQueue));
01905   NS_ASSERTION(eventQueue, "Event queue is null");
01906 
01907   if (eventQueue == mScrollEventQueue)
01908     return;
01909     
01910   PLEvent* ev = new PLEvent;
01911   if (!ev)
01912     return;
01913   PL_InitEvent(ev, this, ::HandleScrollEvent, ::DestroyScrollEvent);  
01914 
01915   if (mScrollEventQueue) {
01916     mScrollEventQueue->RevokeEvents(this);
01917   }
01918   eventQueue->PostEvent(ev);
01919   mScrollEventQueue = eventQueue;
01920 }
01921 
01922 void
01923 nsGfxScrollFrameInner::AdjustHorizontalScrollbar()
01924 {
01925 #ifdef IBMBIDI
01926   const nsStyleVisibility* vis = mOuter->GetStyleVisibility();
01927 
01928   // Scroll the view horizontally if:
01929   // 1)  We are creating the scrollbar for the first time and the
01930   //     horizontal scroll position of the view is 0 or
01931   // 2)  The display direction is changed
01932   PRBool needScroll;
01933   if (mLastDir == -1) {
01934     // Creating the scrollbar the first time
01935     nscoord curPosX = 0, curPosY = 0;
01936     nsIScrollableView* s = GetScrollableView();
01937     if (s) {
01938       s->GetScrollPosition(curPosX, curPosY);
01939     }
01940     needScroll = (curPosX == 0);
01941   } else {
01942     needScroll = (mLastDir != vis->mDirection);
01943   }
01944   if (needScroll) {
01945     SetAttribute(mHScrollbarBox, nsXULAtoms::curpos,
01946                  (NS_STYLE_DIRECTION_LTR == vis->mDirection) ? 0 : 0x7FFFFFFF);
01947   }
01948   mLastDir = vis->mDirection;
01949 #endif // IBMBIDI
01950 }
01951 
01952 PRBool
01953 nsXULScrollFrame::AddHorizontalScrollbar(nsBoxLayoutState& aState,
01954                                          nsRect& aScrollAreaSize, PRBool aOnTop)
01955 {
01956   if (!mInner.mHScrollbarBox)
01957     return PR_TRUE;
01958 
01959   mInner.AdjustHorizontalScrollbar();
01960   return AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_TRUE);
01961 }
01962 
01963 PRBool
01964 nsXULScrollFrame::AddVerticalScrollbar(nsBoxLayoutState& aState,
01965                                        nsRect& aScrollAreaSize, PRBool aOnRight)
01966 {
01967   if (!mInner.mVScrollbarBox)
01968     return PR_TRUE;
01969 
01970   return AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_TRUE);
01971 }
01972 
01973 void
01974 nsXULScrollFrame::RemoveHorizontalScrollbar(nsBoxLayoutState& aState,
01975                                             nsRect& aScrollAreaSize, PRBool aOnTop)
01976 {
01977   // removing a scrollbar should always fit
01978 #ifdef DEBUG
01979   PRBool result =
01980 #endif
01981   AddRemoveScrollbar(aState, aScrollAreaSize, aOnTop, PR_TRUE, PR_FALSE);
01982   NS_ASSERTION(result, "Removing horizontal scrollbar failed to fit??");
01983 }
01984 
01985 void
01986 nsXULScrollFrame::RemoveVerticalScrollbar(nsBoxLayoutState& aState,
01987                                           nsRect& aScrollAreaSize, PRBool aOnRight)
01988 {
01989   // removing a scrollbar should always fit
01990 #ifdef DEBUG
01991   PRBool result =
01992 #endif
01993   AddRemoveScrollbar(aState, aScrollAreaSize, aOnRight, PR_FALSE, PR_FALSE);
01994   NS_ASSERTION(result, "Removing vertical scrollbar failed to fit??");
01995 }
01996 
01997 PRBool
01998 nsXULScrollFrame::AddRemoveScrollbar(nsBoxLayoutState& aState, nsRect& aScrollAreaSize,
01999                                      PRBool aOnTop, PRBool aHorizontal, PRBool aAdd)
02000 {
02001   if (aHorizontal) {
02002      if (mInner.mNeverHasHorizontalScrollbar || !mInner.mHScrollbarBox)
02003        return PR_FALSE;
02004 
02005      nsSize hSize;
02006      mInner.mHScrollbarBox->GetPrefSize(aState, hSize);
02007      nsBox::AddMargin(mInner.mHScrollbarBox, hSize);
02008 
02009      mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, aAdd);
02010 
02011      PRBool hasHorizontalScrollbar;
02012      PRBool fit = AddRemoveScrollbar(hasHorizontalScrollbar, aScrollAreaSize.y, aScrollAreaSize.height, hSize.height, aOnTop, aAdd);
02013      mInner.mHasHorizontalScrollbar = hasHorizontalScrollbar;    // because mHasHorizontalScrollbar is a PRPackedBool
02014      if (!fit)
02015         mInner.SetScrollbarVisibility(mInner.mHScrollbarBox, !aAdd);
02016 
02017      return fit;
02018   } else {
02019      if (mInner.mNeverHasVerticalScrollbar || !mInner.mVScrollbarBox)
02020        return PR_FALSE;
02021 
02022      nsSize vSize;
02023      mInner.mVScrollbarBox->GetPrefSize(aState, vSize);
02024      nsBox::AddMargin(mInner.mVScrollbarBox, vSize);
02025 
02026      mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, aAdd);
02027 
02028      PRBool hasVerticalScrollbar;
02029      PRBool fit = AddRemoveScrollbar(hasVerticalScrollbar, aScrollAreaSize.x, aScrollAreaSize.width, vSize.width, aOnTop, aAdd);
02030      mInner.mHasVerticalScrollbar = hasVerticalScrollbar;    // because mHasVerticalScrollbar is a PRPackedBool
02031      if (!fit)
02032         mInner.SetScrollbarVisibility(mInner.mVScrollbarBox, !aAdd);
02033 
02034      return fit;
02035   }
02036 }
02037 
02038 PRBool
02039 nsXULScrollFrame::AddRemoveScrollbar(PRBool& aHasScrollbar, nscoord& aXY,
02040                                      nscoord& aSize, nscoord aSbSize,
02041                                      PRBool aRightOrBottom, PRBool aAdd)
02042 { 
02043    nscoord size = aSize;
02044    nscoord xy = aXY;
02045 
02046    if (size != NS_INTRINSICSIZE) {
02047      if (aAdd) {
02048         size -= aSbSize;
02049         if (!aRightOrBottom && size >= 0)
02050           xy += aSbSize;
02051      } else {
02052         size += aSbSize;
02053         if (!aRightOrBottom)
02054           xy -= aSbSize;
02055      }
02056    }
02057 
02058    // not enough room? Yes? Return true.
02059    if (size >= 0) {
02060        aHasScrollbar = aAdd;
02061        aSize = size;
02062        aXY = xy;
02063        return PR_TRUE;
02064    }
02065 
02066    aHasScrollbar = PR_FALSE;
02067    return PR_FALSE;
02068 }
02069 
02091 void
02092 nsXULScrollFrame::AdjustReflowStateForPrintPreview(nsBoxLayoutState& aState, PRBool& aSetBack)
02093 {
02094   aSetBack = PR_FALSE;
02095   PRBool isChrome;
02096   PRBool isInitialPP = nsBoxFrame::IsInitialReflowForPrintPreview(aState, isChrome);
02097   if (isInitialPP && !isChrome) {
02098     // I know you shouldn't, but we cast away the "const" here
02099     nsHTMLReflowState* reflowState = (nsHTMLReflowState*)aState.GetReflowState();
02100     reflowState->reason = eReflowReason_Resize;
02101     aSetBack = PR_TRUE;
02102   }
02103 }
02104 
02108 void
02109 nsXULScrollFrame::AdjustReflowStateBack(nsBoxLayoutState& aState, PRBool aSetBack)
02110 {
02111   // I know you shouldn't, but we cast away the "const" here
02112   nsHTMLReflowState* reflowState = (nsHTMLReflowState*)aState.GetReflowState();
02113   if (aSetBack && reflowState->reason == eReflowReason_Resize) {
02114     reflowState->reason = eReflowReason_Initial;
02115   }
02116 }
02117 
02118 void
02119 nsXULScrollFrame::LayoutScrollArea(nsBoxLayoutState& aState, const nsRect& aRect)
02120 {
02121   nsIView* scrollView = mInner.mScrollableView->View();
02122   nsIViewManager* vm = scrollView->GetViewManager();
02123   vm->MoveViewTo(scrollView, aRect.x, aRect.y);
02124   vm->ResizeView(scrollView, nsRect(nsPoint(0, 0), aRect.Size()), PR_TRUE);
02125 
02126   PRUint32 oldflags = aState.LayoutFlags();
02127   // set the origin of childRect to (0,0) even though we might have
02128   // borders or a left-hand-side scrollbar. We've accounted for that
02129   // by positioning the anonymous mScrollableView.
02130   nsRect childRect = nsRect(nsPoint(0, 0), aRect.Size());
02131 
02132   PRInt32 flags = NS_FRAME_NO_MOVE_VIEW;
02133 
02134   nsSize minSize(0,0);
02135   mInner.mScrolledFrame->GetMinSize(aState, minSize);
02136   
02137   if (minSize.height > childRect.height)
02138     childRect.height = minSize.height;
02139   
02140   if (minSize.width > childRect.width)
02141     childRect.width = minSize.width;
02142 
02143   aState.SetLayoutFlags(flags);
02144   mInner.mScrolledFrame->SetBounds(aState, childRect);
02145   mInner.mScrolledFrame->Layout(aState);
02146 
02147   childRect = mInner.mScrolledFrame->GetRect();
02148 
02149   if (childRect.width < aRect.width || childRect.height < aRect.height)
02150   {
02151     childRect.width = PR_MAX(childRect.width, aRect.width);
02152     childRect.height = PR_MAX(childRect.height, aRect.height);
02153 
02154     // remove overflow area when we update the bounds,
02155     // because we've already accounted for it
02156     mInner.mScrolledFrame->SetBounds(aState, childRect, PR_TRUE);
02157   }
02158 
02159   aState.SetLayoutFlags(oldflags);
02160 
02161   // XXX hack! force the scrolled frame to think it has overflow
02162   // to avoid problems with incorrect event targeting.
02163   mInner.mScrolledFrame->AddStateBits(NS_FRAME_OUTSIDE_CHILDREN);
02164   
02165   mInner.PostOverflowEvents();
02166 }
02167 
02168 void nsGfxScrollFrameInner::PostOverflowEvents()
02169 {
02170   nsSize childSize = mScrolledFrame->GetSize();
02171   nsSize scrollportSize = mScrollableView->View()->GetBounds().Size();
02172     
02173   PRBool newVerticalOverflow = childSize.height > scrollportSize.height;
02174   PRBool vertChanged = mVerticalOverflow != newVerticalOverflow;
02175   mVerticalOverflow = newVerticalOverflow;
02176 
02177   PRBool newHorizontalOverflow = childSize.width > scrollportSize.width;
02178   PRBool horizChanged = mHorizontalOverflow != newHorizontalOverflow;
02179   mHorizontalOverflow = newHorizontalOverflow;
02180 
02181   if (vertChanged) {
02182     if (horizChanged) {
02183       if (mVerticalOverflow == mHorizontalOverflow) {
02184         // both either overflowed or underflowed. 1 event
02185         PostScrollPortEvent(mVerticalOverflow, nsScrollPortEvent::both);
02186       } else {
02187         // one overflowed and one underflowed
02188         PostScrollPortEvent(mVerticalOverflow, nsScrollPortEvent::vertical);
02189         PostScrollPortEvent(mHorizontalOverflow, nsScrollPortEvent::horizontal);
02190       }
02191     } else {
02192       PostScrollPortEvent(mVerticalOverflow, nsScrollPortEvent::vertical);
02193     }
02194   } else {
02195     if (horizChanged) {
02196       PostScrollPortEvent(mHorizontalOverflow, nsScrollPortEvent::horizontal);
02197     }
02198   }
02199 }
02200 
02201 PRBool
02202 nsGfxScrollFrameInner::IsScrollbarOnRight()
02203 {
02204 #ifdef IBMBIDI
02205   //TODO make bidi code set these from preferences
02206   const nsStyleVisibility* vis = mOuter->GetStyleVisibility();
02207   return vis->mDirection != NS_STYLE_DIRECTION_RTL;
02208 #else
02209   return PR_TRUE;
02210 #endif // IBMBIDI
02211 }
02212 
02217 nsresult
02218 nsXULScrollFrame::Layout(nsBoxLayoutState& aState)
02219 {
02220   PRBool scrollbarRight = mInner.IsScrollbarOnRight();
02221   PRBool scrollbarBottom = PR_TRUE;
02222 
02223   // get the content rect
02224   nsRect clientRect(0,0,0,0);
02225   GetClientRect(clientRect);
02226 
02227   // the scroll area size starts off as big as our content area
02228   nsRect scrollAreaRect(clientRect);
02229 
02230   /**************
02231    Our basic strategy here is to first try laying out the content with
02232    the scrollbars in their current state. We're hoping that that will
02233    just "work"; the content will overflow wherever there's a scrollbar
02234    already visible. If that does work, then there's no need to lay out
02235    the scrollarea. Otherwise we fix up the scrollbars; first we add a
02236    vertical one to scroll the content if necessary, or remove it if
02237    it's not needed. Then we reflow the content if the scrollbar
02238    changed.  Then we add a horizontal scrollbar if necessary (or
02239    remove if not needed), and if that changed, we reflow the content
02240    again. At this point, any scrollbars that are needed to scroll the
02241    content have been added.
02242 
02243    In the second phase we check to see if any scrollbars are too small
02244    to display, and if so, we remove them. We check the horizontal
02245    scrollbar first; removing it might make room for the vertical
02246    scrollbar, and if we have room for just one scrollbar we'll save
02247    the vertical one.
02248 
02249    Finally we position and size the scrollbars and scrollcorner (the
02250    square that is needed in the corner of the window when two
02251    scrollbars are visible), and reflow any fixed position views
02252    (if we're the viewport and we added or removed a scrollbar).
02253    **************/
02254 
02255   ScrollbarStyles styles = GetScrollbarStyles();
02256 
02257   // Look at our style do we always have vertical or horizontal scrollbars?
02258   if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL)
02259      mInner.mHasHorizontalScrollbar = PR_TRUE;
02260   if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL)
02261      mInner.mHasVerticalScrollbar = PR_TRUE;
02262 
02263   if (mInner.mHasHorizontalScrollbar)
02264      AddHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
02265 
02266   if (mInner.mHasVerticalScrollbar)
02267      AddVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
02268      
02269   nsRect oldScrollAreaBounds = mInner.mScrollableView->View()->GetBounds();
02270 
02271   // layout our the scroll area
02272   LayoutScrollArea(aState, scrollAreaRect);
02273   
02274   // now look at the content area and see if we need scrollbars or not
02275   PRBool needsLayout = PR_FALSE;
02276   nsSize scrolledContentSize(0,0);
02277 
02278   // if we have 'auto' scrollbars look at the vertical case
02279   if (styles.mVertical != NS_STYLE_OVERFLOW_SCROLL) {
02280       // get the area frame is the scrollarea
02281       scrolledContentSize = mInner.GetScrolledSize();
02282 
02283     // There are two cases to consider
02284       if (scrolledContentSize.height <= scrollAreaRect.height
02285           || styles.mVertical != NS_STYLE_OVERFLOW_AUTO) {
02286         if (mInner.mHasVerticalScrollbar) {
02287           // We left room for the vertical scrollbar, but it's not needed;
02288           // remove it.
02289           RemoveVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
02290           needsLayout = PR_TRUE;
02291         }
02292       } else {
02293         if (!mInner.mHasVerticalScrollbar) {
02294           // We didn't leave room for the vertical scrollbar, but it turns
02295           // out we needed it
02296           if (AddVerticalScrollbar(aState, scrollAreaRect, scrollbarRight))
02297             needsLayout = PR_TRUE;
02298         }
02299     }
02300 
02301     // ok layout at the right size
02302     if (needsLayout) {
02303        nsBoxLayoutState resizeState(aState);
02304        resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
02305        PRBool setBack;
02306        AdjustReflowStateForPrintPreview(aState, setBack);
02307        LayoutScrollArea(resizeState, scrollAreaRect);
02308        AdjustReflowStateBack(aState, setBack);
02309        needsLayout = PR_FALSE;
02310     }
02311   }
02312 
02313 
02314   // if scrollbars are auto look at the horizontal case
02315   if (styles.mHorizontal != NS_STYLE_OVERFLOW_SCROLL)
02316   {
02317     // get the area frame is the scrollarea
02318     scrolledContentSize = mInner.GetScrolledSize();
02319 
02320     // if the child is wider that the scroll area
02321     // and we don't have a scrollbar add one.
02322     if (scrolledContentSize.width > scrollAreaRect.width
02323         && styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
02324 
02325       if (!mInner.mHasHorizontalScrollbar) {
02326            // no scrollbar? 
02327           if (AddHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom))
02328              needsLayout = PR_TRUE;
02329 
02330            // if we added a horizonal scrollbar and we did not have a vertical
02331            // there is a chance that by adding the horizonal scrollbar we will
02332            // suddenly need a vertical scrollbar. Is a special case but its 
02333            // important.
02334            //if (!mHasVerticalScrollbar && scrolledContentSize.height > scrollAreaRect.height - sbSize.height)
02335            //  printf("****Gfx Scrollbar Special case hit!!*****\n");
02336            
02337       }
02338     } else {
02339         // if the area is smaller or equal to and we have a scrollbar then
02340         // remove it.
02341       if (mInner.mHasHorizontalScrollbar) {
02342         RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
02343         needsLayout = PR_TRUE;
02344       }
02345     }
02346   }
02347 
02348   // we only need to set the rect. The inner child stays the same size.
02349   if (needsLayout) {
02350      nsBoxLayoutState resizeState(aState);
02351      resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
02352      PRBool setBack;
02353      AdjustReflowStateForPrintPreview(aState, setBack);
02354      LayoutScrollArea(resizeState, scrollAreaRect);
02355      AdjustReflowStateBack(aState, setBack);
02356      needsLayout = PR_FALSE;
02357   }
02358     
02359   // get the preferred size of the scrollbars
02360   nsSize hMinSize(0, 0);
02361   if (mInner.mHScrollbarBox && mInner.mHasHorizontalScrollbar) {
02362     GetScrollbarMetrics(aState, mInner.mHScrollbarBox, &hMinSize, nsnull, PR_FALSE);
02363   }
02364   nsSize vMinSize(0, 0);
02365   if (mInner.mVScrollbarBox && mInner.mHasVerticalScrollbar) {
02366     GetScrollbarMetrics(aState, mInner.mVScrollbarBox, &vMinSize, nsnull, PR_TRUE);
02367   }
02368 
02369   // Disable scrollbars that are too small
02370   // Disable horizontal scrollbar first. If we have to disable only one
02371   // scrollbar, we'd rather keep the vertical scrollbar.
02372   // Note that we always give horizontal scrollbars their preferred height,
02373   // never their min-height. So check that there's room for the preferred height.
02374   if (mInner.mHasHorizontalScrollbar &&
02375       (hMinSize.width > clientRect.width - vMinSize.width
02376        || hMinSize.height > clientRect.height)) {
02377     RemoveHorizontalScrollbar(aState, scrollAreaRect, scrollbarBottom);
02378     needsLayout = PR_TRUE;
02379   }
02380   // Now disable vertical scrollbar if necessary
02381   if (mInner.mHasVerticalScrollbar &&
02382       (vMinSize.height > clientRect.height - hMinSize.height
02383        || vMinSize.width > clientRect.width)) {
02384     RemoveVerticalScrollbar(aState, scrollAreaRect, scrollbarRight);
02385     needsLayout = PR_TRUE;
02386   }
02387 
02388   // we only need to set the rect. The inner child stays the same size.
02389   if (needsLayout) {
02390     nsBoxLayoutState resizeState(aState);
02391     resizeState.SetLayoutReason(nsBoxLayoutState::Resize);
02392     LayoutScrollArea(resizeState, scrollAreaRect);
02393   }
02394 
02395   if (!mInner.mSupppressScrollbarUpdate) { 
02396     mInner.LayoutScrollbars(aState, clientRect, oldScrollAreaBounds, scrollAreaRect);
02397   }
02398   mInner.ScrollToRestoredPosition();
02399   return NS_OK;
02400 }
02401 
02402 void
02403 nsGfxScrollFrameInner::LayoutScrollbars(nsBoxLayoutState& aState,
02404                                         const nsRect& aContentArea,
02405                                         const nsRect& aOldScrollArea,
02406                                         const nsRect& aScrollArea)
02407 {
02408   NS_ASSERTION(!mSupppressScrollbarUpdate,
02409                "This should have been suppressed");
02410     
02411   nsPresContext* presContext = aState.PresContext();
02412   mOnePixel = presContext->IntScaledPixelsToTwips(1);
02413   const nsStyleFont* font = mOuter->GetStyleFont();
02414   const nsFont& f = font->mFont;
02415   nsCOMPtr<nsIFontMetrics> fm = presContext->GetMetricsFor(f);
02416   nscoord fontHeight = 1;
02417   NS_ASSERTION(fm,"FontMetrics is null assuming fontHeight == 1");
02418   if (fm)
02419     fm->GetHeight(fontHeight);
02420 
02421   nsSize scrolledContentSize = GetScrolledSize();
02422 
02423   nscoord maxX = scrolledContentSize.width - aScrollArea.width;
02424   nscoord maxY = scrolledContentSize.height - aScrollArea.height;
02425 
02426   nsIScrollableView* scrollable = GetScrollableView();
02427   scrollable->SetLineHeight(fontHeight);
02428 
02429   if (mVScrollbarBox) {
02430     NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!");
02431     if (!mHasVerticalScrollbar) {
02432       SetAttribute(mVScrollbarBox, nsXULAtoms::curpos, 0);
02433     }
02434     SetScrollbarEnabled(mVScrollbarBox, maxY);
02435     SetAttribute(mVScrollbarBox, nsXULAtoms::maxpos, maxY);
02436     SetAttribute(mVScrollbarBox, nsXULAtoms::pageincrement, nscoord(aScrollArea.height - fontHeight));
02437     SetAttribute(mVScrollbarBox, nsXULAtoms::increment, fontHeight);
02438 
02439     nsRect vRect(aScrollArea);
02440     vRect.width = aContentArea.width - aScrollArea.width;
02441     vRect.x = IsScrollbarOnRight() ? aScrollArea.XMost() : aContentArea.x;
02442     nsMargin margin;
02443     mVScrollbarBox->GetMargin(margin);
02444     vRect.Deflate(margin);
02445     nsBoxFrame::LayoutChildAt(aState, mVScrollbarBox, vRect);
02446   }
02447     
02448   if (mHScrollbarBox) {
02449     NS_PRECONDITION(mHScrollbarBox->IsBoxFrame(), "Must be a box frame!");
02450     if (!mHasHorizontalScrollbar) {
02451       SetAttribute(mHScrollbarBox, nsXULAtoms::curpos, 0);
02452     }
02453     SetScrollbarEnabled(mHScrollbarBox, maxX);
02454     SetAttribute(mHScrollbarBox, nsXULAtoms::maxpos, maxX);
02455     SetAttribute(mHScrollbarBox, nsXULAtoms::pageincrement, nscoord(float(aScrollArea.width)*0.8));
02456     SetAttribute(mHScrollbarBox, nsXULAtoms::increment, 10*mOnePixel);
02457 
02458     nsRect hRect(aScrollArea);
02459     hRect.height = aContentArea.height - aScrollArea.height;
02460     hRect.y = PR_TRUE ? aScrollArea.YMost() : aContentArea.y;
02461     nsMargin margin;
02462     mHScrollbarBox->GetMargin(margin);
02463     hRect.Deflate(margin);
02464     nsBoxFrame::LayoutChildAt(aState, mHScrollbarBox, hRect);
02465   }
02466 
02467   // place the scrollcorner
02468   if (mScrollCornerBox) {
02469     NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
02470     nsRect r(0, 0, 0, 0);
02471     if (aContentArea.x != aScrollArea.x) {
02472       // scrollbar (if any) on left
02473       r.x = aContentArea.x;
02474       r.width = aScrollArea.x - aContentArea.x;
02475       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
02476     } else {
02477       // scrollbar (if any) on right
02478       r.x = aScrollArea.XMost();
02479       r.width = aContentArea.XMost() - aScrollArea.XMost();
02480       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
02481     }
02482     if (aContentArea.y != aScrollArea.y) {
02483       // scrollbar (if any) on top
02484       r.y = aContentArea.y;
02485       r.height = aScrollArea.y - aContentArea.y;
02486       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
02487     } else {
02488       // scrollbar (if any) on bottom
02489       r.y = aScrollArea.YMost();
02490       r.height = aContentArea.YMost() - aScrollArea.YMost();
02491       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
02492     }
02493     nsBoxFrame::LayoutChildAt(aState, mScrollCornerBox, r); 
02494   }
02495 
02496   // may need to update fixed position children of the viewport,
02497   // if the client area changed size because of some dirty reflow
02498   // (if the reflow is initial or resize, the fixed children will
02499   // be re-laid out anyway)
02500   if (aOldScrollArea.Size() != aScrollArea.Size()
02501       && nsBoxLayoutState::Dirty == aState.LayoutReason() &&
02502       mIsRoot) {
02503     // Usually there are no fixed children, so don't do anything unless there's
02504     // at least one fixed child
02505     nsIFrame* parentFrame = mOuter->GetParent();
02506     if (parentFrame->GetFirstChild(nsLayoutAtoms::fixedList)) {
02507       // force a reflow of the fixed children
02508       mOuter->GetPresContext()->PresShell()->
02509         AppendReflowCommand(parentFrame, eReflowType_UserDefined,
02510                             nsLayoutAtoms::fixedList);
02511     }
02512   }
02513 }
02514 
02515 void
02516 nsGfxScrollFrameInner::ScrollbarChanged(nsPresContext* aPresContext, nscoord aX, nscoord aY, PRUint32 aFlags)
02517 {
02518   nsIScrollableView* scrollable = GetScrollableView();
02519   scrollable->ScrollTo(aX, aY, aFlags);
02520  // printf("scrolling to: %d, %d\n", aX, aY);
02521 }
02522 
02523 void
02524 nsGfxScrollFrameInner::SetScrollbarEnabled(nsIBox* aBox, nscoord aMaxPos, PRBool aReflow)
02525 {
02526   mOuter->GetPresContext()->PresShell()->PostAttributeChange(
02527     aBox->GetContent(),
02528     kNameSpaceID_None,
02529     nsHTMLAtoms::disabled,
02530     NS_LITERAL_STRING("true"),
02531     aReflow,
02532     aMaxPos ? eChangeType_Remove : eChangeType_Set);
02533 }
02534 
02538 PRBool
02539 nsGfxScrollFrameInner::SetAttribute(nsIBox* aBox, nsIAtom* aAtom, nscoord aSize, PRBool aReflow)
02540 {
02541   // convert to pixels
02542   aSize /= mOnePixel;
02543 
02544   // only set the attribute if it changed.
02545 
02546   PRInt32 current = GetIntegerAttribute(aBox, aAtom, -1);
02547   if (current != aSize)
02548   {
02549       nsAutoString newValue;
02550       newValue.AppendInt(aSize);
02551       aBox->GetContent()->SetAttr(kNameSpaceID_None, aAtom, newValue, aReflow);
02552       return PR_TRUE;
02553   }
02554 
02555   return PR_FALSE;
02556 }
02557 
02558 nsSize
02559 nsGfxScrollFrameInner::GetScrolledSize() const
02560 {
02561   nsRect r = mScrolledFrame->GetOverflowRect();
02562   return nsSize(r.XMost(), r.YMost());
02563 }
02564 
02565 nsMargin
02566 nsGfxScrollFrameInner::GetActualScrollbarSizes() const {
02567   nsMargin border;
02568   mOuter->GetBorder(border);
02569   nsRect r(nsPoint(0,0), mOuter->GetSize());
02570   r.Deflate(border);
02571   nsRect scrollArea = mScrollableView->View()->GetBounds();
02572 
02573   return nsMargin(scrollArea.x - r.x, scrollArea.y - r.y,
02574                   r.XMost() - scrollArea.XMost(),
02575                   r.YMost() - scrollArea.YMost());
02576 }
02577 
02578 void
02579 nsGfxScrollFrameInner::SetScrollbarVisibility(nsIBox* aScrollbar, PRBool aVisible)
02580 {
02581   if (!aScrollbar)
02582     return;
02583 
02584   nsCOMPtr<nsIScrollbarFrame> scrollbar(do_QueryInterface(aScrollbar));
02585   if (scrollbar) {
02586     // See if we have a mediator.
02587     nsCOMPtr<nsIScrollbarMediator> mediator;
02588     scrollbar->GetScrollbarMediator(getter_AddRefs(mediator));
02589     if (mediator) {
02590       // Inform the mediator of the visibility change.
02591       mediator->VisibilityChanged(scrollbar, aVisible);
02592     }
02593   }
02594 }
02595 
02596 PRInt32
02597 nsGfxScrollFrameInner::GetIntegerAttribute(nsIBox* aBox, nsIAtom* atom, PRInt32 defaultValue)
02598 {
02599     nsIContent* content = aBox->GetContent();
02600 
02601     nsAutoString value;
02602     if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None, atom, value))
02603     {
02604       PRInt32 error;
02605 
02606       // convert it to an integer
02607       defaultValue = value.ToInteger(&error);
02608     }
02609 
02610     return defaultValue;
02611 }
02612 
02613 nsPresState*
02614 nsGfxScrollFrameInner::SaveState()
02615 {
02616   nsCOMPtr<nsIScrollbarMediator> mediator;
02617   nsIFrame* first = GetScrolledFrame();
02618   mediator = do_QueryInterface(first);
02619   if (mediator) {
02620     // Child manages its own scrolling. Bail.
02621     return nsnull;
02622   }
02623 
02624   nsIScrollableView* scrollingView = GetScrollableView();
02625   PRInt32 x,y;
02626   scrollingView->GetScrollPosition(x,y);
02627   // Don't save scroll position if we are at (0,0)
02628   if (!x && !y) {
02629     return nsnull;
02630   }
02631 
02632   nsIView* child = nsnull;
02633   scrollingView->GetScrolledView(child);
02634   if (!child) {
02635     return nsnull;
02636   }
02637 
02638   nsRect childRect = child->GetBounds();
02639   nsAutoPtr<nsPresState> state;
02640   nsresult rv = NS_NewPresState(getter_Transfers(state));
02641   NS_ENSURE_SUCCESS(rv, nsnull);
02642 
02643   nsCOMPtr<nsISupportsPRInt32> xoffset = do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
02644   if (xoffset) {
02645     rv = xoffset->SetData(x);
02646     NS_ENSURE_SUCCESS(rv, nsnull);
02647     state->SetStatePropertyAsSupports(NS_LITERAL_STRING("x-offset"), xoffset);
02648   }
02649 
02650   nsCOMPtr<nsISupportsPRInt32> yoffset = do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
02651   if (yoffset) {
02652     rv = yoffset->SetData(y);
02653     NS_ENSURE_SUCCESS(rv, nsnull);
02654     state->SetStatePropertyAsSupports(NS_LITERAL_STRING("y-offset"), yoffset);
02655   }
02656 
02657   nsCOMPtr<nsISupportsPRInt32> width = do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
02658   if (width) {
02659     rv = width->SetData(childRect.width);
02660     NS_ENSURE_SUCCESS(rv, nsnull);
02661     state->SetStatePropertyAsSupports(NS_LITERAL_STRING("width"), width);
02662   }
02663 
02664   nsCOMPtr<nsISupportsPRInt32> height = do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID);
02665   if (height) {
02666     rv = height->SetData(childRect.height);
02667     NS_ENSURE_SUCCESS(rv, nsnull);
02668     state->SetStatePropertyAsSupports(NS_LITERAL_STRING("height"), height);
02669   }
02670   return state.forget();
02671 }
02672 
02673 void
02674 nsGfxScrollFrameInner::RestoreState(nsPresState* aState)
02675 {
02676   nsCOMPtr<nsISupportsPRInt32> xoffset;
02677   nsCOMPtr<nsISupportsPRInt32> yoffset;
02678   nsCOMPtr<nsISupportsPRInt32> width;
02679   nsCOMPtr<nsISupportsPRInt32> height;
02680   aState->GetStatePropertyAsSupports(NS_LITERAL_STRING("x-offset"), getter_AddRefs(xoffset));
02681   aState->GetStatePropertyAsSupports(NS_LITERAL_STRING("y-offset"), getter_AddRefs(yoffset));
02682   aState->GetStatePropertyAsSupports(NS_LITERAL_STRING("width"), getter_AddRefs(width));
02683   aState->GetStatePropertyAsSupports(NS_LITERAL_STRING("height"), getter_AddRefs(height));
02684 
02685   if (xoffset && yoffset) {
02686     PRInt32 x,y,w,h;
02687     nsresult rv = xoffset->GetData(&x);
02688     if (NS_SUCCEEDED(rv))
02689       rv = yoffset->GetData(&y);
02690     if (NS_SUCCEEDED(rv))
02691       rv = width->GetData(&w);
02692     if (NS_SUCCEEDED(rv))
02693       rv = height->GetData(&h);
02694 
02695     mLastPos.x = -1;
02696     mLastPos.y = -1;
02697     mRestoreRect.SetRect(-1, -1, -1, -1);
02698 
02699     // don't do it now, store it later and do it in layout.
02700     if (NS_SUCCEEDED(rv)) {
02701       mRestoreRect.SetRect(x, y, w, h);
02702       mDidHistoryRestore = PR_TRUE;
02703       nsIScrollableView* scrollingView = GetScrollableView();
02704       if (scrollingView) {
02705         scrollingView->GetScrollPosition(mLastPos.x, mLastPos.y);
02706       } else {
02707         mLastPos = nsPoint(0, 0);
02708       }
02709     }
02710   }
02711 }