LCOV - code coverage report
Current view: directory - layout/xul/base/src - nsListBoxBodyFrame.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 704 0 0.0 %
Date: 2012-06-02 Functions: 68 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   David W. Hyatt (hyatt@netscape.com) (Original Author)
      24                 :  *   Joe Hewitt (hewitt@netscape.com)
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsListBoxBodyFrame.h"
      41                 : 
      42                 : #include "nsListBoxLayout.h"
      43                 : 
      44                 : #include "nsCOMPtr.h"
      45                 : #include "nsGridRowGroupLayout.h"
      46                 : #include "nsIServiceManager.h"
      47                 : #include "nsGkAtoms.h"
      48                 : #include "nsIContent.h"
      49                 : #include "nsINameSpaceManager.h"
      50                 : #include "nsIDocument.h"
      51                 : #include "nsIDOMEventTarget.h"
      52                 : #include "nsIDOMMouseEvent.h"
      53                 : #include "nsIDOMElement.h"
      54                 : #include "nsIDOMNodeList.h"
      55                 : #include "nsCSSFrameConstructor.h"
      56                 : #include "nsIScrollableFrame.h"
      57                 : #include "nsScrollbarFrame.h"
      58                 : #include "nsIView.h"
      59                 : #include "nsIViewManager.h"
      60                 : #include "nsStyleContext.h"
      61                 : #include "nsFontMetrics.h"
      62                 : #include "nsITimer.h"
      63                 : #include "nsAutoPtr.h"
      64                 : #include "nsStyleSet.h"
      65                 : #include "nsPIBoxObject.h"
      66                 : #include "nsINodeInfo.h"
      67                 : #include "nsLayoutUtils.h"
      68                 : #include "nsPIListBoxObject.h"
      69                 : #include "nsContentUtils.h"
      70                 : #include "nsChildIterator.h"
      71                 : #include "nsRenderingContext.h"
      72                 : 
      73                 : #ifdef ACCESSIBILITY
      74                 : #include "nsAccessibilityService.h"
      75                 : #endif
      76                 : 
      77                 : /////////////// nsListScrollSmoother //////////////////
      78                 : 
      79                 : /* A mediator used to smooth out scrolling. It works by seeing if 
      80                 :  * we have time to scroll the amount of rows requested. This is determined
      81                 :  * by measuring how long it takes to scroll a row. If we can scroll the 
      82                 :  * rows in time we do so. If not we start a timer and skip the request. We
      83                 :  * do this until the timer finally first because the user has stopped moving
      84                 :  * the mouse. Then do all the queued requests in on shot.
      85                 :  */
      86                 : 
      87                 : // the longest amount of time that can go by before the use
      88                 : // notices it as a delay.
      89                 : #define USER_TIME_THRESHOLD 150000
      90                 : 
      91                 : // how long it takes to layout a single row initial value.
      92                 : // we will time this after we scroll a few rows.
      93                 : #define TIME_PER_ROW_INITAL  50000
      94                 : 
      95                 : // if we decide we can't layout the rows in the amount of time. How long
      96                 : // do we wait before checking again?
      97                 : #define SMOOTH_INTERVAL 100
      98                 : 
      99                 : class nsListScrollSmoother : public nsITimerCallback
     100                 : {
     101                 : public:
     102                 :   NS_DECL_ISUPPORTS
     103                 : 
     104                 :   nsListScrollSmoother(nsListBoxBodyFrame* aOuter);
     105                 :   virtual ~nsListScrollSmoother();
     106                 : 
     107                 :   // nsITimerCallback
     108                 :   NS_DECL_NSITIMERCALLBACK
     109                 : 
     110                 :   void Start();
     111                 :   void Stop();
     112                 :   bool IsRunning();
     113                 : 
     114                 :   nsCOMPtr<nsITimer> mRepeatTimer;
     115                 :   PRInt32 mDelta;
     116                 :   nsListBoxBodyFrame* mOuter;
     117                 : }; 
     118                 : 
     119               0 : nsListScrollSmoother::nsListScrollSmoother(nsListBoxBodyFrame* aOuter)
     120                 : {
     121               0 :   mDelta = 0;
     122               0 :   mOuter = aOuter;
     123               0 : }
     124                 : 
     125               0 : nsListScrollSmoother::~nsListScrollSmoother()
     126                 : {
     127               0 :   Stop();
     128               0 : }
     129                 : 
     130                 : NS_IMETHODIMP
     131               0 : nsListScrollSmoother::Notify(nsITimer *timer)
     132                 : {
     133               0 :   Stop();
     134                 : 
     135               0 :   NS_ASSERTION(mOuter, "mOuter is null, see bug #68365");
     136               0 :   if (!mOuter) return NS_OK;
     137                 : 
     138                 :   // actually do some work.
     139               0 :   mOuter->InternalPositionChangedCallback();
     140               0 :   return NS_OK;
     141                 : }
     142                 : 
     143                 : bool
     144               0 : nsListScrollSmoother::IsRunning()
     145                 : {
     146               0 :   return mRepeatTimer ? true : false;
     147                 : }
     148                 : 
     149                 : void
     150               0 : nsListScrollSmoother::Start()
     151                 : {
     152               0 :   Stop();
     153               0 :   mRepeatTimer = do_CreateInstance("@mozilla.org/timer;1");
     154               0 :   mRepeatTimer->InitWithCallback(this, SMOOTH_INTERVAL, nsITimer::TYPE_ONE_SHOT);
     155               0 : }
     156                 : 
     157                 : void
     158               0 : nsListScrollSmoother::Stop()
     159                 : {
     160               0 :   if ( mRepeatTimer ) {
     161               0 :     mRepeatTimer->Cancel();
     162               0 :     mRepeatTimer = nsnull;
     163                 :   }
     164               0 : }
     165                 : 
     166               0 : NS_IMPL_ISUPPORTS1(nsListScrollSmoother, nsITimerCallback)
     167                 : 
     168                 : /////////////// nsListBoxBodyFrame //////////////////
     169                 : 
     170               0 : nsListBoxBodyFrame::nsListBoxBodyFrame(nsIPresShell* aPresShell,
     171                 :                                        nsStyleContext* aContext,
     172                 :                                        nsBoxLayout* aLayoutManager)
     173                 :   : nsBoxFrame(aPresShell, aContext, false, aLayoutManager),
     174                 :     mTopFrame(nsnull),
     175                 :     mBottomFrame(nsnull),
     176                 :     mLinkupFrame(nsnull),
     177                 :     mScrollSmoother(nsnull),
     178                 :     mRowsToPrepend(0),
     179                 :     mRowCount(-1),
     180                 :     mRowHeight(0),
     181                 :     mAvailableHeight(0),
     182                 :     mStringWidth(-1),
     183                 :     mCurrentIndex(0),
     184                 :     mOldIndex(0),
     185                 :     mYPosition(0),
     186                 :     mTimePerRow(TIME_PER_ROW_INITAL),
     187                 :     mRowHeightWasSet(false),
     188                 :     mScrolling(false),
     189                 :     mAdjustScroll(false),
     190               0 :     mReflowCallbackPosted(false)
     191                 : {
     192               0 : }
     193                 : 
     194               0 : nsListBoxBodyFrame::~nsListBoxBodyFrame()
     195                 : {
     196               0 :   NS_IF_RELEASE(mScrollSmoother);
     197                 : 
     198                 : #if USE_TIMER_TO_DELAY_SCROLLING
     199                 :   StopScrollTracking();
     200                 :   mAutoScrollTimer = nsnull;
     201                 : #endif
     202                 : 
     203               0 : }
     204                 : 
     205               0 : NS_QUERYFRAME_HEAD(nsListBoxBodyFrame)
     206               0 :   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
     207               0 :   NS_QUERYFRAME_ENTRY(nsListBoxBodyFrame)
     208               0 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
     209                 : 
     210                 : ////////// nsIFrame /////////////////
     211                 : 
     212                 : NS_IMETHODIMP
     213               0 : nsListBoxBodyFrame::Init(nsIContent*     aContent,
     214                 :                          nsIFrame*       aParent, 
     215                 :                          nsIFrame*       aPrevInFlow)
     216                 : {
     217               0 :   nsresult rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
     218               0 :   NS_ENSURE_SUCCESS(rv, rv);
     219               0 :   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
     220               0 :   if (scrollFrame) {
     221               0 :     nsIBox* verticalScrollbar = scrollFrame->GetScrollbarBox(true);
     222               0 :     nsScrollbarFrame* scrollbarFrame = do_QueryFrame(verticalScrollbar);
     223               0 :     if (scrollbarFrame) {
     224               0 :       scrollbarFrame->SetScrollbarMediatorContent(GetContent());
     225                 :     }
     226                 :   }
     227               0 :   nsRefPtr<nsFontMetrics> fm;
     228               0 :   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
     229               0 :   mRowHeight = fm->MaxHeight();
     230                 : 
     231               0 :   return rv;
     232                 : }
     233                 : 
     234                 : void
     235               0 : nsListBoxBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
     236                 : {
     237                 :   // make sure we cancel any posted callbacks.
     238               0 :   if (mReflowCallbackPosted)
     239               0 :      PresContext()->PresShell()->CancelReflowCallback(this);
     240                 : 
     241                 :   // Revoke any pending position changed events
     242               0 :   for (PRUint32 i = 0; i < mPendingPositionChangeEvents.Length(); ++i) {
     243               0 :     mPendingPositionChangeEvents[i]->Revoke();
     244                 :   }
     245                 : 
     246                 :   // Make sure we tell our listbox's box object we're being destroyed.
     247               0 :   if (mBoxObject) {
     248               0 :     mBoxObject->ClearCachedValues();
     249                 :   }
     250                 : 
     251               0 :   nsBoxFrame::DestroyFrom(aDestructRoot);
     252               0 : }
     253                 : 
     254                 : NS_IMETHODIMP
     255               0 : nsListBoxBodyFrame::AttributeChanged(PRInt32 aNameSpaceID,
     256                 :                                      nsIAtom* aAttribute, 
     257                 :                                      PRInt32 aModType)
     258                 : {
     259               0 :   nsresult rv = NS_OK;
     260                 : 
     261               0 :   if (aAttribute == nsGkAtoms::rows) {
     262               0 :     PresContext()->PresShell()->
     263               0 :       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
     264                 :   }
     265                 :   else
     266               0 :     rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
     267                 : 
     268               0 :   return rv;
     269                 :  
     270                 : }
     271                 : 
     272                 : /////////// nsIBox ///////////////
     273                 : 
     274                 : /* virtual */ void
     275               0 : nsListBoxBodyFrame::MarkIntrinsicWidthsDirty()
     276                 : {
     277               0 :   mStringWidth = -1;
     278               0 :   nsBoxFrame::MarkIntrinsicWidthsDirty();
     279               0 : }
     280                 : 
     281                 : /////////// nsBox ///////////////
     282                 : 
     283                 : NS_IMETHODIMP
     284               0 : nsListBoxBodyFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
     285                 : {
     286               0 :   if (mScrolling)
     287               0 :     aBoxLayoutState.SetPaintingDisabled(true);
     288                 : 
     289               0 :   nsresult rv = nsBoxFrame::DoLayout(aBoxLayoutState);
     290                 : 
     291                 :   // determine the real height for the scrollable area from the total number
     292                 :   // of rows, since non-visible rows don't yet have frames
     293               0 :   nsRect rect(nsPoint(0, 0), GetSize());
     294               0 :   nsOverflowAreas overflow(rect, rect);
     295               0 :   if (mLayoutManager) {
     296               0 :     nsIFrame* childFrame = mFrames.FirstChild();
     297               0 :     while (childFrame) {
     298               0 :       ConsiderChildOverflow(overflow, childFrame);
     299               0 :       childFrame = childFrame->GetNextSibling();
     300                 :     }
     301                 : 
     302               0 :     nsSize prefSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
     303               0 :     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
     304               0 :       nsRect& o = overflow.Overflow(otype);
     305               0 :       o.height = NS_MAX(o.height, prefSize.height);
     306                 :     }
     307                 :   }
     308               0 :   FinishAndStoreOverflow(overflow, GetSize());
     309                 : 
     310               0 :   if (mScrolling)
     311               0 :     aBoxLayoutState.SetPaintingDisabled(false);
     312                 : 
     313                 :   // if we are scrolled and the row height changed
     314                 :   // make sure we are scrolled to a correct index.
     315               0 :   if (mAdjustScroll)
     316               0 :      PostReflowCallback();
     317                 : 
     318               0 :   return rv;
     319                 : }
     320                 : 
     321                 : nsSize
     322               0 : nsListBoxBodyFrame::GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState)
     323                 : {
     324               0 :   nsSize result(0, 0);
     325               0 :   if (nsContentUtils::HasNonEmptyAttr(GetContent(), kNameSpaceID_None,
     326               0 :                                       nsGkAtoms::sizemode)) {
     327               0 :     result = GetPrefSize(aBoxLayoutState);
     328               0 :     result.height = 0;
     329               0 :     nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
     330               0 :     if (scrollFrame &&
     331               0 :         scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
     332                 :       nsMargin scrollbars =
     333               0 :         scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
     334               0 :       result.width += scrollbars.left + scrollbars.right;
     335                 :     }
     336                 :   }
     337                 :   return result;
     338                 : }
     339                 : 
     340                 : nsSize
     341               0 : nsListBoxBodyFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
     342                 : {  
     343               0 :   nsSize pref = nsBoxFrame::GetPrefSize(aBoxLayoutState);
     344                 : 
     345               0 :   PRInt32 size = GetFixedRowSize();
     346               0 :   if (size > -1)
     347               0 :     pref.height = size*GetRowHeightAppUnits();
     348                 : 
     349               0 :   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
     350               0 :   if (scrollFrame &&
     351               0 :       scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
     352               0 :     nsMargin scrollbars = scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
     353               0 :     pref.width += scrollbars.left + scrollbars.right;
     354                 :   }
     355                 :   return pref;
     356                 : }
     357                 : 
     358                 : ///////////// nsIScrollbarMediator ///////////////
     359                 : 
     360                 : NS_IMETHODIMP
     361               0 : nsListBoxBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, PRInt32 aOldIndex, PRInt32& aNewIndex)
     362                 : { 
     363               0 :   if (mScrolling || mRowHeight == 0)
     364               0 :     return NS_OK;
     365                 : 
     366                 :   nscoord oldTwipIndex, newTwipIndex;
     367               0 :   oldTwipIndex = mCurrentIndex*mRowHeight;
     368               0 :   newTwipIndex = nsPresContext::CSSPixelsToAppUnits(aNewIndex);
     369               0 :   PRInt32 twipDelta = newTwipIndex > oldTwipIndex ? newTwipIndex - oldTwipIndex : oldTwipIndex - newTwipIndex;
     370                 : 
     371               0 :   PRInt32 rowDelta = twipDelta / mRowHeight;
     372               0 :   PRInt32 remainder = twipDelta % mRowHeight;
     373               0 :   if (remainder > (mRowHeight/2))
     374               0 :     rowDelta++;
     375                 : 
     376               0 :   if (rowDelta == 0)
     377               0 :     return NS_OK;
     378                 : 
     379                 :   // update the position to be row based.
     380                 : 
     381               0 :   PRInt32 newIndex = newTwipIndex > oldTwipIndex ? mCurrentIndex + rowDelta : mCurrentIndex - rowDelta;
     382                 :   //aNewIndex = newIndex*mRowHeight/mOnePixel;
     383                 : 
     384               0 :   nsListScrollSmoother* smoother = GetSmoother();
     385                 : 
     386                 :   // if we can't scroll the rows in time then start a timer. We will eat
     387                 :   // events until the user stops moving and the timer stops.
     388               0 :   if (smoother->IsRunning() || rowDelta*mTimePerRow > USER_TIME_THRESHOLD) {
     389                 : 
     390               0 :      smoother->Stop();
     391                 : 
     392               0 :      smoother->mDelta = newTwipIndex > oldTwipIndex ? rowDelta : -rowDelta;
     393                 : 
     394               0 :      smoother->Start();
     395                 : 
     396               0 :      return NS_OK;
     397                 :   }
     398                 : 
     399               0 :   smoother->Stop();
     400                 : 
     401               0 :   mCurrentIndex = newIndex;
     402               0 :   smoother->mDelta = 0;
     403                 :   
     404               0 :   if (mCurrentIndex < 0) {
     405               0 :     mCurrentIndex = 0;
     406               0 :     return NS_OK;
     407                 :   }
     408                 : 
     409               0 :   return InternalPositionChanged(newTwipIndex < oldTwipIndex, rowDelta);
     410                 : }
     411                 : 
     412                 : NS_IMETHODIMP
     413               0 : nsListBoxBodyFrame::VisibilityChanged(bool aVisible)
     414                 : {
     415               0 :   if (mRowHeight == 0)
     416               0 :     return NS_OK;
     417                 : 
     418               0 :   PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
     419               0 :   if (lastPageTopRow < 0)
     420               0 :     lastPageTopRow = 0;
     421               0 :   PRInt32 delta = mCurrentIndex - lastPageTopRow;
     422               0 :   if (delta > 0) {
     423               0 :     mCurrentIndex = lastPageTopRow;
     424               0 :     InternalPositionChanged(true, delta);
     425                 :   }
     426                 : 
     427               0 :   return NS_OK;
     428                 : }
     429                 : 
     430                 : NS_IMETHODIMP
     431               0 : nsListBoxBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, PRInt32 aOldIndex, PRInt32 aNewIndex)
     432                 : {
     433               0 :   if (aOldIndex == aNewIndex)
     434               0 :     return NS_OK;
     435               0 :   if (aNewIndex < aOldIndex)
     436               0 :     mCurrentIndex--;
     437               0 :   else mCurrentIndex++;
     438               0 :   if (mCurrentIndex < 0) {
     439               0 :     mCurrentIndex = 0;
     440               0 :     return NS_OK;
     441                 :   }
     442               0 :   InternalPositionChanged(aNewIndex < aOldIndex, 1);
     443                 : 
     444               0 :   return NS_OK;
     445                 : }
     446                 : 
     447                 : ///////////// nsIReflowCallback ///////////////
     448                 : 
     449                 : bool
     450               0 : nsListBoxBodyFrame::ReflowFinished()
     451                 : {
     452               0 :   nsAutoScriptBlocker scriptBlocker;
     453                 :   // now create or destroy any rows as needed
     454               0 :   CreateRows();
     455                 : 
     456                 :   // keep scrollbar in sync
     457               0 :   if (mAdjustScroll) {
     458               0 :      VerticalScroll(mYPosition);
     459               0 :      mAdjustScroll = false;
     460                 :   }
     461                 : 
     462                 :   // if the row height changed then mark everything as a style change. 
     463                 :   // That will dirty the entire listbox
     464               0 :   if (mRowHeightWasSet) {
     465               0 :     PresContext()->PresShell()->
     466               0 :       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
     467               0 :      PRInt32 pos = mCurrentIndex * mRowHeight;
     468               0 :      if (mYPosition != pos) 
     469               0 :        mAdjustScroll = true;
     470               0 :     mRowHeightWasSet = false;
     471                 :   }
     472                 : 
     473               0 :   mReflowCallbackPosted = false;
     474               0 :   return true;
     475                 : }
     476                 : 
     477                 : void
     478               0 : nsListBoxBodyFrame::ReflowCallbackCanceled()
     479                 : {
     480               0 :   mReflowCallbackPosted = false;
     481               0 : }
     482                 : 
     483                 : ///////// nsIListBoxObject ///////////////
     484                 : 
     485                 : nsresult
     486               0 : nsListBoxBodyFrame::GetRowCount(PRInt32* aResult)
     487                 : {
     488               0 :   *aResult = GetRowCount();
     489               0 :   return NS_OK;
     490                 : }
     491                 : 
     492                 : nsresult
     493               0 : nsListBoxBodyFrame::GetNumberOfVisibleRows(PRInt32 *aResult)
     494                 : {
     495               0 :   *aResult= mRowHeight ? GetAvailableHeight() / mRowHeight : 0;
     496               0 :   return NS_OK;
     497                 : }
     498                 : 
     499                 : nsresult
     500               0 : nsListBoxBodyFrame::GetIndexOfFirstVisibleRow(PRInt32 *aResult)
     501                 : {
     502               0 :   *aResult = mCurrentIndex;
     503               0 :   return NS_OK;
     504                 : }
     505                 : 
     506                 : nsresult
     507               0 : nsListBoxBodyFrame::EnsureIndexIsVisible(PRInt32 aRowIndex)
     508                 : {
     509               0 :   if (aRowIndex < 0)
     510               0 :     return NS_ERROR_ILLEGAL_VALUE;
     511                 : 
     512               0 :   PRInt32 rows = 0;
     513               0 :   if (mRowHeight)
     514               0 :     rows = GetAvailableHeight()/mRowHeight;
     515               0 :   if (rows <= 0)
     516               0 :     rows = 1;
     517               0 :   PRInt32 bottomIndex = mCurrentIndex + rows;
     518                 :   
     519                 :   // if row is visible, ignore
     520               0 :   if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex)
     521               0 :     return NS_OK;
     522                 : 
     523                 :   PRInt32 delta;
     524                 : 
     525               0 :   bool up = aRowIndex < mCurrentIndex;
     526               0 :   if (up) {
     527               0 :     delta = mCurrentIndex - aRowIndex;
     528               0 :     mCurrentIndex = aRowIndex;
     529                 :   }
     530                 :   else {
     531                 :     // Check to be sure we're not scrolling off the bottom of the tree
     532               0 :     if (aRowIndex >= GetRowCount())
     533               0 :       return NS_ERROR_ILLEGAL_VALUE;
     534                 : 
     535                 :     // Bring it just into view.
     536               0 :     delta = 1 + (aRowIndex-bottomIndex);
     537               0 :     mCurrentIndex += delta; 
     538                 :   }
     539                 : 
     540                 :   // Safe to not go off an event here, since this is coming from the
     541                 :   // box object.
     542               0 :   DoInternalPositionChangedSync(up, delta);
     543               0 :   return NS_OK;
     544                 : }
     545                 : 
     546                 : nsresult
     547               0 : nsListBoxBodyFrame::ScrollByLines(PRInt32 aNumLines)
     548                 : {
     549                 :   PRInt32 scrollIndex, visibleRows;
     550               0 :   GetIndexOfFirstVisibleRow(&scrollIndex);
     551               0 :   GetNumberOfVisibleRows(&visibleRows);
     552                 : 
     553               0 :   scrollIndex += aNumLines;
     554                 :   
     555               0 :   if (scrollIndex < 0)
     556               0 :     scrollIndex = 0;
     557                 :   else {
     558               0 :     PRInt32 numRows = GetRowCount();
     559               0 :     PRInt32 lastPageTopRow = numRows - visibleRows;
     560               0 :     if (scrollIndex > lastPageTopRow)
     561               0 :       scrollIndex = lastPageTopRow;
     562                 :   }
     563                 :   
     564               0 :   ScrollToIndex(scrollIndex);
     565                 : 
     566               0 :   return NS_OK;
     567                 : }
     568                 : 
     569                 : // walks the DOM to get the zero-based row index of the content
     570                 : nsresult
     571               0 : nsListBoxBodyFrame::GetIndexOfItem(nsIDOMElement* aItem, PRInt32* _retval)
     572                 : {
     573               0 :   if (aItem) {
     574               0 :     *_retval = 0;
     575               0 :     nsCOMPtr<nsIContent> itemContent(do_QueryInterface(aItem));
     576                 : 
     577               0 :     ChildIterator iter, last;
     578               0 :     for (ChildIterator::Init(mContent, &iter, &last);
     579                 :          iter != last;
     580                 :          ++iter) {
     581               0 :       nsIContent *child = (*iter);
     582                 :       // we hit a list row, count it
     583               0 :       if (child->Tag() == nsGkAtoms::listitem) {
     584                 :         // is this it?
     585               0 :         if (child == itemContent)
     586               0 :           return NS_OK;
     587                 : 
     588               0 :         ++(*_retval);
     589                 :       }
     590                 :     }
     591                 :   }
     592                 : 
     593                 :   // not found
     594               0 :   *_retval = -1;
     595               0 :   return NS_OK;
     596                 : }
     597                 : 
     598                 : nsresult
     599               0 : nsListBoxBodyFrame::GetItemAtIndex(PRInt32 aIndex, nsIDOMElement** aItem)
     600                 : {
     601               0 :   *aItem = nsnull;
     602               0 :   if (aIndex < 0)
     603               0 :     return NS_OK;
     604                 :   
     605               0 :   PRInt32 itemCount = 0;
     606               0 :   ChildIterator iter, last;
     607               0 :   for (ChildIterator::Init(mContent, &iter, &last);
     608                 :        iter != last;
     609                 :        ++iter) {
     610               0 :     nsIContent *child = (*iter);
     611                 :     // we hit a list row, check if it is the one we are looking for
     612               0 :     if (child->Tag() == nsGkAtoms::listitem) {
     613                 :       // is this it?
     614               0 :       if (itemCount == aIndex) {
     615               0 :         return CallQueryInterface(child, aItem);
     616                 :       }
     617               0 :       ++itemCount;
     618                 :     }
     619                 :   }
     620                 : 
     621                 :   // not found
     622               0 :   return NS_OK;
     623                 : }
     624                 : 
     625                 : /////////// nsListBoxBodyFrame ///////////////
     626                 : 
     627                 : PRInt32
     628               0 : nsListBoxBodyFrame::GetRowCount()
     629                 : {
     630               0 :   if (mRowCount < 0)
     631               0 :     ComputeTotalRowCount();
     632               0 :   return mRowCount;
     633                 : }
     634                 : 
     635                 : PRInt32
     636               0 : nsListBoxBodyFrame::GetFixedRowSize()
     637                 : {
     638                 :   PRInt32 dummy;
     639                 : 
     640               0 :   nsAutoString rows;
     641               0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
     642               0 :   if (!rows.IsEmpty())
     643               0 :     return rows.ToInteger(&dummy);
     644                 :  
     645               0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::size, rows);
     646                 : 
     647               0 :   if (!rows.IsEmpty())
     648               0 :     return rows.ToInteger(&dummy);
     649                 : 
     650               0 :   return -1;
     651                 : }
     652                 : 
     653                 : void
     654               0 : nsListBoxBodyFrame::SetRowHeight(nscoord aRowHeight)
     655                 : { 
     656               0 :   if (aRowHeight > mRowHeight) { 
     657               0 :     mRowHeight = aRowHeight;
     658                 : 
     659                 :     // signal we need to dirty everything 
     660                 :     // and we want to be notified after reflow
     661                 :     // so we can create or destory rows as needed
     662               0 :     mRowHeightWasSet = true;
     663               0 :     PostReflowCallback();
     664                 :   }
     665               0 : }
     666                 : 
     667                 : nscoord
     668               0 : nsListBoxBodyFrame::GetAvailableHeight()
     669                 : {
     670                 :   nsIScrollableFrame* scrollFrame =
     671               0 :     nsLayoutUtils::GetScrollableFrameFor(this);
     672               0 :   if (scrollFrame) {
     673               0 :     return scrollFrame->GetScrollPortRect().height;
     674                 :   }
     675               0 :   return 0;
     676                 : }
     677                 : 
     678                 : nscoord
     679               0 : nsListBoxBodyFrame::GetYPosition()
     680                 : {
     681               0 :   return mYPosition;
     682                 : }
     683                 : 
     684                 : nscoord
     685               0 : nsListBoxBodyFrame::ComputeIntrinsicWidth(nsBoxLayoutState& aBoxLayoutState)
     686                 : {
     687               0 :   if (mStringWidth != -1)
     688               0 :     return mStringWidth;
     689                 : 
     690               0 :   nscoord largestWidth = 0;
     691                 : 
     692               0 :   PRInt32 index = 0;
     693               0 :   nsCOMPtr<nsIDOMElement> firstRowEl;
     694               0 :   GetItemAtIndex(index, getter_AddRefs(firstRowEl));
     695               0 :   nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl));
     696                 : 
     697               0 :   if (firstRowContent) {
     698               0 :     nsRefPtr<nsStyleContext> styleContext;
     699               0 :     nsPresContext *presContext = aBoxLayoutState.PresContext();
     700                 :     styleContext = presContext->StyleSet()->
     701               0 :       ResolveStyleFor(firstRowContent->AsElement(), nsnull);
     702                 : 
     703               0 :     nscoord width = 0;
     704               0 :     nsMargin margin(0,0,0,0);
     705                 : 
     706               0 :     if (styleContext->GetStylePadding()->GetPadding(margin))
     707               0 :       width += margin.LeftRight();
     708               0 :     width += styleContext->GetStyleBorder()->GetActualBorder().LeftRight();
     709               0 :     if (styleContext->GetStyleMargin()->GetMargin(margin))
     710               0 :       width += margin.LeftRight();
     711                 : 
     712                 : 
     713               0 :     ChildIterator iter, last;
     714               0 :     PRUint32 i = 0;
     715               0 :     for (ChildIterator::Init(mContent, &iter, &last);
     716               0 :          iter != last && i < 100;
     717                 :          ++iter, ++i) {
     718               0 :       nsIContent *child = (*iter);
     719                 : 
     720               0 :       if (child->Tag() == nsGkAtoms::listitem) {
     721               0 :         nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
     722               0 :         if (rendContext) {
     723               0 :           nsAutoString value;
     724               0 :           PRUint32 textCount = child->GetChildCount();
     725               0 :           for (PRUint32 j = 0; j < textCount; ++j) {
     726               0 :             nsIContent* text = child->GetChildAt(j);
     727               0 :             if (text && text->IsNodeOfType(nsINode::eTEXT)) {
     728               0 :               text->AppendTextTo(value);
     729                 :             }
     730                 :           }
     731                 : 
     732               0 :           nsRefPtr<nsFontMetrics> fm;
     733                 :           nsLayoutUtils::GetFontMetricsForStyleContext(styleContext,
     734               0 :                                                        getter_AddRefs(fm));
     735               0 :           rendContext->SetFont(fm);
     736                 : 
     737                 :           nscoord textWidth =
     738               0 :             nsLayoutUtils::GetStringWidth(this, rendContext, value.get(), value.Length());
     739               0 :           textWidth += width;
     740                 : 
     741               0 :           if (textWidth > largestWidth) 
     742               0 :             largestWidth = textWidth;
     743                 :         }
     744                 :       }
     745                 :     }
     746                 :   }
     747                 : 
     748               0 :   mStringWidth = largestWidth;
     749               0 :   return mStringWidth;
     750                 : }
     751                 : 
     752                 : void
     753               0 : nsListBoxBodyFrame::ComputeTotalRowCount()
     754                 : {
     755               0 :   mRowCount = 0;
     756                 : 
     757               0 :   ChildIterator iter, last;
     758               0 :   for (ChildIterator::Init(mContent, &iter, &last);
     759                 :        iter != last;
     760                 :        ++iter) {
     761               0 :     if ((*iter)->Tag() == nsGkAtoms::listitem)
     762               0 :       ++mRowCount;
     763                 :   }
     764               0 : }
     765                 : 
     766                 : void
     767               0 : nsListBoxBodyFrame::PostReflowCallback()
     768                 : {
     769               0 :   if (!mReflowCallbackPosted) {
     770               0 :     mReflowCallbackPosted = true;
     771               0 :     PresContext()->PresShell()->PostReflowCallback(this);
     772                 :   }
     773               0 : }
     774                 : 
     775                 : ////////// scrolling
     776                 : 
     777                 : nsresult
     778               0 : nsListBoxBodyFrame::ScrollToIndex(PRInt32 aRowIndex)
     779                 : {
     780               0 :   if (( aRowIndex < 0 ) || (mRowHeight == 0))
     781               0 :     return NS_OK;
     782                 :     
     783               0 :   PRInt32 newIndex = aRowIndex;
     784               0 :   PRInt32 delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex;
     785               0 :   bool up = newIndex < mCurrentIndex;
     786                 : 
     787                 :   // Check to be sure we're not scrolling off the bottom of the tree
     788               0 :   PRInt32 lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
     789               0 :   if (lastPageTopRow < 0)
     790               0 :     lastPageTopRow = 0;
     791                 : 
     792               0 :   if (aRowIndex > lastPageTopRow)
     793               0 :     return NS_OK;
     794                 : 
     795               0 :   mCurrentIndex = newIndex;
     796                 : 
     797               0 :   nsWeakFrame weak(this);
     798                 : 
     799                 :   // Since we're going to flush anyway, we need to not do this off an event
     800               0 :   DoInternalPositionChangedSync(up, delta);
     801                 : 
     802               0 :   if (!weak.IsAlive()) {
     803               0 :     return NS_OK;
     804                 :   }
     805                 : 
     806                 :   // This change has to happen immediately.
     807                 :   // Flush any pending reflow commands.
     808                 :   // XXXbz why, exactly?
     809               0 :   mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
     810                 : 
     811               0 :   return NS_OK;
     812                 : }
     813                 : 
     814                 : nsresult
     815               0 : nsListBoxBodyFrame::InternalPositionChangedCallback()
     816                 : {
     817               0 :   nsListScrollSmoother* smoother = GetSmoother();
     818                 : 
     819               0 :   if (smoother->mDelta == 0)
     820               0 :     return NS_OK;
     821                 : 
     822               0 :   mCurrentIndex += smoother->mDelta;
     823                 : 
     824               0 :   if (mCurrentIndex < 0)
     825               0 :     mCurrentIndex = 0;
     826                 : 
     827                 :   return DoInternalPositionChangedSync(smoother->mDelta < 0,
     828                 :                                        smoother->mDelta < 0 ?
     829               0 :                                          -smoother->mDelta : smoother->mDelta);
     830                 : }
     831                 : 
     832                 : nsresult
     833               0 : nsListBoxBodyFrame::InternalPositionChanged(bool aUp, PRInt32 aDelta)
     834                 : {
     835                 :   nsRefPtr<nsPositionChangedEvent> ev =
     836               0 :     new nsPositionChangedEvent(this, aUp, aDelta);
     837               0 :   nsresult rv = NS_DispatchToCurrentThread(ev);
     838               0 :   if (NS_SUCCEEDED(rv)) {
     839               0 :     if (!mPendingPositionChangeEvents.AppendElement(ev)) {
     840               0 :       rv = NS_ERROR_OUT_OF_MEMORY;
     841               0 :       ev->Revoke();
     842                 :     }
     843                 :   }
     844               0 :   return rv;
     845                 : }
     846                 : 
     847                 : nsresult
     848               0 : nsListBoxBodyFrame::DoInternalPositionChangedSync(bool aUp, PRInt32 aDelta)
     849                 : {
     850               0 :   nsWeakFrame weak(this);
     851                 :   
     852                 :   // Process all the pending position changes first
     853               0 :   nsTArray< nsRefPtr<nsPositionChangedEvent> > temp;
     854               0 :   temp.SwapElements(mPendingPositionChangeEvents);
     855               0 :   for (PRUint32 i = 0; i < temp.Length(); ++i) {
     856               0 :     if (weak.IsAlive()) {
     857               0 :       temp[i]->Run();
     858                 :     }
     859               0 :     temp[i]->Revoke();
     860                 :   }
     861                 : 
     862               0 :   if (!weak.IsAlive()) {
     863               0 :     return NS_OK;
     864                 :   }
     865                 : 
     866               0 :   return DoInternalPositionChanged(aUp, aDelta);
     867                 : }
     868                 : 
     869                 : nsresult
     870               0 : nsListBoxBodyFrame::DoInternalPositionChanged(bool aUp, PRInt32 aDelta)
     871                 : {
     872               0 :   if (aDelta == 0)
     873               0 :     return NS_OK;
     874                 : 
     875               0 :   nsRefPtr<nsPresContext> presContext(PresContext());
     876               0 :   nsBoxLayoutState state(presContext);
     877                 : 
     878                 :   // begin timing how long it takes to scroll a row
     879               0 :   PRTime start = PR_Now();
     880                 : 
     881               0 :   nsWeakFrame weakThis(this);
     882               0 :   mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
     883               0 :   if (!weakThis.IsAlive()) {
     884               0 :     return NS_OK;
     885                 :   }
     886                 : 
     887                 :   {
     888               0 :     nsAutoScriptBlocker scriptBlocker;
     889                 : 
     890               0 :     PRInt32 visibleRows = 0;
     891               0 :     if (mRowHeight)
     892               0 :       visibleRows = GetAvailableHeight()/mRowHeight;
     893                 :   
     894               0 :     if (aDelta < visibleRows) {
     895               0 :       PRInt32 loseRows = aDelta;
     896               0 :       if (aUp) {
     897                 :         // scrolling up, destroy rows from the bottom downwards
     898               0 :         ReverseDestroyRows(loseRows);
     899               0 :         mRowsToPrepend += aDelta;
     900               0 :         mLinkupFrame = nsnull;
     901                 :       }
     902                 :       else {
     903                 :         // scrolling down, destroy rows from the top upwards
     904               0 :         DestroyRows(loseRows);
     905               0 :         mRowsToPrepend = 0;
     906                 :       }
     907                 :     }
     908                 :     else {
     909                 :       // We have scrolled so much that all of our current frames will
     910                 :       // go off screen, so blow them all away. Weeee!
     911               0 :       nsIFrame *currBox = mFrames.FirstChild();
     912               0 :       nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
     913               0 :       fc->BeginUpdate();
     914               0 :       while (currBox) {
     915               0 :         nsIFrame *nextBox = currBox->GetNextSibling();
     916               0 :         RemoveChildFrame(state, currBox);
     917               0 :         currBox = nextBox;
     918                 :       }
     919               0 :       fc->EndUpdate();
     920                 :     }
     921                 : 
     922                 :     // clear frame markers so that CreateRows will re-create
     923               0 :     mTopFrame = mBottomFrame = nsnull; 
     924                 :   
     925               0 :     mYPosition = mCurrentIndex*mRowHeight;
     926               0 :     mScrolling = true;
     927               0 :     presContext->PresShell()->
     928               0 :       FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
     929                 :   }
     930               0 :   if (!weakThis.IsAlive()) {
     931               0 :     return NS_OK;
     932                 :   }
     933                 :   // Flush calls CreateRows
     934                 :   // XXXbz there has to be a better way to do this than flushing!
     935               0 :   presContext->PresShell()->FlushPendingNotifications(Flush_Layout);
     936               0 :   if (!weakThis.IsAlive()) {
     937               0 :     return NS_OK;
     938                 :   }
     939                 : 
     940               0 :   mScrolling = false;
     941                 :   
     942               0 :   VerticalScroll(mYPosition);
     943                 : 
     944               0 :   PRTime end = PR_Now();
     945                 : 
     946                 :   PRTime difTime;
     947               0 :   LL_SUB(difTime, end, start);
     948                 : 
     949                 :   PRInt32 newTime;
     950               0 :   LL_L2I(newTime, difTime);
     951               0 :   newTime /= aDelta;
     952                 : 
     953                 :   // average old and new
     954               0 :   mTimePerRow = (newTime + mTimePerRow)/2;
     955                 :   
     956               0 :   return NS_OK;
     957                 : }
     958                 : 
     959                 : nsListScrollSmoother* 
     960               0 : nsListBoxBodyFrame::GetSmoother()
     961                 : {
     962               0 :   if (!mScrollSmoother) {
     963               0 :     mScrollSmoother = new nsListScrollSmoother(this);
     964               0 :     NS_ASSERTION(mScrollSmoother, "out of memory");
     965               0 :     NS_IF_ADDREF(mScrollSmoother);
     966                 :   }
     967                 : 
     968               0 :   return mScrollSmoother;
     969                 : }
     970                 : 
     971                 : void
     972               0 : nsListBoxBodyFrame::VerticalScroll(PRInt32 aPosition)
     973                 : {
     974                 :   nsIScrollableFrame* scrollFrame
     975               0 :     = nsLayoutUtils::GetScrollableFrameFor(this);
     976               0 :   if (!scrollFrame) {
     977               0 :     return;
     978                 :   }
     979                 : 
     980               0 :   nsPoint scrollPosition = scrollFrame->GetScrollPosition();
     981                 :  
     982                 :   scrollFrame->ScrollTo(nsPoint(scrollPosition.x, aPosition),
     983               0 :                         nsIScrollableFrame::INSTANT);
     984                 : 
     985               0 :   mYPosition = aPosition;
     986                 : }
     987                 : 
     988                 : ////////// frame and box retrieval
     989                 : 
     990                 : nsIFrame*
     991               0 : nsListBoxBodyFrame::GetFirstFrame()
     992                 : {
     993               0 :   mTopFrame = mFrames.FirstChild();
     994               0 :   return mTopFrame;
     995                 : }
     996                 : 
     997                 : nsIFrame*
     998               0 : nsListBoxBodyFrame::GetLastFrame()
     999                 : {
    1000               0 :   return mFrames.LastChild();
    1001                 : }
    1002                 : 
    1003                 : bool
    1004               0 : nsListBoxBodyFrame::SupportsOrdinalsInChildren()
    1005                 : {
    1006               0 :   return false;
    1007                 : }
    1008                 : 
    1009                 : ////////// lazy row creation and destruction
    1010                 : 
    1011                 : void
    1012               0 : nsListBoxBodyFrame::CreateRows()
    1013                 : {
    1014                 :   // Get our client rect.
    1015               0 :   nsRect clientRect;
    1016               0 :   GetClientRect(clientRect);
    1017                 : 
    1018                 :   // Get the starting y position and the remaining available
    1019                 :   // height.
    1020               0 :   nscoord availableHeight = GetAvailableHeight();
    1021                 :   
    1022               0 :   if (availableHeight <= 0) {
    1023               0 :     bool fixed = (GetFixedRowSize() != -1);
    1024               0 :     if (fixed)
    1025               0 :       availableHeight = 10;
    1026                 :     else
    1027                 :       return;
    1028                 :   }
    1029                 :   
    1030                 :   // get the first tree box. If there isn't one create one.
    1031               0 :   bool created = false;
    1032               0 :   nsIBox* box = GetFirstItemBox(0, &created);
    1033               0 :   nscoord rowHeight = GetRowHeightAppUnits();
    1034               0 :   while (box) {  
    1035               0 :     if (created && mRowsToPrepend > 0)
    1036               0 :       --mRowsToPrepend;
    1037                 : 
    1038                 :     // if the row height is 0 then fail. Wait until someone 
    1039                 :     // laid out and sets the row height.
    1040               0 :     if (rowHeight == 0)
    1041                 :         return;
    1042                 :      
    1043               0 :     availableHeight -= rowHeight;
    1044                 :     
    1045                 :     // should we continue? Is the enought height?
    1046               0 :     if (!ContinueReflow(availableHeight))
    1047               0 :       break;
    1048                 : 
    1049                 :     // get the next tree box. Create one if needed.
    1050               0 :     box = GetNextItemBox(box, 0, &created);
    1051                 :   }
    1052                 : 
    1053               0 :   mRowsToPrepend = 0;
    1054               0 :   mLinkupFrame = nsnull;
    1055                 : }
    1056                 : 
    1057                 : void
    1058               0 : nsListBoxBodyFrame::DestroyRows(PRInt32& aRowsToLose) 
    1059                 : {
    1060                 :   // We need to destroy frames until our row count has been properly
    1061                 :   // reduced.  A reflow will then pick up and create the new frames.
    1062               0 :   nsIFrame* childFrame = GetFirstFrame();
    1063               0 :   nsBoxLayoutState state(PresContext());
    1064                 : 
    1065               0 :   nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
    1066               0 :   fc->BeginUpdate();
    1067               0 :   while (childFrame && aRowsToLose > 0) {
    1068               0 :     --aRowsToLose;
    1069                 : 
    1070               0 :     nsIFrame* nextFrame = childFrame->GetNextSibling();
    1071               0 :     RemoveChildFrame(state, childFrame);
    1072                 : 
    1073               0 :     mTopFrame = childFrame = nextFrame;
    1074                 :   }
    1075               0 :   fc->EndUpdate();
    1076                 : 
    1077               0 :   PresContext()->PresShell()->
    1078                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1079               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1080               0 : }
    1081                 : 
    1082                 : void
    1083               0 : nsListBoxBodyFrame::ReverseDestroyRows(PRInt32& aRowsToLose) 
    1084                 : {
    1085                 :   // We need to destroy frames until our row count has been properly
    1086                 :   // reduced.  A reflow will then pick up and create the new frames.
    1087               0 :   nsIFrame* childFrame = GetLastFrame();
    1088               0 :   nsBoxLayoutState state(PresContext());
    1089                 : 
    1090               0 :   nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
    1091               0 :   fc->BeginUpdate();
    1092               0 :   while (childFrame && aRowsToLose > 0) {
    1093               0 :     --aRowsToLose;
    1094                 :     
    1095                 :     nsIFrame* prevFrame;
    1096               0 :     prevFrame = childFrame->GetPrevSibling();
    1097               0 :     RemoveChildFrame(state, childFrame);
    1098                 : 
    1099               0 :     mBottomFrame = childFrame = prevFrame;
    1100                 :   }
    1101               0 :   fc->EndUpdate();
    1102                 : 
    1103               0 :   PresContext()->PresShell()->
    1104                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1105               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1106               0 : }
    1107                 : 
    1108                 : static bool
    1109               0 : IsListItemChild(nsListBoxBodyFrame* aParent, nsIContent* aChild,
    1110                 :                 nsIFrame** aChildFrame)
    1111                 : {
    1112               0 :   *aChildFrame = nsnull;
    1113               0 :   if (!aChild->IsXUL() || aChild->Tag() != nsGkAtoms::listitem) {
    1114               0 :     return false;
    1115                 :   }
    1116               0 :   nsIFrame* existingFrame = aChild->GetPrimaryFrame();
    1117               0 :   if (existingFrame && existingFrame->GetParent() != aParent) {
    1118               0 :     return false;
    1119                 :   }
    1120               0 :   *aChildFrame = existingFrame;
    1121               0 :   return true;
    1122                 : }
    1123                 : 
    1124                 : //
    1125                 : // Get the nsIBox for the first visible listitem, and if none exists,
    1126                 : // create one.
    1127                 : //
    1128                 : nsIBox* 
    1129               0 : nsListBoxBodyFrame::GetFirstItemBox(PRInt32 aOffset, bool* aCreated)
    1130                 : {
    1131               0 :   if (aCreated)
    1132               0 :    *aCreated = false;
    1133                 : 
    1134                 :   // Clear ourselves out.
    1135               0 :   mBottomFrame = mTopFrame;
    1136                 : 
    1137               0 :   if (mTopFrame) {
    1138               0 :     return mTopFrame->IsBoxFrame() ? static_cast<nsIBox*>(mTopFrame) : nsnull;
    1139                 :   }
    1140                 : 
    1141                 :   // top frame was cleared out
    1142               0 :   mTopFrame = GetFirstFrame();
    1143               0 :   mBottomFrame = mTopFrame;
    1144                 : 
    1145               0 :   if (mTopFrame && mRowsToPrepend <= 0) {
    1146               0 :     return mTopFrame->IsBoxFrame() ? static_cast<nsIBox*>(mTopFrame) : nsnull;
    1147                 :   }
    1148                 : 
    1149                 :   // At this point, we either have no frames at all, 
    1150                 :   // or the user has scrolled upwards, leaving frames
    1151                 :   // to be created at the top.  Let's determine which
    1152                 :   // content needs a new frame first.
    1153                 : 
    1154               0 :   nsCOMPtr<nsIContent> startContent;
    1155               0 :   if (mTopFrame && mRowsToPrepend > 0) {
    1156                 :     // We need to insert rows before the top frame
    1157               0 :     nsIContent* topContent = mTopFrame->GetContent();
    1158               0 :     nsIContent* topParent = topContent->GetParent();
    1159               0 :     PRInt32 contentIndex = topParent->IndexOf(topContent);
    1160               0 :     contentIndex -= aOffset;
    1161               0 :     if (contentIndex < 0)
    1162               0 :       return nsnull;
    1163               0 :     startContent = topParent->GetChildAt(contentIndex - mRowsToPrepend);
    1164                 :   } else {
    1165                 :     // This will be the first item frame we create.  Use the content
    1166                 :     // at the current index, which is the first index scrolled into view
    1167               0 :     GetListItemContentAt(mCurrentIndex+aOffset, getter_AddRefs(startContent));
    1168                 :   }
    1169                 : 
    1170               0 :   if (startContent) {  
    1171                 :     nsIFrame* existingFrame;
    1172               0 :     if (!IsListItemChild(this, startContent, &existingFrame)) {
    1173               0 :       return GetFirstItemBox(++aOffset, aCreated);
    1174                 :     }
    1175               0 :     if (existingFrame) {
    1176               0 :       return existingFrame->IsBoxFrame() ? existingFrame : nsnull;
    1177                 :     }
    1178                 : 
    1179                 :     // Either append the new frame, or prepend it (at index 0)
    1180                 :     // XXX check here if frame was even created, it may not have been if
    1181                 :     //     display: none was on listitem content
    1182               0 :     bool isAppend = mRowsToPrepend <= 0;
    1183                 :     
    1184               0 :     nsPresContext* presContext = PresContext();
    1185               0 :     nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
    1186               0 :     nsIFrame* topFrame = nsnull;
    1187                 :     fc->CreateListBoxContent(presContext, this, nsnull, startContent,
    1188               0 :                              &topFrame, isAppend, false, nsnull);
    1189               0 :     mTopFrame = topFrame;
    1190               0 :     if (mTopFrame) {
    1191               0 :       if (aCreated)
    1192               0 :         *aCreated = true;
    1193                 : 
    1194               0 :       mBottomFrame = mTopFrame;
    1195                 : 
    1196               0 :       return mTopFrame->IsBoxFrame() ? static_cast<nsIBox*>(mTopFrame) : nsnull;
    1197                 :     } else
    1198               0 :       return GetFirstItemBox(++aOffset, 0);
    1199                 :   }
    1200                 : 
    1201               0 :   return nsnull;
    1202                 : }
    1203                 : 
    1204                 : //
    1205                 : // Get the nsIBox for the next visible listitem after aBox, and if none
    1206                 : // exists, create one.
    1207                 : //
    1208                 : nsIBox* 
    1209               0 : nsListBoxBodyFrame::GetNextItemBox(nsIBox* aBox, PRInt32 aOffset,
    1210                 :                                    bool* aCreated)
    1211                 : {
    1212               0 :   if (aCreated)
    1213               0 :     *aCreated = false;
    1214                 : 
    1215               0 :   nsIFrame* result = aBox->GetNextSibling();
    1216                 : 
    1217               0 :   if (!result || result == mLinkupFrame || mRowsToPrepend > 0) {
    1218                 :     // No result found. See if there's a content node that wants a frame.
    1219               0 :     nsIContent* prevContent = aBox->GetContent();
    1220               0 :     nsIContent* parentContent = prevContent->GetParent();
    1221                 : 
    1222               0 :     PRInt32 i = parentContent->IndexOf(prevContent);
    1223                 : 
    1224               0 :     PRUint32 childCount = parentContent->GetChildCount();
    1225               0 :     if (((PRUint32)i + aOffset + 1) < childCount) {
    1226                 :       // There is a content node that wants a frame.
    1227               0 :       nsIContent *nextContent = parentContent->GetChildAt(i + aOffset + 1);
    1228                 : 
    1229                 :       nsIFrame* existingFrame;
    1230               0 :       if (!IsListItemChild(this, nextContent, &existingFrame)) {
    1231               0 :         return GetNextItemBox(aBox, ++aOffset, aCreated);
    1232                 :       }
    1233               0 :       if (!existingFrame) {
    1234                 :         // Either append the new frame, or insert it after the current frame
    1235               0 :         bool isAppend = result != mLinkupFrame && mRowsToPrepend <= 0;
    1236               0 :         nsIFrame* prevFrame = isAppend ? nsnull : aBox;
    1237                 :       
    1238               0 :         nsPresContext* presContext = PresContext();
    1239               0 :         nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
    1240                 :         fc->CreateListBoxContent(presContext, this, prevFrame, nextContent,
    1241               0 :                                  &result, isAppend, false, nsnull);
    1242                 : 
    1243               0 :         if (result) {
    1244               0 :           if (aCreated)
    1245               0 :             *aCreated = true;
    1246                 :         } else
    1247               0 :           return GetNextItemBox(aBox, ++aOffset, aCreated);
    1248                 :       } else {
    1249               0 :         result = existingFrame;
    1250                 :       }
    1251                 :             
    1252               0 :       mLinkupFrame = nsnull;
    1253                 :     }
    1254                 :   }
    1255                 : 
    1256               0 :   if (!result)
    1257               0 :     return nsnull;
    1258                 : 
    1259               0 :   mBottomFrame = result;
    1260                 : 
    1261               0 :   NS_ASSERTION(!result->IsBoxFrame() || result->GetParent() == this,
    1262                 :                "returning frame that is not in childlist");
    1263                 : 
    1264               0 :   return result->IsBoxFrame() ? result : nsnull;
    1265                 : }
    1266                 : 
    1267                 : bool
    1268               0 : nsListBoxBodyFrame::ContinueReflow(nscoord height) 
    1269                 : {
    1270                 : #ifdef ACCESSIBILITY
    1271               0 :   if (nsIPresShell::IsAccessibilityActive()) {
    1272                 :     // Create all the frames at once so screen readers and
    1273                 :     // onscreen keyboards can see the full list right away
    1274               0 :     return true;
    1275                 :   }
    1276                 : #endif
    1277                 : 
    1278               0 :   if (height <= 0) {
    1279               0 :     nsIFrame* lastChild = GetLastFrame();
    1280               0 :     nsIFrame* startingPoint = mBottomFrame;
    1281               0 :     if (startingPoint == nsnull) {
    1282                 :       // We just want to delete everything but the first item.
    1283               0 :       startingPoint = GetFirstFrame();
    1284                 :     }
    1285                 : 
    1286               0 :     if (lastChild != startingPoint) {
    1287                 :       // We have some hangers on (probably caused by shrinking the size of the window).
    1288                 :       // Nuke them.
    1289               0 :       nsIFrame* currFrame = startingPoint->GetNextSibling();
    1290               0 :       nsBoxLayoutState state(PresContext());
    1291                 : 
    1292                 :       nsCSSFrameConstructor* fc =
    1293               0 :         PresContext()->PresShell()->FrameConstructor();
    1294               0 :       fc->BeginUpdate();
    1295               0 :       while (currFrame) {
    1296               0 :         nsIFrame* nextFrame = currFrame->GetNextSibling();
    1297               0 :         RemoveChildFrame(state, currFrame);
    1298               0 :         currFrame = nextFrame;
    1299                 :       }
    1300               0 :       fc->EndUpdate();
    1301                 : 
    1302               0 :       PresContext()->PresShell()->
    1303                 :         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1304               0 :                          NS_FRAME_HAS_DIRTY_CHILDREN);
    1305                 :     }
    1306               0 :     return false;
    1307                 :   }
    1308                 :   else
    1309               0 :     return true;
    1310                 : }
    1311                 : 
    1312                 : NS_IMETHODIMP
    1313               0 : nsListBoxBodyFrame::ListBoxAppendFrames(nsFrameList& aFrameList)
    1314                 : {
    1315                 :   // append them after
    1316               0 :   nsBoxLayoutState state(PresContext());
    1317               0 :   const nsFrameList::Slice& newFrames = mFrames.AppendFrames(nsnull, aFrameList);
    1318               0 :   if (mLayoutManager)
    1319               0 :     mLayoutManager->ChildrenAppended(this, state, newFrames);
    1320               0 :   PresContext()->PresShell()->
    1321                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1322               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1323                 :   
    1324               0 :   return NS_OK;
    1325                 : }
    1326                 : 
    1327                 : NS_IMETHODIMP
    1328               0 : nsListBoxBodyFrame::ListBoxInsertFrames(nsIFrame* aPrevFrame,
    1329                 :                                         nsFrameList& aFrameList)
    1330                 : {
    1331                 :   // insert the frames to our info list
    1332               0 :   nsBoxLayoutState state(PresContext());
    1333                 :   const nsFrameList::Slice& newFrames =
    1334               0 :     mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
    1335               0 :   if (mLayoutManager)
    1336               0 :     mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
    1337               0 :   PresContext()->PresShell()->
    1338                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1339               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1340                 : 
    1341               0 :   return NS_OK;
    1342                 : }
    1343                 : 
    1344                 : // 
    1345                 : // Called by nsCSSFrameConstructor when a new listitem content is inserted.
    1346                 : //
    1347                 : void 
    1348               0 : nsListBoxBodyFrame::OnContentInserted(nsPresContext* aPresContext, nsIContent* aChildContent)
    1349                 : {
    1350               0 :   if (mRowCount >= 0)
    1351               0 :     ++mRowCount;
    1352                 : 
    1353                 :   // The RDF content builder will build content nodes such that they are all 
    1354                 :   // ready when OnContentInserted is first called, meaning the first call
    1355                 :   // to CreateRows will create all the frames, but OnContentInserted will
    1356                 :   // still be called again for each content node - so we need to make sure
    1357                 :   // that the frame for each content node hasn't already been created.
    1358               0 :   nsIFrame* childFrame = aChildContent->GetPrimaryFrame();
    1359               0 :   if (childFrame)
    1360               0 :     return;
    1361                 : 
    1362                 :   PRInt32 siblingIndex;
    1363               0 :   nsCOMPtr<nsIContent> nextSiblingContent;
    1364               0 :   GetListItemNextSibling(aChildContent, getter_AddRefs(nextSiblingContent), siblingIndex);
    1365                 :   
    1366                 :   // if we're inserting our item before the first visible content,
    1367                 :   // then we need to shift all rows down by one
    1368               0 :   if (siblingIndex >= 0 &&  siblingIndex-1 <= mCurrentIndex) {
    1369               0 :     mTopFrame = nsnull;
    1370               0 :     mRowsToPrepend = 1;
    1371               0 :   } else if (nextSiblingContent) {
    1372                 :     // we may be inserting before a frame that is on screen
    1373               0 :     nsIFrame* nextSiblingFrame = nextSiblingContent->GetPrimaryFrame();
    1374               0 :     mLinkupFrame = nextSiblingFrame;
    1375                 :   }
    1376                 :   
    1377               0 :   CreateRows();
    1378               0 :   PresContext()->PresShell()->
    1379                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1380               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1381                 : }
    1382                 : 
    1383                 : // 
    1384                 : // Called by nsCSSFrameConstructor when listitem content is removed.
    1385                 : //
    1386                 : void
    1387               0 : nsListBoxBodyFrame::OnContentRemoved(nsPresContext* aPresContext,
    1388                 :                                      nsIContent* aContainer,
    1389                 :                                      nsIFrame* aChildFrame,
    1390                 :                                      nsIContent* aOldNextSibling)
    1391                 : {
    1392               0 :   NS_ASSERTION(!aChildFrame || aChildFrame->GetParent() == this,
    1393                 :                "Removing frame that's not our child... Not good");
    1394                 :   
    1395               0 :   if (mRowCount >= 0)
    1396               0 :     --mRowCount;
    1397                 : 
    1398               0 :   if (aContainer) {
    1399               0 :     if (!aChildFrame) {
    1400                 :       // The row we are removing is out of view, so we need to try to
    1401                 :       // determine the index of its next sibling.
    1402               0 :       PRInt32 siblingIndex = -1;
    1403               0 :       if (aOldNextSibling) {
    1404               0 :         nsCOMPtr<nsIContent> nextSiblingContent;
    1405                 :         GetListItemNextSibling(aOldNextSibling,
    1406               0 :                                getter_AddRefs(nextSiblingContent),
    1407               0 :                                siblingIndex);
    1408                 :       }
    1409                 :     
    1410                 :       // if the row being removed is off-screen and above the top frame, we need to
    1411                 :       // adjust our top index and tell the scrollbar to shift up one row.
    1412               0 :       if (siblingIndex >= 0 && siblingIndex-1 < mCurrentIndex) {
    1413               0 :         NS_PRECONDITION(mCurrentIndex > 0, "mCurrentIndex > 0");
    1414               0 :         --mCurrentIndex;
    1415               0 :         mYPosition = mCurrentIndex*mRowHeight;
    1416               0 :         VerticalScroll(mYPosition);
    1417                 :       }
    1418               0 :     } else if (mCurrentIndex > 0) {
    1419                 :       // At this point, we know we have a scrollbar, and we need to know 
    1420                 :       // if we are scrolled to the last row.  In this case, the behavior
    1421                 :       // of the scrollbar is to stay locked to the bottom.  Since we are
    1422                 :       // removing visible content, the first visible row will have to move
    1423                 :       // down by one, and we will have to insert a new frame at the top.
    1424                 :       
    1425                 :       // if the last content node has a frame, we are scrolled to the bottom
    1426               0 :       ChildIterator iter, last;
    1427               0 :       ChildIterator::Init(mContent, &iter, &last);
    1428               0 :       if (iter != last) {
    1429               0 :         iter = last;
    1430               0 :         --iter;
    1431               0 :         nsIContent *lastChild = *iter;
    1432               0 :         nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
    1433                 :       
    1434               0 :         if (lastChildFrame) {
    1435               0 :           mTopFrame = nsnull;
    1436               0 :           mRowsToPrepend = 1;
    1437               0 :           --mCurrentIndex;
    1438               0 :           mYPosition = mCurrentIndex*mRowHeight;
    1439               0 :           VerticalScroll(mYPosition);
    1440                 :         }
    1441                 :       }
    1442                 :     }
    1443                 :   }
    1444                 : 
    1445                 :   // if we're removing the top row, the new top row is the next row
    1446               0 :   if (mTopFrame && mTopFrame == aChildFrame)
    1447               0 :     mTopFrame = mTopFrame->GetNextSibling();
    1448                 : 
    1449                 :   // Go ahead and delete the frame.
    1450               0 :   nsBoxLayoutState state(aPresContext);
    1451               0 :   if (aChildFrame) {
    1452               0 :     RemoveChildFrame(state, aChildFrame);
    1453                 :   }
    1454                 : 
    1455               0 :   PresContext()->PresShell()->
    1456                 :     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1457               0 :                      NS_FRAME_HAS_DIRTY_CHILDREN);
    1458               0 : }
    1459                 : 
    1460                 : void
    1461               0 : nsListBoxBodyFrame::GetListItemContentAt(PRInt32 aIndex, nsIContent** aContent)
    1462                 : {
    1463               0 :   *aContent = nsnull;
    1464                 : 
    1465               0 :   PRInt32 itemsFound = 0;
    1466               0 :   ChildIterator iter, last;
    1467               0 :   for (ChildIterator::Init(mContent, &iter, &last);
    1468                 :        iter != last;
    1469                 :        ++iter) {
    1470               0 :     nsIContent *kid = (*iter);
    1471               0 :     if (kid->Tag() == nsGkAtoms::listitem) {
    1472               0 :       ++itemsFound;
    1473               0 :       if (itemsFound-1 == aIndex) {
    1474               0 :         *aContent = kid;
    1475               0 :         NS_IF_ADDREF(*aContent);
    1476               0 :         return;
    1477                 :       }
    1478                 :     }
    1479                 :   }
    1480                 : }
    1481                 : 
    1482                 : void
    1483               0 : nsListBoxBodyFrame::GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, PRInt32& aSiblingIndex)
    1484                 : {
    1485               0 :   *aContent = nsnull;
    1486               0 :   aSiblingIndex = -1;
    1487               0 :   nsIContent *prevKid = nsnull;
    1488               0 :   ChildIterator iter, last;
    1489               0 :   for (ChildIterator::Init(mContent, &iter, &last);
    1490                 :        iter != last;
    1491                 :        ++iter) {
    1492               0 :     nsIContent *kid = (*iter);
    1493                 : 
    1494               0 :     if (kid->Tag() == nsGkAtoms::listitem) {
    1495               0 :       ++aSiblingIndex;
    1496               0 :       if (prevKid == aListItem) {
    1497               0 :         *aContent = kid;
    1498               0 :         NS_IF_ADDREF(*aContent);
    1499               0 :         return;
    1500                 :       }
    1501                 :     }
    1502               0 :     prevKid = kid;
    1503                 :   }
    1504                 : 
    1505               0 :   aSiblingIndex = -1; // no match, so there is no next sibling
    1506                 : }
    1507                 : 
    1508                 : void
    1509               0 : nsListBoxBodyFrame::RemoveChildFrame(nsBoxLayoutState &aState,
    1510                 :                                      nsIFrame         *aFrame)
    1511                 : {
    1512               0 :   if (!mFrames.ContainsFrame(aFrame)) {
    1513               0 :     NS_ERROR("tried to remove a child frame which isn't our child");
    1514               0 :     return;
    1515                 :   }
    1516                 : 
    1517               0 :   if (aFrame == GetContentInsertionFrame()) {
    1518                 :     // Don't touch that one
    1519               0 :     return;
    1520                 :   }
    1521                 : 
    1522                 : #ifdef ACCESSIBILITY
    1523               0 :   nsAccessibilityService* accService = nsIPresShell::AccService();
    1524               0 :   if (accService) {
    1525               0 :     nsIContent* content = aFrame->GetContent();
    1526                 :     accService->ContentRemoved(PresContext()->PresShell(), content->GetParent(),
    1527               0 :                                content);
    1528                 :   }
    1529                 : #endif
    1530                 : 
    1531               0 :   mFrames.RemoveFrame(aFrame);
    1532               0 :   if (mLayoutManager)
    1533               0 :     mLayoutManager->ChildrenRemoved(this, aState, aFrame);
    1534               0 :   aFrame->Destroy();
    1535                 : }
    1536                 : 
    1537                 : // Creation Routines ///////////////////////////////////////////////////////////////////////
    1538                 : 
    1539                 : already_AddRefed<nsBoxLayout> NS_NewListBoxLayout();
    1540                 : 
    1541                 : nsIFrame*
    1542               0 : NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    1543                 : {
    1544               0 :   nsCOMPtr<nsBoxLayout> layout = NS_NewListBoxLayout();
    1545               0 :   if (!layout) {
    1546               0 :     return nsnull;
    1547                 :   }
    1548                 : 
    1549               0 :   return new (aPresShell) nsListBoxBodyFrame(aPresShell, aContext, layout);
    1550                 : }
    1551                 : 
    1552               0 : NS_IMPL_FRAMEARENA_HELPERS(nsListBoxBodyFrame)

Generated by: LCOV version 1.7