layout/xul/nsListBoxBodyFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsListBoxBodyFrame.h"
     8 #include "nsListBoxLayout.h"
    10 #include "nsCOMPtr.h"
    11 #include "nsGridRowGroupLayout.h"
    12 #include "nsIServiceManager.h"
    13 #include "nsGkAtoms.h"
    14 #include "nsIContent.h"
    15 #include "nsNameSpaceManager.h"
    16 #include "nsIDocument.h"
    17 #include "nsIDOMMouseEvent.h"
    18 #include "nsIDOMElement.h"
    19 #include "nsIDOMNodeList.h"
    20 #include "nsCSSFrameConstructor.h"
    21 #include "nsIScrollableFrame.h"
    22 #include "nsScrollbarFrame.h"
    23 #include "nsView.h"
    24 #include "nsViewManager.h"
    25 #include "nsStyleContext.h"
    26 #include "nsFontMetrics.h"
    27 #include "nsITimer.h"
    28 #include "nsAutoPtr.h"
    29 #include "nsStyleSet.h"
    30 #include "nsPIBoxObject.h"
    31 #include "nsINodeInfo.h"
    32 #include "nsLayoutUtils.h"
    33 #include "nsPIListBoxObject.h"
    34 #include "nsContentUtils.h"
    35 #include "ChildIterator.h"
    36 #include "nsRenderingContext.h"
    37 #include "prtime.h"
    38 #include <algorithm>
    40 #ifdef ACCESSIBILITY
    41 #include "nsAccessibilityService.h"
    42 #endif
    44 using namespace mozilla::dom;
    46 /////////////// nsListScrollSmoother //////////////////
    48 /* A mediator used to smooth out scrolling. It works by seeing if 
    49  * we have time to scroll the amount of rows requested. This is determined
    50  * by measuring how long it takes to scroll a row. If we can scroll the 
    51  * rows in time we do so. If not we start a timer and skip the request. We
    52  * do this until the timer finally first because the user has stopped moving
    53  * the mouse. Then do all the queued requests in on shot.
    54  */
    56 // the longest amount of time that can go by before the use
    57 // notices it as a delay.
    58 #define USER_TIME_THRESHOLD 150000
    60 // how long it takes to layout a single row initial value.
    61 // we will time this after we scroll a few rows.
    62 #define TIME_PER_ROW_INITAL  50000
    64 // if we decide we can't layout the rows in the amount of time. How long
    65 // do we wait before checking again?
    66 #define SMOOTH_INTERVAL 100
    68 class nsListScrollSmoother : public nsITimerCallback
    69 {
    70 public:
    71   NS_DECL_ISUPPORTS
    73   nsListScrollSmoother(nsListBoxBodyFrame* aOuter);
    74   virtual ~nsListScrollSmoother();
    76   // nsITimerCallback
    77   NS_DECL_NSITIMERCALLBACK
    79   void Start();
    80   void Stop();
    81   bool IsRunning();
    83   nsCOMPtr<nsITimer> mRepeatTimer;
    84   int32_t mDelta;
    85   nsListBoxBodyFrame* mOuter;
    86 }; 
    88 nsListScrollSmoother::nsListScrollSmoother(nsListBoxBodyFrame* aOuter)
    89 {
    90   mDelta = 0;
    91   mOuter = aOuter;
    92 }
    94 nsListScrollSmoother::~nsListScrollSmoother()
    95 {
    96   Stop();
    97 }
    99 NS_IMETHODIMP
   100 nsListScrollSmoother::Notify(nsITimer *timer)
   101 {
   102   Stop();
   104   NS_ASSERTION(mOuter, "mOuter is null, see bug #68365");
   105   if (!mOuter) return NS_OK;
   107   // actually do some work.
   108   mOuter->InternalPositionChangedCallback();
   109   return NS_OK;
   110 }
   112 bool
   113 nsListScrollSmoother::IsRunning()
   114 {
   115   return mRepeatTimer ? true : false;
   116 }
   118 void
   119 nsListScrollSmoother::Start()
   120 {
   121   Stop();
   122   mRepeatTimer = do_CreateInstance("@mozilla.org/timer;1");
   123   mRepeatTimer->InitWithCallback(this, SMOOTH_INTERVAL, nsITimer::TYPE_ONE_SHOT);
   124 }
   126 void
   127 nsListScrollSmoother::Stop()
   128 {
   129   if ( mRepeatTimer ) {
   130     mRepeatTimer->Cancel();
   131     mRepeatTimer = nullptr;
   132   }
   133 }
   135 NS_IMPL_ISUPPORTS(nsListScrollSmoother, nsITimerCallback)
   137 /////////////// nsListBoxBodyFrame //////////////////
   139 nsListBoxBodyFrame::nsListBoxBodyFrame(nsIPresShell* aPresShell,
   140                                        nsStyleContext* aContext,
   141                                        nsBoxLayout* aLayoutManager)
   142   : nsBoxFrame(aPresShell, aContext, false, aLayoutManager),
   143     mTopFrame(nullptr),
   144     mBottomFrame(nullptr),
   145     mLinkupFrame(nullptr),
   146     mScrollSmoother(nullptr),
   147     mRowsToPrepend(0),
   148     mRowCount(-1),
   149     mRowHeight(0),
   150     mAvailableHeight(0),
   151     mStringWidth(-1),
   152     mCurrentIndex(0),
   153     mOldIndex(0),
   154     mYPosition(0),
   155     mTimePerRow(TIME_PER_ROW_INITAL),
   156     mRowHeightWasSet(false),
   157     mScrolling(false),
   158     mAdjustScroll(false),
   159     mReflowCallbackPosted(false)
   160 {
   161 }
   163 nsListBoxBodyFrame::~nsListBoxBodyFrame()
   164 {
   165   NS_IF_RELEASE(mScrollSmoother);
   167 #if USE_TIMER_TO_DELAY_SCROLLING
   168   StopScrollTracking();
   169   mAutoScrollTimer = nullptr;
   170 #endif
   172 }
   174 NS_QUERYFRAME_HEAD(nsListBoxBodyFrame)
   175   NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
   176   NS_QUERYFRAME_ENTRY(nsListBoxBodyFrame)
   177 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
   179 ////////// nsIFrame /////////////////
   181 void
   182 nsListBoxBodyFrame::Init(nsIContent*     aContent,
   183                          nsIFrame*       aParent, 
   184                          nsIFrame*       aPrevInFlow)
   185 {
   186   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
   187   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
   188   if (scrollFrame) {
   189     nsIFrame* verticalScrollbar = scrollFrame->GetScrollbarBox(true);
   190     nsScrollbarFrame* scrollbarFrame = do_QueryFrame(verticalScrollbar);
   191     if (scrollbarFrame) {
   192       scrollbarFrame->SetScrollbarMediatorContent(GetContent());
   193     }
   194   }
   195   nsRefPtr<nsFontMetrics> fm;
   196   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   197   mRowHeight = fm->MaxHeight();
   198 }
   200 void
   201 nsListBoxBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
   202 {
   203   // make sure we cancel any posted callbacks.
   204   if (mReflowCallbackPosted)
   205      PresContext()->PresShell()->CancelReflowCallback(this);
   207   // Revoke any pending position changed events
   208   for (uint32_t i = 0; i < mPendingPositionChangeEvents.Length(); ++i) {
   209     mPendingPositionChangeEvents[i]->Revoke();
   210   }
   212   // Make sure we tell our listbox's box object we're being destroyed.
   213   if (mBoxObject) {
   214     mBoxObject->ClearCachedValues();
   215   }
   217   nsBoxFrame::DestroyFrom(aDestructRoot);
   218 }
   220 nsresult
   221 nsListBoxBodyFrame::AttributeChanged(int32_t aNameSpaceID,
   222                                      nsIAtom* aAttribute, 
   223                                      int32_t aModType)
   224 {
   225   nsresult rv = NS_OK;
   227   if (aAttribute == nsGkAtoms::rows) {
   228     PresContext()->PresShell()->
   229       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   230   }
   231   else
   232     rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
   234   return rv;
   236 }
   238 /* virtual */ void
   239 nsListBoxBodyFrame::MarkIntrinsicWidthsDirty()
   240 {
   241   mStringWidth = -1;
   242   nsBoxFrame::MarkIntrinsicWidthsDirty();
   243 }
   245 /////////// nsBox ///////////////
   247 NS_IMETHODIMP
   248 nsListBoxBodyFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
   249 {
   250   if (mScrolling)
   251     aBoxLayoutState.SetPaintingDisabled(true);
   253   nsresult rv = nsBoxFrame::DoLayout(aBoxLayoutState);
   255   // determine the real height for the scrollable area from the total number
   256   // of rows, since non-visible rows don't yet have frames
   257   nsRect rect(nsPoint(0, 0), GetSize());
   258   nsOverflowAreas overflow(rect, rect);
   259   if (mLayoutManager) {
   260     nsIFrame* childFrame = mFrames.FirstChild();
   261     while (childFrame) {
   262       ConsiderChildOverflow(overflow, childFrame);
   263       childFrame = childFrame->GetNextSibling();
   264     }
   266     nsSize prefSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
   267     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
   268       nsRect& o = overflow.Overflow(otype);
   269       o.height = std::max(o.height, prefSize.height);
   270     }
   271   }
   272   FinishAndStoreOverflow(overflow, GetSize());
   274   if (mScrolling)
   275     aBoxLayoutState.SetPaintingDisabled(false);
   277   // if we are scrolled and the row height changed
   278   // make sure we are scrolled to a correct index.
   279   if (mAdjustScroll)
   280      PostReflowCallback();
   282   return rv;
   283 }
   285 nsSize
   286 nsListBoxBodyFrame::GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState)
   287 {
   288   nsSize result(0, 0);
   289   if (nsContentUtils::HasNonEmptyAttr(GetContent(), kNameSpaceID_None,
   290                                       nsGkAtoms::sizemode)) {
   291     result = GetPrefSize(aBoxLayoutState);
   292     result.height = 0;
   293     nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
   294     if (scrollFrame &&
   295         scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
   296       nsMargin scrollbars =
   297         scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
   298       result.width += scrollbars.left + scrollbars.right;
   299     }
   300   }
   301   return result;
   302 }
   304 nsSize
   305 nsListBoxBodyFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
   306 {  
   307   nsSize pref = nsBoxFrame::GetPrefSize(aBoxLayoutState);
   309   int32_t size = GetFixedRowSize();
   310   if (size > -1)
   311     pref.height = size*GetRowHeightAppUnits();
   313   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
   314   if (scrollFrame &&
   315       scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
   316     nsMargin scrollbars = scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
   317     pref.width += scrollbars.left + scrollbars.right;
   318   }
   319   return pref;
   320 }
   322 ///////////// nsIScrollbarMediator ///////////////
   324 NS_IMETHODIMP
   325 nsListBoxBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t& aNewIndex)
   326 { 
   327   if (mScrolling || mRowHeight == 0)
   328     return NS_OK;
   330   nscoord oldTwipIndex, newTwipIndex;
   331   oldTwipIndex = mCurrentIndex*mRowHeight;
   332   newTwipIndex = nsPresContext::CSSPixelsToAppUnits(aNewIndex);
   333   int32_t twipDelta = newTwipIndex > oldTwipIndex ? newTwipIndex - oldTwipIndex : oldTwipIndex - newTwipIndex;
   335   int32_t rowDelta = twipDelta / mRowHeight;
   336   int32_t remainder = twipDelta % mRowHeight;
   337   if (remainder > (mRowHeight/2))
   338     rowDelta++;
   340   if (rowDelta == 0)
   341     return NS_OK;
   343   // update the position to be row based.
   345   int32_t newIndex = newTwipIndex > oldTwipIndex ? mCurrentIndex + rowDelta : mCurrentIndex - rowDelta;
   346   //aNewIndex = newIndex*mRowHeight/mOnePixel;
   348   nsListScrollSmoother* smoother = GetSmoother();
   350   // if we can't scroll the rows in time then start a timer. We will eat
   351   // events until the user stops moving and the timer stops.
   352   if (smoother->IsRunning() || rowDelta*mTimePerRow > USER_TIME_THRESHOLD) {
   354      smoother->Stop();
   356      smoother->mDelta = newTwipIndex > oldTwipIndex ? rowDelta : -rowDelta;
   358      smoother->Start();
   360      return NS_OK;
   361   }
   363   smoother->Stop();
   365   mCurrentIndex = newIndex;
   366   smoother->mDelta = 0;
   368   if (mCurrentIndex < 0) {
   369     mCurrentIndex = 0;
   370     return NS_OK;
   371   }
   373   return InternalPositionChanged(newTwipIndex < oldTwipIndex, rowDelta);
   374 }
   376 NS_IMETHODIMP
   377 nsListBoxBodyFrame::VisibilityChanged(bool aVisible)
   378 {
   379   if (mRowHeight == 0)
   380     return NS_OK;
   382   int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
   383   if (lastPageTopRow < 0)
   384     lastPageTopRow = 0;
   385   int32_t delta = mCurrentIndex - lastPageTopRow;
   386   if (delta > 0) {
   387     mCurrentIndex = lastPageTopRow;
   388     InternalPositionChanged(true, delta);
   389   }
   391   return NS_OK;
   392 }
   394 NS_IMETHODIMP
   395 nsListBoxBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t aNewIndex)
   396 {
   397   if (aOldIndex == aNewIndex)
   398     return NS_OK;
   399   if (aNewIndex < aOldIndex)
   400     mCurrentIndex--;
   401   else mCurrentIndex++;
   402   if (mCurrentIndex < 0) {
   403     mCurrentIndex = 0;
   404     return NS_OK;
   405   }
   406   InternalPositionChanged(aNewIndex < aOldIndex, 1);
   408   return NS_OK;
   409 }
   411 ///////////// nsIReflowCallback ///////////////
   413 bool
   414 nsListBoxBodyFrame::ReflowFinished()
   415 {
   416   nsAutoScriptBlocker scriptBlocker;
   417   // now create or destroy any rows as needed
   418   CreateRows();
   420   // keep scrollbar in sync
   421   if (mAdjustScroll) {
   422      VerticalScroll(mYPosition);
   423      mAdjustScroll = false;
   424   }
   426   // if the row height changed then mark everything as a style change. 
   427   // That will dirty the entire listbox
   428   if (mRowHeightWasSet) {
   429     PresContext()->PresShell()->
   430       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   431      int32_t pos = mCurrentIndex * mRowHeight;
   432      if (mYPosition != pos) 
   433        mAdjustScroll = true;
   434     mRowHeightWasSet = false;
   435   }
   437   mReflowCallbackPosted = false;
   438   return true;
   439 }
   441 void
   442 nsListBoxBodyFrame::ReflowCallbackCanceled()
   443 {
   444   mReflowCallbackPosted = false;
   445 }
   447 ///////// nsIListBoxObject ///////////////
   449 nsresult
   450 nsListBoxBodyFrame::GetRowCount(int32_t* aResult)
   451 {
   452   *aResult = GetRowCount();
   453   return NS_OK;
   454 }
   456 nsresult
   457 nsListBoxBodyFrame::GetNumberOfVisibleRows(int32_t *aResult)
   458 {
   459   *aResult= mRowHeight ? GetAvailableHeight() / mRowHeight : 0;
   460   return NS_OK;
   461 }
   463 nsresult
   464 nsListBoxBodyFrame::GetIndexOfFirstVisibleRow(int32_t *aResult)
   465 {
   466   *aResult = mCurrentIndex;
   467   return NS_OK;
   468 }
   470 nsresult
   471 nsListBoxBodyFrame::EnsureIndexIsVisible(int32_t aRowIndex)
   472 {
   473   if (aRowIndex < 0)
   474     return NS_ERROR_ILLEGAL_VALUE;
   476   int32_t rows = 0;
   477   if (mRowHeight)
   478     rows = GetAvailableHeight()/mRowHeight;
   479   if (rows <= 0)
   480     rows = 1;
   481   int32_t bottomIndex = mCurrentIndex + rows;
   483   // if row is visible, ignore
   484   if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex)
   485     return NS_OK;
   487   int32_t delta;
   489   bool up = aRowIndex < mCurrentIndex;
   490   if (up) {
   491     delta = mCurrentIndex - aRowIndex;
   492     mCurrentIndex = aRowIndex;
   493   }
   494   else {
   495     // Check to be sure we're not scrolling off the bottom of the tree
   496     if (aRowIndex >= GetRowCount())
   497       return NS_ERROR_ILLEGAL_VALUE;
   499     // Bring it just into view.
   500     delta = 1 + (aRowIndex-bottomIndex);
   501     mCurrentIndex += delta; 
   502   }
   504   // Safe to not go off an event here, since this is coming from the
   505   // box object.
   506   DoInternalPositionChangedSync(up, delta);
   507   return NS_OK;
   508 }
   510 nsresult
   511 nsListBoxBodyFrame::ScrollByLines(int32_t aNumLines)
   512 {
   513   int32_t scrollIndex, visibleRows;
   514   GetIndexOfFirstVisibleRow(&scrollIndex);
   515   GetNumberOfVisibleRows(&visibleRows);
   517   scrollIndex += aNumLines;
   519   if (scrollIndex < 0)
   520     scrollIndex = 0;
   521   else {
   522     int32_t numRows = GetRowCount();
   523     int32_t lastPageTopRow = numRows - visibleRows;
   524     if (scrollIndex > lastPageTopRow)
   525       scrollIndex = lastPageTopRow;
   526   }
   528   ScrollToIndex(scrollIndex);
   530   return NS_OK;
   531 }
   533 // walks the DOM to get the zero-based row index of the content
   534 nsresult
   535 nsListBoxBodyFrame::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
   536 {
   537   if (aItem) {
   538     *_retval = 0;
   539     nsCOMPtr<nsIContent> itemContent(do_QueryInterface(aItem));
   541     FlattenedChildIterator iter(mContent);
   542     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   543       // we hit a list row, count it
   544       if (child->Tag() == nsGkAtoms::listitem) {
   545         // is this it?
   546         if (child == itemContent)
   547           return NS_OK;
   549         ++(*_retval);
   550       }
   551     }
   552   }
   554   // not found
   555   *_retval = -1;
   556   return NS_OK;
   557 }
   559 nsresult
   560 nsListBoxBodyFrame::GetItemAtIndex(int32_t aIndex, nsIDOMElement** aItem)
   561 {
   562   *aItem = nullptr;
   563   if (aIndex < 0)
   564     return NS_OK;
   566   int32_t itemCount = 0;
   567   FlattenedChildIterator iter(mContent);
   568   for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   569     // we hit a list row, check if it is the one we are looking for
   570     if (child->Tag() == nsGkAtoms::listitem) {
   571       // is this it?
   572       if (itemCount == aIndex) {
   573         return CallQueryInterface(child, aItem);
   574       }
   575       ++itemCount;
   576     }
   577   }
   579   // not found
   580   return NS_OK;
   581 }
   583 /////////// nsListBoxBodyFrame ///////////////
   585 int32_t
   586 nsListBoxBodyFrame::GetRowCount()
   587 {
   588   if (mRowCount < 0)
   589     ComputeTotalRowCount();
   590   return mRowCount;
   591 }
   593 int32_t
   594 nsListBoxBodyFrame::GetFixedRowSize()
   595 {
   596   nsresult dummy;
   598   nsAutoString rows;
   599   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
   600   if (!rows.IsEmpty())
   601     return rows.ToInteger(&dummy);
   603   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::size, rows);
   605   if (!rows.IsEmpty())
   606     return rows.ToInteger(&dummy);
   608   return -1;
   609 }
   611 void
   612 nsListBoxBodyFrame::SetRowHeight(nscoord aRowHeight)
   613 { 
   614   if (aRowHeight > mRowHeight) { 
   615     mRowHeight = aRowHeight;
   617     // signal we need to dirty everything 
   618     // and we want to be notified after reflow
   619     // so we can create or destory rows as needed
   620     mRowHeightWasSet = true;
   621     PostReflowCallback();
   622   }
   623 }
   625 nscoord
   626 nsListBoxBodyFrame::GetAvailableHeight()
   627 {
   628   nsIScrollableFrame* scrollFrame =
   629     nsLayoutUtils::GetScrollableFrameFor(this);
   630   if (scrollFrame) {
   631     return scrollFrame->GetScrollPortRect().height;
   632   }
   633   return 0;
   634 }
   636 nscoord
   637 nsListBoxBodyFrame::GetYPosition()
   638 {
   639   return mYPosition;
   640 }
   642 nscoord
   643 nsListBoxBodyFrame::ComputeIntrinsicWidth(nsBoxLayoutState& aBoxLayoutState)
   644 {
   645   if (mStringWidth != -1)
   646     return mStringWidth;
   648   nscoord largestWidth = 0;
   650   int32_t index = 0;
   651   nsCOMPtr<nsIDOMElement> firstRowEl;
   652   GetItemAtIndex(index, getter_AddRefs(firstRowEl));
   653   nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl));
   655   if (firstRowContent) {
   656     nsRefPtr<nsStyleContext> styleContext;
   657     nsPresContext *presContext = aBoxLayoutState.PresContext();
   658     styleContext = presContext->StyleSet()->
   659       ResolveStyleFor(firstRowContent->AsElement(), nullptr);
   661     nscoord width = 0;
   662     nsMargin margin(0,0,0,0);
   664     if (styleContext->StylePadding()->GetPadding(margin))
   665       width += margin.LeftRight();
   666     width += styleContext->StyleBorder()->GetComputedBorder().LeftRight();
   667     if (styleContext->StyleMargin()->GetMargin(margin))
   668       width += margin.LeftRight();
   670     FlattenedChildIterator iter(mContent);
   671     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   672       if (child->Tag() == nsGkAtoms::listitem) {
   673         nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
   674         if (rendContext) {
   675           nsAutoString value;
   676           uint32_t textCount = child->GetChildCount();
   677           for (uint32_t j = 0; j < textCount; ++j) {
   678             nsIContent* text = child->GetChildAt(j);
   679             if (text && text->IsNodeOfType(nsINode::eTEXT)) {
   680               text->AppendTextTo(value);
   681             }
   682           }
   684           nsRefPtr<nsFontMetrics> fm;
   685           nsLayoutUtils::GetFontMetricsForStyleContext(styleContext,
   686                                                        getter_AddRefs(fm));
   687           rendContext->SetFont(fm);
   689           nscoord textWidth =
   690             nsLayoutUtils::GetStringWidth(this, rendContext, value.get(), value.Length());
   691           textWidth += width;
   693           if (textWidth > largestWidth) 
   694             largestWidth = textWidth;
   695         }
   696       }
   697     }
   698   }
   700   mStringWidth = largestWidth;
   701   return mStringWidth;
   702 }
   704 void
   705 nsListBoxBodyFrame::ComputeTotalRowCount()
   706 {
   707   mRowCount = 0;
   708   FlattenedChildIterator iter(mContent);
   709   for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   710     if (child->Tag() == nsGkAtoms::listitem) {
   711       ++mRowCount;
   712     }
   713   }
   714 }
   716 void
   717 nsListBoxBodyFrame::PostReflowCallback()
   718 {
   719   if (!mReflowCallbackPosted) {
   720     mReflowCallbackPosted = true;
   721     PresContext()->PresShell()->PostReflowCallback(this);
   722   }
   723 }
   725 ////////// scrolling
   727 nsresult
   728 nsListBoxBodyFrame::ScrollToIndex(int32_t aRowIndex)
   729 {
   730   if (( aRowIndex < 0 ) || (mRowHeight == 0))
   731     return NS_OK;
   733   int32_t newIndex = aRowIndex;
   734   int32_t delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex;
   735   bool up = newIndex < mCurrentIndex;
   737   // Check to be sure we're not scrolling off the bottom of the tree
   738   int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
   739   if (lastPageTopRow < 0)
   740     lastPageTopRow = 0;
   742   if (aRowIndex > lastPageTopRow)
   743     return NS_OK;
   745   mCurrentIndex = newIndex;
   747   nsWeakFrame weak(this);
   749   // Since we're going to flush anyway, we need to not do this off an event
   750   DoInternalPositionChangedSync(up, delta);
   752   if (!weak.IsAlive()) {
   753     return NS_OK;
   754   }
   756   // This change has to happen immediately.
   757   // Flush any pending reflow commands.
   758   // XXXbz why, exactly?
   759   mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
   761   return NS_OK;
   762 }
   764 nsresult
   765 nsListBoxBodyFrame::InternalPositionChangedCallback()
   766 {
   767   nsListScrollSmoother* smoother = GetSmoother();
   769   if (smoother->mDelta == 0)
   770     return NS_OK;
   772   mCurrentIndex += smoother->mDelta;
   774   if (mCurrentIndex < 0)
   775     mCurrentIndex = 0;
   777   return DoInternalPositionChangedSync(smoother->mDelta < 0,
   778                                        smoother->mDelta < 0 ?
   779                                          -smoother->mDelta : smoother->mDelta);
   780 }
   782 nsresult
   783 nsListBoxBodyFrame::InternalPositionChanged(bool aUp, int32_t aDelta)
   784 {
   785   nsRefPtr<nsPositionChangedEvent> ev =
   786     new nsPositionChangedEvent(this, aUp, aDelta);
   787   nsresult rv = NS_DispatchToCurrentThread(ev);
   788   if (NS_SUCCEEDED(rv)) {
   789     if (!mPendingPositionChangeEvents.AppendElement(ev)) {
   790       rv = NS_ERROR_OUT_OF_MEMORY;
   791       ev->Revoke();
   792     }
   793   }
   794   return rv;
   795 }
   797 nsresult
   798 nsListBoxBodyFrame::DoInternalPositionChangedSync(bool aUp, int32_t aDelta)
   799 {
   800   nsWeakFrame weak(this);
   802   // Process all the pending position changes first
   803   nsTArray< nsRefPtr<nsPositionChangedEvent> > temp;
   804   temp.SwapElements(mPendingPositionChangeEvents);
   805   for (uint32_t i = 0; i < temp.Length(); ++i) {
   806     if (weak.IsAlive()) {
   807       temp[i]->Run();
   808     }
   809     temp[i]->Revoke();
   810   }
   812   if (!weak.IsAlive()) {
   813     return NS_OK;
   814   }
   816   return DoInternalPositionChanged(aUp, aDelta);
   817 }
   819 nsresult
   820 nsListBoxBodyFrame::DoInternalPositionChanged(bool aUp, int32_t aDelta)
   821 {
   822   if (aDelta == 0)
   823     return NS_OK;
   825   nsRefPtr<nsPresContext> presContext(PresContext());
   826   nsBoxLayoutState state(presContext);
   828   // begin timing how long it takes to scroll a row
   829   PRTime start = PR_Now();
   831   nsWeakFrame weakThis(this);
   832   mContent->GetDocument()->FlushPendingNotifications(Flush_Layout);
   833   if (!weakThis.IsAlive()) {
   834     return NS_OK;
   835   }
   837   {
   838     nsAutoScriptBlocker scriptBlocker;
   840     int32_t visibleRows = 0;
   841     if (mRowHeight)
   842       visibleRows = GetAvailableHeight()/mRowHeight;
   844     if (aDelta < visibleRows) {
   845       int32_t loseRows = aDelta;
   846       if (aUp) {
   847         // scrolling up, destroy rows from the bottom downwards
   848         ReverseDestroyRows(loseRows);
   849         mRowsToPrepend += aDelta;
   850         mLinkupFrame = nullptr;
   851       }
   852       else {
   853         // scrolling down, destroy rows from the top upwards
   854         DestroyRows(loseRows);
   855         mRowsToPrepend = 0;
   856       }
   857     }
   858     else {
   859       // We have scrolled so much that all of our current frames will
   860       // go off screen, so blow them all away. Weeee!
   861       nsIFrame *currBox = mFrames.FirstChild();
   862       nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
   863       fc->BeginUpdate();
   864       while (currBox) {
   865         nsIFrame *nextBox = currBox->GetNextSibling();
   866         RemoveChildFrame(state, currBox);
   867         currBox = nextBox;
   868       }
   869       fc->EndUpdate();
   870     }
   872     // clear frame markers so that CreateRows will re-create
   873     mTopFrame = mBottomFrame = nullptr; 
   875     mYPosition = mCurrentIndex*mRowHeight;
   876     mScrolling = true;
   877     presContext->PresShell()->
   878       FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
   879   }
   880   if (!weakThis.IsAlive()) {
   881     return NS_OK;
   882   }
   883   // Flush calls CreateRows
   884   // XXXbz there has to be a better way to do this than flushing!
   885   presContext->PresShell()->FlushPendingNotifications(Flush_Layout);
   886   if (!weakThis.IsAlive()) {
   887     return NS_OK;
   888   }
   890   mScrolling = false;
   892   VerticalScroll(mYPosition);
   894   PRTime end = PR_Now();
   896   int32_t newTime = int32_t(end - start) / aDelta;
   898   // average old and new
   899   mTimePerRow = (newTime + mTimePerRow)/2;
   901   return NS_OK;
   902 }
   904 nsListScrollSmoother* 
   905 nsListBoxBodyFrame::GetSmoother()
   906 {
   907   if (!mScrollSmoother) {
   908     mScrollSmoother = new nsListScrollSmoother(this);
   909     NS_ASSERTION(mScrollSmoother, "out of memory");
   910     NS_IF_ADDREF(mScrollSmoother);
   911   }
   913   return mScrollSmoother;
   914 }
   916 void
   917 nsListBoxBodyFrame::VerticalScroll(int32_t aPosition)
   918 {
   919   nsIScrollableFrame* scrollFrame
   920     = nsLayoutUtils::GetScrollableFrameFor(this);
   921   if (!scrollFrame) {
   922     return;
   923   }
   925   nsPoint scrollPosition = scrollFrame->GetScrollPosition();
   927   nsWeakFrame weakFrame(this);
   928   scrollFrame->ScrollTo(nsPoint(scrollPosition.x, aPosition),
   929                         nsIScrollableFrame::INSTANT);
   930   if (!weakFrame.IsAlive()) {
   931     return;
   932   }
   934   mYPosition = aPosition;
   935 }
   937 ////////// frame and box retrieval
   939 nsIFrame*
   940 nsListBoxBodyFrame::GetFirstFrame()
   941 {
   942   mTopFrame = mFrames.FirstChild();
   943   return mTopFrame;
   944 }
   946 nsIFrame*
   947 nsListBoxBodyFrame::GetLastFrame()
   948 {
   949   return mFrames.LastChild();
   950 }
   952 bool
   953 nsListBoxBodyFrame::SupportsOrdinalsInChildren()
   954 {
   955   return false;
   956 }
   958 ////////// lazy row creation and destruction
   960 void
   961 nsListBoxBodyFrame::CreateRows()
   962 {
   963   // Get our client rect.
   964   nsRect clientRect;
   965   GetClientRect(clientRect);
   967   // Get the starting y position and the remaining available
   968   // height.
   969   nscoord availableHeight = GetAvailableHeight();
   971   if (availableHeight <= 0) {
   972     bool fixed = (GetFixedRowSize() != -1);
   973     if (fixed)
   974       availableHeight = 10;
   975     else
   976       return;
   977   }
   979   // get the first tree box. If there isn't one create one.
   980   bool created = false;
   981   nsIFrame* box = GetFirstItemBox(0, &created);
   982   nscoord rowHeight = GetRowHeightAppUnits();
   983   while (box) {  
   984     if (created && mRowsToPrepend > 0)
   985       --mRowsToPrepend;
   987     // if the row height is 0 then fail. Wait until someone 
   988     // laid out and sets the row height.
   989     if (rowHeight == 0)
   990         return;
   992     availableHeight -= rowHeight;
   994     // should we continue? Is the enought height?
   995     if (!ContinueReflow(availableHeight))
   996       break;
   998     // get the next tree box. Create one if needed.
   999     box = GetNextItemBox(box, 0, &created);
  1002   mRowsToPrepend = 0;
  1003   mLinkupFrame = nullptr;
  1006 void
  1007 nsListBoxBodyFrame::DestroyRows(int32_t& aRowsToLose) 
  1009   // We need to destroy frames until our row count has been properly
  1010   // reduced.  A reflow will then pick up and create the new frames.
  1011   nsIFrame* childFrame = GetFirstFrame();
  1012   nsBoxLayoutState state(PresContext());
  1014   nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
  1015   fc->BeginUpdate();
  1016   while (childFrame && aRowsToLose > 0) {
  1017     --aRowsToLose;
  1019     nsIFrame* nextFrame = childFrame->GetNextSibling();
  1020     RemoveChildFrame(state, childFrame);
  1022     mTopFrame = childFrame = nextFrame;
  1024   fc->EndUpdate();
  1026   PresContext()->PresShell()->
  1027     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1028                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1031 void
  1032 nsListBoxBodyFrame::ReverseDestroyRows(int32_t& aRowsToLose) 
  1034   // We need to destroy frames until our row count has been properly
  1035   // reduced.  A reflow will then pick up and create the new frames.
  1036   nsIFrame* childFrame = GetLastFrame();
  1037   nsBoxLayoutState state(PresContext());
  1039   nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
  1040   fc->BeginUpdate();
  1041   while (childFrame && aRowsToLose > 0) {
  1042     --aRowsToLose;
  1044     nsIFrame* prevFrame;
  1045     prevFrame = childFrame->GetPrevSibling();
  1046     RemoveChildFrame(state, childFrame);
  1048     mBottomFrame = childFrame = prevFrame;
  1050   fc->EndUpdate();
  1052   PresContext()->PresShell()->
  1053     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1054                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1057 static bool
  1058 IsListItemChild(nsListBoxBodyFrame* aParent, nsIContent* aChild,
  1059                 nsIFrame** aChildFrame)
  1061   *aChildFrame = nullptr;
  1062   if (!aChild->IsXUL() || aChild->Tag() != nsGkAtoms::listitem) {
  1063     return false;
  1065   nsIFrame* existingFrame = aChild->GetPrimaryFrame();
  1066   if (existingFrame && existingFrame->GetParent() != aParent) {
  1067     return false;
  1069   *aChildFrame = existingFrame;
  1070   return true;
  1073 //
  1074 // Get the nsIFrame for the first visible listitem, and if none exists,
  1075 // create one.
  1076 //
  1077 nsIFrame*
  1078 nsListBoxBodyFrame::GetFirstItemBox(int32_t aOffset, bool* aCreated)
  1080   if (aCreated)
  1081    *aCreated = false;
  1083   // Clear ourselves out.
  1084   mBottomFrame = mTopFrame;
  1086   if (mTopFrame) {
  1087     return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
  1090   // top frame was cleared out
  1091   mTopFrame = GetFirstFrame();
  1092   mBottomFrame = mTopFrame;
  1094   if (mTopFrame && mRowsToPrepend <= 0) {
  1095     return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
  1098   // At this point, we either have no frames at all, 
  1099   // or the user has scrolled upwards, leaving frames
  1100   // to be created at the top.  Let's determine which
  1101   // content needs a new frame first.
  1103   nsCOMPtr<nsIContent> startContent;
  1104   if (mTopFrame && mRowsToPrepend > 0) {
  1105     // We need to insert rows before the top frame
  1106     nsIContent* topContent = mTopFrame->GetContent();
  1107     nsIContent* topParent = topContent->GetParent();
  1108     int32_t contentIndex = topParent->IndexOf(topContent);
  1109     contentIndex -= aOffset;
  1110     if (contentIndex < 0)
  1111       return nullptr;
  1112     startContent = topParent->GetChildAt(contentIndex - mRowsToPrepend);
  1113   } else {
  1114     // This will be the first item frame we create.  Use the content
  1115     // at the current index, which is the first index scrolled into view
  1116     GetListItemContentAt(mCurrentIndex+aOffset, getter_AddRefs(startContent));
  1119   if (startContent) {  
  1120     nsIFrame* existingFrame;
  1121     if (!IsListItemChild(this, startContent, &existingFrame)) {
  1122       return GetFirstItemBox(++aOffset, aCreated);
  1124     if (existingFrame) {
  1125       return existingFrame->IsBoxFrame() ? existingFrame : nullptr;
  1128     // Either append the new frame, or prepend it (at index 0)
  1129     // XXX check here if frame was even created, it may not have been if
  1130     //     display: none was on listitem content
  1131     bool isAppend = mRowsToPrepend <= 0;
  1133     nsPresContext* presContext = PresContext();
  1134     nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
  1135     nsIFrame* topFrame = nullptr;
  1136     fc->CreateListBoxContent(presContext, this, nullptr, startContent,
  1137                              &topFrame, isAppend, false, nullptr);
  1138     mTopFrame = topFrame;
  1139     if (mTopFrame) {
  1140       if (aCreated)
  1141         *aCreated = true;
  1143       mBottomFrame = mTopFrame;
  1145       return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
  1146     } else
  1147       return GetFirstItemBox(++aOffset, 0);
  1150   return nullptr;
  1153 //
  1154 // Get the nsIFrame for the next visible listitem after aBox, and if none
  1155 // exists, create one.
  1156 //
  1157 nsIFrame*
  1158 nsListBoxBodyFrame::GetNextItemBox(nsIFrame* aBox, int32_t aOffset,
  1159                                    bool* aCreated)
  1161   if (aCreated)
  1162     *aCreated = false;
  1164   nsIFrame* result = aBox->GetNextSibling();
  1166   if (!result || result == mLinkupFrame || mRowsToPrepend > 0) {
  1167     // No result found. See if there's a content node that wants a frame.
  1168     nsIContent* prevContent = aBox->GetContent();
  1169     nsIContent* parentContent = prevContent->GetParent();
  1171     int32_t i = parentContent->IndexOf(prevContent);
  1173     uint32_t childCount = parentContent->GetChildCount();
  1174     if (((uint32_t)i + aOffset + 1) < childCount) {
  1175       // There is a content node that wants a frame.
  1176       nsIContent *nextContent = parentContent->GetChildAt(i + aOffset + 1);
  1178       nsIFrame* existingFrame;
  1179       if (!IsListItemChild(this, nextContent, &existingFrame)) {
  1180         return GetNextItemBox(aBox, ++aOffset, aCreated);
  1182       if (!existingFrame) {
  1183         // Either append the new frame, or insert it after the current frame
  1184         bool isAppend = result != mLinkupFrame && mRowsToPrepend <= 0;
  1185         nsIFrame* prevFrame = isAppend ? nullptr : aBox;
  1187         nsPresContext* presContext = PresContext();
  1188         nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
  1189         fc->CreateListBoxContent(presContext, this, prevFrame, nextContent,
  1190                                  &result, isAppend, false, nullptr);
  1192         if (result) {
  1193           if (aCreated)
  1194             *aCreated = true;
  1195         } else
  1196           return GetNextItemBox(aBox, ++aOffset, aCreated);
  1197       } else {
  1198         result = existingFrame;
  1201       mLinkupFrame = nullptr;
  1205   if (!result)
  1206     return nullptr;
  1208   mBottomFrame = result;
  1210   NS_ASSERTION(!result->IsBoxFrame() || result->GetParent() == this,
  1211                "returning frame that is not in childlist");
  1213   return result->IsBoxFrame() ? result : nullptr;
  1216 bool
  1217 nsListBoxBodyFrame::ContinueReflow(nscoord height) 
  1219 #ifdef ACCESSIBILITY
  1220   if (nsIPresShell::IsAccessibilityActive()) {
  1221     // Create all the frames at once so screen readers and
  1222     // onscreen keyboards can see the full list right away
  1223     return true;
  1225 #endif
  1227   if (height <= 0) {
  1228     nsIFrame* lastChild = GetLastFrame();
  1229     nsIFrame* startingPoint = mBottomFrame;
  1230     if (startingPoint == nullptr) {
  1231       // We just want to delete everything but the first item.
  1232       startingPoint = GetFirstFrame();
  1235     if (lastChild != startingPoint) {
  1236       // We have some hangers on (probably caused by shrinking the size of the window).
  1237       // Nuke them.
  1238       nsIFrame* currFrame = startingPoint->GetNextSibling();
  1239       nsBoxLayoutState state(PresContext());
  1241       nsCSSFrameConstructor* fc =
  1242         PresContext()->PresShell()->FrameConstructor();
  1243       fc->BeginUpdate();
  1244       while (currFrame) {
  1245         nsIFrame* nextFrame = currFrame->GetNextSibling();
  1246         RemoveChildFrame(state, currFrame);
  1247         currFrame = nextFrame;
  1249       fc->EndUpdate();
  1251       PresContext()->PresShell()->
  1252         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1253                          NS_FRAME_HAS_DIRTY_CHILDREN);
  1255     return false;
  1257   else
  1258     return true;
  1261 NS_IMETHODIMP
  1262 nsListBoxBodyFrame::ListBoxAppendFrames(nsFrameList& aFrameList)
  1264   // append them after
  1265   nsBoxLayoutState state(PresContext());
  1266   const nsFrameList::Slice& newFrames = mFrames.AppendFrames(nullptr, aFrameList);
  1267   if (mLayoutManager)
  1268     mLayoutManager->ChildrenAppended(this, state, newFrames);
  1269   PresContext()->PresShell()->
  1270     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1271                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1273   return NS_OK;
  1276 NS_IMETHODIMP
  1277 nsListBoxBodyFrame::ListBoxInsertFrames(nsIFrame* aPrevFrame,
  1278                                         nsFrameList& aFrameList)
  1280   // insert the frames to our info list
  1281   nsBoxLayoutState state(PresContext());
  1282   const nsFrameList::Slice& newFrames =
  1283     mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
  1284   if (mLayoutManager)
  1285     mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
  1286   PresContext()->PresShell()->
  1287     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1288                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1290   return NS_OK;
  1293 // 
  1294 // Called by nsCSSFrameConstructor when a new listitem content is inserted.
  1295 //
  1296 void 
  1297 nsListBoxBodyFrame::OnContentInserted(nsPresContext* aPresContext, nsIContent* aChildContent)
  1299   if (mRowCount >= 0)
  1300     ++mRowCount;
  1302   // The RDF content builder will build content nodes such that they are all 
  1303   // ready when OnContentInserted is first called, meaning the first call
  1304   // to CreateRows will create all the frames, but OnContentInserted will
  1305   // still be called again for each content node - so we need to make sure
  1306   // that the frame for each content node hasn't already been created.
  1307   nsIFrame* childFrame = aChildContent->GetPrimaryFrame();
  1308   if (childFrame)
  1309     return;
  1311   int32_t siblingIndex;
  1312   nsCOMPtr<nsIContent> nextSiblingContent;
  1313   GetListItemNextSibling(aChildContent, getter_AddRefs(nextSiblingContent), siblingIndex);
  1315   // if we're inserting our item before the first visible content,
  1316   // then we need to shift all rows down by one
  1317   if (siblingIndex >= 0 &&  siblingIndex-1 <= mCurrentIndex) {
  1318     mTopFrame = nullptr;
  1319     mRowsToPrepend = 1;
  1320   } else if (nextSiblingContent) {
  1321     // we may be inserting before a frame that is on screen
  1322     nsIFrame* nextSiblingFrame = nextSiblingContent->GetPrimaryFrame();
  1323     mLinkupFrame = nextSiblingFrame;
  1326   CreateRows();
  1327   PresContext()->PresShell()->
  1328     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1329                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1332 // 
  1333 // Called by nsCSSFrameConstructor when listitem content is removed.
  1334 //
  1335 void
  1336 nsListBoxBodyFrame::OnContentRemoved(nsPresContext* aPresContext,
  1337                                      nsIContent* aContainer,
  1338                                      nsIFrame* aChildFrame,
  1339                                      nsIContent* aOldNextSibling)
  1341   NS_ASSERTION(!aChildFrame || aChildFrame->GetParent() == this,
  1342                "Removing frame that's not our child... Not good");
  1344   if (mRowCount >= 0)
  1345     --mRowCount;
  1347   if (aContainer) {
  1348     if (!aChildFrame) {
  1349       // The row we are removing is out of view, so we need to try to
  1350       // determine the index of its next sibling.
  1351       int32_t siblingIndex = -1;
  1352       if (aOldNextSibling) {
  1353         nsCOMPtr<nsIContent> nextSiblingContent;
  1354         GetListItemNextSibling(aOldNextSibling,
  1355                                getter_AddRefs(nextSiblingContent),
  1356                                siblingIndex);
  1359       // if the row being removed is off-screen and above the top frame, we need to
  1360       // adjust our top index and tell the scrollbar to shift up one row.
  1361       if (siblingIndex >= 0 && siblingIndex-1 < mCurrentIndex) {
  1362         NS_PRECONDITION(mCurrentIndex > 0, "mCurrentIndex > 0");
  1363         --mCurrentIndex;
  1364         mYPosition = mCurrentIndex*mRowHeight;
  1365         nsWeakFrame weakChildFrame(aChildFrame);
  1366         VerticalScroll(mYPosition);
  1367         if (!weakChildFrame.IsAlive()) {
  1368           return;
  1371     } else if (mCurrentIndex > 0) {
  1372       // At this point, we know we have a scrollbar, and we need to know 
  1373       // if we are scrolled to the last row.  In this case, the behavior
  1374       // of the scrollbar is to stay locked to the bottom.  Since we are
  1375       // removing visible content, the first visible row will have to move
  1376       // down by one, and we will have to insert a new frame at the top.
  1378       // if the last content node has a frame, we are scrolled to the bottom
  1379       nsIContent* lastChild = nullptr;
  1380       FlattenedChildIterator iter(mContent);
  1381       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  1382         lastChild = child;
  1385       if (lastChild) {
  1386         nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
  1388         if (lastChildFrame) {
  1389           mTopFrame = nullptr;
  1390           mRowsToPrepend = 1;
  1391           --mCurrentIndex;
  1392           mYPosition = mCurrentIndex*mRowHeight;
  1393           nsWeakFrame weakChildFrame(aChildFrame);
  1394           VerticalScroll(mYPosition);
  1395           if (!weakChildFrame.IsAlive()) {
  1396             return;
  1403   // if we're removing the top row, the new top row is the next row
  1404   if (mTopFrame && mTopFrame == aChildFrame)
  1405     mTopFrame = mTopFrame->GetNextSibling();
  1407   // Go ahead and delete the frame.
  1408   nsBoxLayoutState state(aPresContext);
  1409   if (aChildFrame) {
  1410     RemoveChildFrame(state, aChildFrame);
  1413   PresContext()->PresShell()->
  1414     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
  1415                      NS_FRAME_HAS_DIRTY_CHILDREN);
  1418 void
  1419 nsListBoxBodyFrame::GetListItemContentAt(int32_t aIndex, nsIContent** aContent)
  1421   *aContent = nullptr;
  1423   int32_t itemsFound = 0;
  1424   FlattenedChildIterator iter(mContent);
  1425   for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  1426     if (child->Tag() == nsGkAtoms::listitem) {
  1427       ++itemsFound;
  1428       if (itemsFound-1 == aIndex) {
  1429         *aContent = child;
  1430         NS_IF_ADDREF(*aContent);
  1431         return;
  1437 void
  1438 nsListBoxBodyFrame::GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, int32_t& aSiblingIndex)
  1440   *aContent = nullptr;
  1441   aSiblingIndex = -1;
  1442   nsIContent *prevKid = nullptr;
  1443   FlattenedChildIterator iter(mContent);
  1444   for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  1445     if (child->Tag() == nsGkAtoms::listitem) {
  1446       ++aSiblingIndex;
  1447       if (prevKid == aListItem) {
  1448         *aContent = child;
  1449         NS_IF_ADDREF(*aContent);
  1450         return;
  1453     prevKid = child;
  1456   aSiblingIndex = -1; // no match, so there is no next sibling
  1459 void
  1460 nsListBoxBodyFrame::RemoveChildFrame(nsBoxLayoutState &aState,
  1461                                      nsIFrame         *aFrame)
  1463   MOZ_ASSERT(mFrames.ContainsFrame(aFrame));
  1464   MOZ_ASSERT(aFrame != GetContentInsertionFrame());
  1466 #ifdef ACCESSIBILITY
  1467   nsAccessibilityService* accService = nsIPresShell::AccService();
  1468   if (accService) {
  1469     nsIContent* content = aFrame->GetContent();
  1470     accService->ContentRemoved(PresContext()->PresShell(), content->GetParent(),
  1471                                content);
  1473 #endif
  1475   mFrames.RemoveFrame(aFrame);
  1476   if (mLayoutManager)
  1477     mLayoutManager->ChildrenRemoved(this, aState, aFrame);
  1478   aFrame->Destroy();
  1481 // Creation Routines ///////////////////////////////////////////////////////////////////////
  1483 already_AddRefed<nsBoxLayout> NS_NewListBoxLayout();
  1485 nsIFrame*
  1486 NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  1488   nsCOMPtr<nsBoxLayout> layout = NS_NewListBoxLayout();
  1489   return new (aPresShell) nsListBoxBodyFrame(aPresShell, aContext, layout);
  1492 NS_IMPL_FRAMEARENA_HELPERS(nsListBoxBodyFrame)

mercurial