LCOV - code coverage report
Current view: top level - layout/xul - nsListBoxBodyFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 743 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 73 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13