123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsListBoxBodyFrame.h"
- #include "nsListBoxLayout.h"
- #include "mozilla/MathAlgorithms.h"
- #include "nsCOMPtr.h"
- #include "nsGridRowGroupLayout.h"
- #include "nsIServiceManager.h"
- #include "nsGkAtoms.h"
- #include "nsIContent.h"
- #include "nsNameSpaceManager.h"
- #include "nsIDocument.h"
- #include "nsIDOMMouseEvent.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMNodeList.h"
- #include "nsCSSFrameConstructor.h"
- #include "nsIScrollableFrame.h"
- #include "nsScrollbarFrame.h"
- #include "nsView.h"
- #include "nsViewManager.h"
- #include "nsStyleContext.h"
- #include "nsFontMetrics.h"
- #include "nsITimer.h"
- #include "mozilla/StyleSetHandle.h"
- #include "mozilla/StyleSetHandleInlines.h"
- #include "nsPIBoxObject.h"
- #include "nsLayoutUtils.h"
- #include "nsPIListBoxObject.h"
- #include "nsContentUtils.h"
- #include "ChildIterator.h"
- #include "nsRenderingContext.h"
- #include "prtime.h"
- #include <algorithm>
- #ifdef ACCESSIBILITY
- #include "nsAccessibilityService.h"
- #endif
- using namespace mozilla;
- using namespace mozilla::dom;
- /////////////// nsListScrollSmoother //////////////////
- /* A mediator used to smooth out scrolling. It works by seeing if
- * we have time to scroll the amount of rows requested. This is determined
- * by measuring how long it takes to scroll a row. If we can scroll the
- * rows in time we do so. If not we start a timer and skip the request. We
- * do this until the timer finally first because the user has stopped moving
- * the mouse. Then do all the queued requests in on shot.
- */
- // the longest amount of time that can go by before the use
- // notices it as a delay.
- #define USER_TIME_THRESHOLD 150000
- // how long it takes to layout a single row initial value.
- // we will time this after we scroll a few rows.
- #define TIME_PER_ROW_INITAL 50000
- // if we decide we can't layout the rows in the amount of time. How long
- // do we wait before checking again?
- #define SMOOTH_INTERVAL 100
- class nsListScrollSmoother final : public nsITimerCallback
- {
- private:
- virtual ~nsListScrollSmoother();
- public:
- NS_DECL_ISUPPORTS
- explicit nsListScrollSmoother(nsListBoxBodyFrame* aOuter);
- // nsITimerCallback
- NS_DECL_NSITIMERCALLBACK
- void Start();
- void Stop();
- bool IsRunning();
- nsCOMPtr<nsITimer> mRepeatTimer;
- int32_t mDelta;
- nsListBoxBodyFrame* mOuter;
- };
- nsListScrollSmoother::nsListScrollSmoother(nsListBoxBodyFrame* aOuter)
- {
- mDelta = 0;
- mOuter = aOuter;
- }
- nsListScrollSmoother::~nsListScrollSmoother()
- {
- Stop();
- }
- NS_IMETHODIMP
- nsListScrollSmoother::Notify(nsITimer *timer)
- {
- Stop();
- NS_ASSERTION(mOuter, "mOuter is null, see bug #68365");
- if (!mOuter) return NS_OK;
- // actually do some work.
- mOuter->InternalPositionChangedCallback();
- return NS_OK;
- }
- bool
- nsListScrollSmoother::IsRunning()
- {
- return mRepeatTimer ? true : false;
- }
- void
- nsListScrollSmoother::Start()
- {
- Stop();
- mRepeatTimer = do_CreateInstance("@mozilla.org/timer;1");
- mRepeatTimer->InitWithCallback(this, SMOOTH_INTERVAL, nsITimer::TYPE_ONE_SHOT);
- }
- void
- nsListScrollSmoother::Stop()
- {
- if ( mRepeatTimer ) {
- mRepeatTimer->Cancel();
- mRepeatTimer = nullptr;
- }
- }
- NS_IMPL_ISUPPORTS(nsListScrollSmoother, nsITimerCallback)
- /////////////// nsListBoxBodyFrame //////////////////
- nsListBoxBodyFrame::nsListBoxBodyFrame(nsStyleContext* aContext,
- nsBoxLayout* aLayoutManager)
- : nsBoxFrame(aContext, false, aLayoutManager),
- mTopFrame(nullptr),
- mBottomFrame(nullptr),
- mLinkupFrame(nullptr),
- mScrollSmoother(nullptr),
- mRowsToPrepend(0),
- mRowCount(-1),
- mRowHeight(0),
- mAvailableHeight(0),
- mStringWidth(-1),
- mCurrentIndex(0),
- mOldIndex(0),
- mYPosition(0),
- mTimePerRow(TIME_PER_ROW_INITAL),
- mRowHeightWasSet(false),
- mScrolling(false),
- mAdjustScroll(false),
- mReflowCallbackPosted(false)
- {
- }
- nsListBoxBodyFrame::~nsListBoxBodyFrame()
- {
- NS_IF_RELEASE(mScrollSmoother);
- #if USE_TIMER_TO_DELAY_SCROLLING
- StopScrollTracking();
- mAutoScrollTimer = nullptr;
- #endif
- }
- NS_QUERYFRAME_HEAD(nsListBoxBodyFrame)
- NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
- NS_QUERYFRAME_ENTRY(nsListBoxBodyFrame)
- NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
- ////////// nsIFrame /////////////////
- void
- nsListBoxBodyFrame::Init(nsIContent* aContent,
- nsContainerFrame* aParent,
- nsIFrame* aPrevInFlow)
- {
- nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
- // Don't call nsLayoutUtils::GetScrollableFrameFor since we are not its
- // scrollframe child yet.
- nsIScrollableFrame* scrollFrame = do_QueryFrame(aParent);
- if (scrollFrame) {
- nsIFrame* verticalScrollbar = scrollFrame->GetScrollbarBox(true);
- nsScrollbarFrame* scrollbarFrame = do_QueryFrame(verticalScrollbar);
- if (scrollbarFrame) {
- scrollbarFrame->SetScrollbarMediatorContent(GetContent());
- }
- }
- RefPtr<nsFontMetrics> fm =
- nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
- mRowHeight = fm->MaxHeight();
- }
- void
- nsListBoxBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
- {
- // make sure we cancel any posted callbacks.
- if (mReflowCallbackPosted)
- PresContext()->PresShell()->CancelReflowCallback(this);
- // Revoke any pending position changed events
- for (uint32_t i = 0; i < mPendingPositionChangeEvents.Length(); ++i) {
- mPendingPositionChangeEvents[i]->Revoke();
- }
- // Make sure we tell our listbox's box object we're being destroyed.
- if (mBoxObject) {
- mBoxObject->ClearCachedValues();
- }
- nsBoxFrame::DestroyFrom(aDestructRoot);
- }
- nsresult
- nsListBoxBodyFrame::AttributeChanged(int32_t aNameSpaceID,
- nsIAtom* aAttribute,
- int32_t aModType)
- {
- nsresult rv = NS_OK;
- if (aAttribute == nsGkAtoms::rows) {
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- }
- else
- rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
- return rv;
-
- }
- /* virtual */ void
- nsListBoxBodyFrame::MarkIntrinsicISizesDirty()
- {
- mStringWidth = -1;
- nsBoxFrame::MarkIntrinsicISizesDirty();
- }
- /////////// nsBox ///////////////
- NS_IMETHODIMP
- nsListBoxBodyFrame::DoXULLayout(nsBoxLayoutState& aBoxLayoutState)
- {
- if (mScrolling)
- aBoxLayoutState.SetPaintingDisabled(true);
- nsresult rv = nsBoxFrame::DoXULLayout(aBoxLayoutState);
- // determine the real height for the scrollable area from the total number
- // of rows, since non-visible rows don't yet have frames
- nsRect rect(nsPoint(0, 0), GetSize());
- nsOverflowAreas overflow(rect, rect);
- if (mLayoutManager) {
- nsIFrame* childFrame = mFrames.FirstChild();
- while (childFrame) {
- ConsiderChildOverflow(overflow, childFrame);
- childFrame = childFrame->GetNextSibling();
- }
- nsSize prefSize = mLayoutManager->GetXULPrefSize(this, aBoxLayoutState);
- NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
- nsRect& o = overflow.Overflow(otype);
- o.height = std::max(o.height, prefSize.height);
- }
- }
- FinishAndStoreOverflow(overflow, GetSize());
- if (mScrolling)
- aBoxLayoutState.SetPaintingDisabled(false);
- // if we are scrolled and the row height changed
- // make sure we are scrolled to a correct index.
- if (mAdjustScroll)
- PostReflowCallback();
- return rv;
- }
- nsSize
- nsListBoxBodyFrame::GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState)
- {
- nsSize result(0, 0);
- if (nsContentUtils::HasNonEmptyAttr(GetContent(), kNameSpaceID_None,
- nsGkAtoms::sizemode)) {
- result = GetXULPrefSize(aBoxLayoutState);
- result.height = 0;
- nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
- if (scrollFrame &&
- scrollFrame->GetScrollStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
- nsMargin scrollbars =
- scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
- result.width += scrollbars.left + scrollbars.right;
- }
- }
- return result;
- }
- nsSize
- nsListBoxBodyFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
- {
- nsSize pref = nsBoxFrame::GetXULPrefSize(aBoxLayoutState);
- int32_t size = GetFixedRowSize();
- if (size > -1)
- pref.height = size*GetRowHeightAppUnits();
- nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
- if (scrollFrame &&
- scrollFrame->GetScrollStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
- nsMargin scrollbars = scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
- pref.width += scrollbars.left + scrollbars.right;
- }
- return pref;
- }
- ///////////// nsIScrollbarMediator ///////////////
- void
- nsListBoxBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection,
- nsIScrollbarMediator::ScrollSnapMode aSnap)
- {
- // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
- MOZ_ASSERT(aScrollbar != nullptr);
- aScrollbar->SetIncrementToPage(aDirection);
- nsWeakFrame weakFrame(this);
- int32_t newPos = aScrollbar->MoveToNewPosition();
- if (!weakFrame.IsAlive()) {
- return;
- }
- UpdateIndex(newPos);
- }
- void
- nsListBoxBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection,
- nsIScrollbarMediator::ScrollSnapMode aSnap)
- {
- // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
- MOZ_ASSERT(aScrollbar != nullptr);
- aScrollbar->SetIncrementToWhole(aDirection);
- nsWeakFrame weakFrame(this);
- int32_t newPos = aScrollbar->MoveToNewPosition();
- if (!weakFrame.IsAlive()) {
- return;
- }
- UpdateIndex(newPos);
- }
- void
- nsListBoxBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection,
- nsIScrollbarMediator::ScrollSnapMode aSnap)
- {
- // CSS Scroll Snapping is not enabled for XUL, aSnap is ignored
- MOZ_ASSERT(aScrollbar != nullptr);
- aScrollbar->SetIncrementToLine(aDirection);
- nsWeakFrame weakFrame(this);
- int32_t newPos = aScrollbar->MoveToNewPosition();
- if (!weakFrame.IsAlive()) {
- return;
- }
- UpdateIndex(newPos);
- }
- void
- nsListBoxBodyFrame::RepeatButtonScroll(nsScrollbarFrame* aScrollbar)
- {
- nsWeakFrame weakFrame(this);
- int32_t newPos = aScrollbar->MoveToNewPosition();
- if (!weakFrame.IsAlive()) {
- return;
- }
- UpdateIndex(newPos);
- }
- int32_t
- nsListBoxBodyFrame::ToRowIndex(nscoord aPos) const
- {
- return NS_roundf(float(std::max(aPos, 0)) / mRowHeight);
- }
- void
- nsListBoxBodyFrame::ThumbMoved(nsScrollbarFrame* aScrollbar,
- nscoord aOldPos,
- nscoord aNewPos)
- {
- if (mScrolling || mRowHeight == 0)
- return;
- int32_t newIndex = ToRowIndex(aNewPos);
- if (newIndex == mCurrentIndex) {
- return;
- }
- int32_t rowDelta = newIndex - mCurrentIndex;
- nsListScrollSmoother* smoother = GetSmoother();
- // if we can't scroll the rows in time then start a timer. We will eat
- // events until the user stops moving and the timer stops.
- if (smoother->IsRunning() || Abs(rowDelta)*mTimePerRow > USER_TIME_THRESHOLD) {
- smoother->Stop();
- smoother->mDelta = rowDelta;
- smoother->Start();
- return;
- }
- smoother->Stop();
- mCurrentIndex = newIndex;
- smoother->mDelta = 0;
-
- if (mCurrentIndex < 0) {
- mCurrentIndex = 0;
- return;
- }
- InternalPositionChanged(rowDelta < 0, Abs(rowDelta));
- }
- void
- nsListBoxBodyFrame::VisibilityChanged(bool aVisible)
- {
- if (mRowHeight == 0)
- return;
- int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
- if (lastPageTopRow < 0)
- lastPageTopRow = 0;
- int32_t delta = mCurrentIndex - lastPageTopRow;
- if (delta > 0) {
- mCurrentIndex = lastPageTopRow;
- InternalPositionChanged(true, delta);
- }
- }
- nsIFrame*
- nsListBoxBodyFrame::GetScrollbarBox(bool aVertical)
- {
- nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
- return scrollFrame ? scrollFrame->GetScrollbarBox(true) : nullptr;
- }
- void
- nsListBoxBodyFrame::UpdateIndex(int32_t aNewPos)
- {
- int32_t newIndex = ToRowIndex(nsPresContext::CSSPixelsToAppUnits(aNewPos));
- if (newIndex == mCurrentIndex) {
- return;
- }
- bool up = newIndex < mCurrentIndex;
- int32_t indexDelta = Abs(newIndex - mCurrentIndex);
- mCurrentIndex = newIndex;
- InternalPositionChanged(up, indexDelta);
- }
-
- ///////////// nsIReflowCallback ///////////////
- bool
- nsListBoxBodyFrame::ReflowFinished()
- {
- nsAutoScriptBlocker scriptBlocker;
- // now create or destroy any rows as needed
- CreateRows();
- // keep scrollbar in sync
- if (mAdjustScroll) {
- VerticalScroll(mYPosition);
- mAdjustScroll = false;
- }
- // if the row height changed then mark everything as a style change.
- // That will dirty the entire listbox
- if (mRowHeightWasSet) {
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- int32_t pos = mCurrentIndex * mRowHeight;
- if (mYPosition != pos)
- mAdjustScroll = true;
- mRowHeightWasSet = false;
- }
- mReflowCallbackPosted = false;
- return true;
- }
- void
- nsListBoxBodyFrame::ReflowCallbackCanceled()
- {
- mReflowCallbackPosted = false;
- }
- ///////// ListBoxObject ///////////////
- int32_t
- nsListBoxBodyFrame::GetNumberOfVisibleRows()
- {
- return mRowHeight ? GetAvailableHeight() / mRowHeight : 0;
- }
- int32_t
- nsListBoxBodyFrame::GetIndexOfFirstVisibleRow()
- {
- return mCurrentIndex;
- }
- nsresult
- nsListBoxBodyFrame::EnsureIndexIsVisible(int32_t aRowIndex)
- {
- if (aRowIndex < 0)
- return NS_ERROR_ILLEGAL_VALUE;
- int32_t rows = 0;
- if (mRowHeight)
- rows = GetAvailableHeight()/mRowHeight;
- if (rows <= 0)
- rows = 1;
- int32_t bottomIndex = mCurrentIndex + rows;
-
- // if row is visible, ignore
- if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex)
- return NS_OK;
- int32_t delta;
- bool up = aRowIndex < mCurrentIndex;
- if (up) {
- delta = mCurrentIndex - aRowIndex;
- mCurrentIndex = aRowIndex;
- }
- else {
- // Check to be sure we're not scrolling off the bottom of the tree
- if (aRowIndex >= GetRowCount())
- return NS_ERROR_ILLEGAL_VALUE;
- // Bring it just into view.
- delta = 1 + (aRowIndex-bottomIndex);
- mCurrentIndex += delta;
- }
- // Safe to not go off an event here, since this is coming from the
- // box object.
- DoInternalPositionChangedSync(up, delta);
- return NS_OK;
- }
- nsresult
- nsListBoxBodyFrame::ScrollByLines(int32_t aNumLines)
- {
- int32_t scrollIndex = GetIndexOfFirstVisibleRow(),
- visibleRows = GetNumberOfVisibleRows();
- scrollIndex += aNumLines;
-
- if (scrollIndex < 0)
- scrollIndex = 0;
- else {
- int32_t numRows = GetRowCount();
- int32_t lastPageTopRow = numRows - visibleRows;
- if (scrollIndex > lastPageTopRow)
- scrollIndex = lastPageTopRow;
- }
-
- ScrollToIndex(scrollIndex);
- return NS_OK;
- }
- // walks the DOM to get the zero-based row index of the content
- nsresult
- nsListBoxBodyFrame::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
- {
- if (aItem) {
- *_retval = 0;
- nsCOMPtr<nsIContent> itemContent(do_QueryInterface(aItem));
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- // we hit a list row, count it
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- // is this it?
- if (child == itemContent)
- return NS_OK;
- ++(*_retval);
- }
- }
- }
- // not found
- *_retval = -1;
- return NS_OK;
- }
- nsresult
- nsListBoxBodyFrame::GetItemAtIndex(int32_t aIndex, nsIDOMElement** aItem)
- {
- *aItem = nullptr;
- if (aIndex < 0)
- return NS_OK;
- int32_t itemCount = 0;
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- // we hit a list row, check if it is the one we are looking for
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- // is this it?
- if (itemCount == aIndex) {
- return CallQueryInterface(child, aItem);
- }
- ++itemCount;
- }
- }
- // not found
- return NS_OK;
- }
- /////////// nsListBoxBodyFrame ///////////////
- int32_t
- nsListBoxBodyFrame::GetRowCount()
- {
- if (mRowCount < 0)
- ComputeTotalRowCount();
- return mRowCount;
- }
- int32_t
- nsListBoxBodyFrame::GetFixedRowSize()
- {
- nsresult dummy;
- nsAutoString rows;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
- if (!rows.IsEmpty())
- return rows.ToInteger(&dummy);
-
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::size, rows);
- if (!rows.IsEmpty())
- return rows.ToInteger(&dummy);
- return -1;
- }
- void
- nsListBoxBodyFrame::SetRowHeight(nscoord aRowHeight)
- {
- if (aRowHeight > mRowHeight) {
- mRowHeight = aRowHeight;
- // signal we need to dirty everything
- // and we want to be notified after reflow
- // so we can create or destory rows as needed
- mRowHeightWasSet = true;
- PostReflowCallback();
- }
- }
- nscoord
- nsListBoxBodyFrame::GetAvailableHeight()
- {
- nsIScrollableFrame* scrollFrame =
- nsLayoutUtils::GetScrollableFrameFor(this);
- if (scrollFrame) {
- return scrollFrame->GetScrollPortRect().height;
- }
- return 0;
- }
- nscoord
- nsListBoxBodyFrame::GetYPosition()
- {
- return mYPosition;
- }
- nscoord
- nsListBoxBodyFrame::ComputeIntrinsicISize(nsBoxLayoutState& aBoxLayoutState)
- {
- if (mStringWidth != -1)
- return mStringWidth;
- nscoord largestWidth = 0;
- int32_t index = 0;
- nsCOMPtr<nsIDOMElement> firstRowEl;
- GetItemAtIndex(index, getter_AddRefs(firstRowEl));
- nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl));
- if (firstRowContent) {
- RefPtr<nsStyleContext> styleContext;
- nsPresContext *presContext = aBoxLayoutState.PresContext();
- styleContext = presContext->StyleSet()->
- ResolveStyleFor(firstRowContent->AsElement(), nullptr);
- nscoord width = 0;
- nsMargin margin(0,0,0,0);
- if (styleContext->StylePadding()->GetPadding(margin))
- width += margin.LeftRight();
- width += styleContext->StyleBorder()->GetComputedBorder().LeftRight();
- if (styleContext->StyleMargin()->GetMargin(margin))
- width += margin.LeftRight();
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
- if (rendContext) {
- nsAutoString value;
- uint32_t textCount = child->GetChildCount();
- for (uint32_t j = 0; j < textCount; ++j) {
- nsIContent* text = child->GetChildAt(j);
- if (text && text->IsNodeOfType(nsINode::eTEXT)) {
- text->AppendTextTo(value);
- }
- }
- RefPtr<nsFontMetrics> fm =
- nsLayoutUtils::GetFontMetricsForStyleContext(styleContext);
- nscoord textWidth =
- nsLayoutUtils::AppUnitWidthOfStringBidi(value, this, *fm,
- *rendContext);
- textWidth += width;
- if (textWidth > largestWidth)
- largestWidth = textWidth;
- }
- }
- }
- }
- mStringWidth = largestWidth;
- return mStringWidth;
- }
- void
- nsListBoxBodyFrame::ComputeTotalRowCount()
- {
- mRowCount = 0;
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- ++mRowCount;
- }
- }
- }
- void
- nsListBoxBodyFrame::PostReflowCallback()
- {
- if (!mReflowCallbackPosted) {
- mReflowCallbackPosted = true;
- PresContext()->PresShell()->PostReflowCallback(this);
- }
- }
- ////////// scrolling
- nsresult
- nsListBoxBodyFrame::ScrollToIndex(int32_t aRowIndex)
- {
- if (( aRowIndex < 0 ) || (mRowHeight == 0))
- return NS_OK;
-
- int32_t newIndex = aRowIndex;
- int32_t delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex;
- bool up = newIndex < mCurrentIndex;
- // Check to be sure we're not scrolling off the bottom of the tree
- int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
- if (lastPageTopRow < 0)
- lastPageTopRow = 0;
- if (aRowIndex > lastPageTopRow)
- return NS_OK;
- mCurrentIndex = newIndex;
- nsWeakFrame weak(this);
- // Since we're going to flush anyway, we need to not do this off an event
- DoInternalPositionChangedSync(up, delta);
- if (!weak.IsAlive()) {
- return NS_OK;
- }
- // This change has to happen immediately.
- // Flush any pending reflow commands.
- // XXXbz why, exactly?
- mContent->GetComposedDoc()->FlushPendingNotifications(Flush_Layout);
- return NS_OK;
- }
- nsresult
- nsListBoxBodyFrame::InternalPositionChangedCallback()
- {
- nsListScrollSmoother* smoother = GetSmoother();
- if (smoother->mDelta == 0)
- return NS_OK;
- mCurrentIndex += smoother->mDelta;
- if (mCurrentIndex < 0)
- mCurrentIndex = 0;
- return DoInternalPositionChangedSync(smoother->mDelta < 0,
- smoother->mDelta < 0 ?
- -smoother->mDelta : smoother->mDelta);
- }
- nsresult
- nsListBoxBodyFrame::InternalPositionChanged(bool aUp, int32_t aDelta)
- {
- RefPtr<nsPositionChangedEvent> ev =
- new nsPositionChangedEvent(this, aUp, aDelta);
- nsresult rv = NS_DispatchToCurrentThread(ev);
- if (NS_SUCCEEDED(rv)) {
- if (!mPendingPositionChangeEvents.AppendElement(ev)) {
- rv = NS_ERROR_OUT_OF_MEMORY;
- ev->Revoke();
- }
- }
- return rv;
- }
- nsresult
- nsListBoxBodyFrame::DoInternalPositionChangedSync(bool aUp, int32_t aDelta)
- {
- nsWeakFrame weak(this);
-
- // Process all the pending position changes first
- nsTArray< RefPtr<nsPositionChangedEvent> > temp;
- temp.SwapElements(mPendingPositionChangeEvents);
- for (uint32_t i = 0; i < temp.Length(); ++i) {
- if (weak.IsAlive()) {
- temp[i]->Run();
- }
- temp[i]->Revoke();
- }
- if (!weak.IsAlive()) {
- return NS_OK;
- }
- return DoInternalPositionChanged(aUp, aDelta);
- }
- nsresult
- nsListBoxBodyFrame::DoInternalPositionChanged(bool aUp, int32_t aDelta)
- {
- if (aDelta == 0)
- return NS_OK;
- RefPtr<nsPresContext> presContext(PresContext());
- nsBoxLayoutState state(presContext);
- // begin timing how long it takes to scroll a row
- PRTime start = PR_Now();
- nsWeakFrame weakThis(this);
- mContent->GetComposedDoc()->FlushPendingNotifications(Flush_Layout);
- if (!weakThis.IsAlive()) {
- return NS_OK;
- }
- {
- nsAutoScriptBlocker scriptBlocker;
- int32_t visibleRows = 0;
- if (mRowHeight)
- visibleRows = GetAvailableHeight()/mRowHeight;
-
- if (aDelta < visibleRows) {
- int32_t loseRows = aDelta;
- if (aUp) {
- // scrolling up, destroy rows from the bottom downwards
- ReverseDestroyRows(loseRows);
- mRowsToPrepend += aDelta;
- mLinkupFrame = nullptr;
- }
- else {
- // scrolling down, destroy rows from the top upwards
- DestroyRows(loseRows);
- mRowsToPrepend = 0;
- }
- }
- else {
- // We have scrolled so much that all of our current frames will
- // go off screen, so blow them all away. Weeee!
- nsIFrame *currBox = mFrames.FirstChild();
- nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
- fc->BeginUpdate();
- while (currBox) {
- nsIFrame *nextBox = currBox->GetNextSibling();
- RemoveChildFrame(state, currBox);
- currBox = nextBox;
- }
- fc->EndUpdate();
- }
- // clear frame markers so that CreateRows will re-create
- mTopFrame = mBottomFrame = nullptr;
-
- mYPosition = mCurrentIndex*mRowHeight;
- mScrolling = true;
- presContext->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- if (!weakThis.IsAlive()) {
- return NS_OK;
- }
- // Flush calls CreateRows
- // XXXbz there has to be a better way to do this than flushing!
- presContext->PresShell()->FlushPendingNotifications(Flush_Layout);
- if (!weakThis.IsAlive()) {
- return NS_OK;
- }
- mScrolling = false;
-
- VerticalScroll(mYPosition);
- PRTime end = PR_Now();
- int32_t newTime = int32_t(end - start) / aDelta;
- // average old and new
- mTimePerRow = (newTime + mTimePerRow)/2;
-
- return NS_OK;
- }
- nsListScrollSmoother*
- nsListBoxBodyFrame::GetSmoother()
- {
- if (!mScrollSmoother) {
- mScrollSmoother = new nsListScrollSmoother(this);
- NS_ASSERTION(mScrollSmoother, "out of memory");
- NS_IF_ADDREF(mScrollSmoother);
- }
- return mScrollSmoother;
- }
- void
- nsListBoxBodyFrame::VerticalScroll(int32_t aPosition)
- {
- nsIScrollableFrame* scrollFrame
- = nsLayoutUtils::GetScrollableFrameFor(this);
- if (!scrollFrame) {
- return;
- }
- nsPoint scrollPosition = scrollFrame->GetScrollPosition();
-
- nsWeakFrame weakFrame(this);
- scrollFrame->ScrollTo(nsPoint(scrollPosition.x, aPosition),
- nsIScrollableFrame::INSTANT);
- if (!weakFrame.IsAlive()) {
- return;
- }
- mYPosition = aPosition;
- }
- ////////// frame and box retrieval
- nsIFrame*
- nsListBoxBodyFrame::GetFirstFrame()
- {
- mTopFrame = mFrames.FirstChild();
- return mTopFrame;
- }
- nsIFrame*
- nsListBoxBodyFrame::GetLastFrame()
- {
- return mFrames.LastChild();
- }
- bool
- nsListBoxBodyFrame::SupportsOrdinalsInChildren()
- {
- return false;
- }
- ////////// lazy row creation and destruction
- void
- nsListBoxBodyFrame::CreateRows()
- {
- // Get our client rect.
- nsRect clientRect;
- GetXULClientRect(clientRect);
- // Get the starting y position and the remaining available
- // height.
- nscoord availableHeight = GetAvailableHeight();
-
- if (availableHeight <= 0) {
- bool fixed = (GetFixedRowSize() != -1);
- if (fixed)
- availableHeight = 10;
- else
- return;
- }
-
- // get the first tree box. If there isn't one create one.
- bool created = false;
- nsIFrame* box = GetFirstItemBox(0, &created);
- nscoord rowHeight = GetRowHeightAppUnits();
- while (box) {
- if (created && mRowsToPrepend > 0)
- --mRowsToPrepend;
- // if the row height is 0 then fail. Wait until someone
- // laid out and sets the row height.
- if (rowHeight == 0)
- return;
-
- availableHeight -= rowHeight;
-
- // should we continue? Is the enought height?
- if (!ContinueReflow(availableHeight))
- break;
- // get the next tree box. Create one if needed.
- box = GetNextItemBox(box, 0, &created);
- }
- mRowsToPrepend = 0;
- mLinkupFrame = nullptr;
- }
- void
- nsListBoxBodyFrame::DestroyRows(int32_t& aRowsToLose)
- {
- // We need to destroy frames until our row count has been properly
- // reduced. A reflow will then pick up and create the new frames.
- nsIFrame* childFrame = GetFirstFrame();
- nsBoxLayoutState state(PresContext());
- nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
- fc->BeginUpdate();
- while (childFrame && aRowsToLose > 0) {
- --aRowsToLose;
- nsIFrame* nextFrame = childFrame->GetNextSibling();
- RemoveChildFrame(state, childFrame);
- mTopFrame = childFrame = nextFrame;
- }
- fc->EndUpdate();
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- void
- nsListBoxBodyFrame::ReverseDestroyRows(int32_t& aRowsToLose)
- {
- // We need to destroy frames until our row count has been properly
- // reduced. A reflow will then pick up and create the new frames.
- nsIFrame* childFrame = GetLastFrame();
- nsBoxLayoutState state(PresContext());
- nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
- fc->BeginUpdate();
- while (childFrame && aRowsToLose > 0) {
- --aRowsToLose;
-
- nsIFrame* prevFrame;
- prevFrame = childFrame->GetPrevSibling();
- RemoveChildFrame(state, childFrame);
- mBottomFrame = childFrame = prevFrame;
- }
- fc->EndUpdate();
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- static bool
- IsListItemChild(nsListBoxBodyFrame* aParent, nsIContent* aChild,
- nsIFrame** aChildFrame)
- {
- *aChildFrame = nullptr;
- if (!aChild->IsXULElement(nsGkAtoms::listitem)) {
- return false;
- }
- nsIFrame* existingFrame = aChild->GetPrimaryFrame();
- if (existingFrame && existingFrame->GetParent() != aParent) {
- return false;
- }
- *aChildFrame = existingFrame;
- return true;
- }
- //
- // Get the nsIFrame for the first visible listitem, and if none exists,
- // create one.
- //
- nsIFrame*
- nsListBoxBodyFrame::GetFirstItemBox(int32_t aOffset, bool* aCreated)
- {
- if (aCreated)
- *aCreated = false;
- // Clear ourselves out.
- mBottomFrame = mTopFrame;
- if (mTopFrame) {
- return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
- }
- // top frame was cleared out
- mTopFrame = GetFirstFrame();
- mBottomFrame = mTopFrame;
- if (mTopFrame && mRowsToPrepend <= 0) {
- return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
- }
- // At this point, we either have no frames at all,
- // or the user has scrolled upwards, leaving frames
- // to be created at the top. Let's determine which
- // content needs a new frame first.
- nsCOMPtr<nsIContent> startContent;
- if (mTopFrame && mRowsToPrepend > 0) {
- // We need to insert rows before the top frame
- nsIContent* topContent = mTopFrame->GetContent();
- nsIContent* topParent = topContent->GetParent();
- int32_t contentIndex = topParent->IndexOf(topContent);
- contentIndex -= aOffset;
- if (contentIndex < 0)
- return nullptr;
- startContent = topParent->GetChildAt(contentIndex - mRowsToPrepend);
- } else {
- // This will be the first item frame we create. Use the content
- // at the current index, which is the first index scrolled into view
- GetListItemContentAt(mCurrentIndex+aOffset, getter_AddRefs(startContent));
- }
- if (startContent) {
- nsIFrame* existingFrame;
- if (!IsListItemChild(this, startContent, &existingFrame)) {
- return GetFirstItemBox(++aOffset, aCreated);
- }
- if (existingFrame) {
- return existingFrame->IsXULBoxFrame() ? existingFrame : nullptr;
- }
- // Either append the new frame, or prepend it (at index 0)
- // XXX check here if frame was even created, it may not have been if
- // display: none was on listitem content
- bool isAppend = mRowsToPrepend <= 0;
-
- nsPresContext* presContext = PresContext();
- nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
- nsIFrame* topFrame = nullptr;
- fc->CreateListBoxContent(this, nullptr, startContent, &topFrame, isAppend);
- mTopFrame = topFrame;
- if (mTopFrame) {
- if (aCreated)
- *aCreated = true;
- mBottomFrame = mTopFrame;
- return mTopFrame->IsXULBoxFrame() ? mTopFrame : nullptr;
- } else
- return GetFirstItemBox(++aOffset, 0);
- }
- return nullptr;
- }
- //
- // Get the nsIFrame for the next visible listitem after aBox, and if none
- // exists, create one.
- //
- nsIFrame*
- nsListBoxBodyFrame::GetNextItemBox(nsIFrame* aBox, int32_t aOffset,
- bool* aCreated)
- {
- if (aCreated)
- *aCreated = false;
- nsIFrame* result = aBox->GetNextSibling();
- if (!result || result == mLinkupFrame || mRowsToPrepend > 0) {
- // No result found. See if there's a content node that wants a frame.
- nsIContent* prevContent = aBox->GetContent();
- nsIContent* parentContent = prevContent->GetParent();
- int32_t i = parentContent->IndexOf(prevContent);
- uint32_t childCount = parentContent->GetChildCount();
- if (((uint32_t)i + aOffset + 1) < childCount) {
- // There is a content node that wants a frame.
- nsIContent *nextContent = parentContent->GetChildAt(i + aOffset + 1);
- nsIFrame* existingFrame;
- if (!IsListItemChild(this, nextContent, &existingFrame)) {
- return GetNextItemBox(aBox, ++aOffset, aCreated);
- }
- if (!existingFrame) {
- // Either append the new frame, or insert it after the current frame
- bool isAppend = result != mLinkupFrame && mRowsToPrepend <= 0;
- nsIFrame* prevFrame = isAppend ? nullptr : aBox;
-
- nsPresContext* presContext = PresContext();
- nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
- fc->CreateListBoxContent(this, prevFrame, nextContent,
- &result, isAppend);
- if (result) {
- if (aCreated)
- *aCreated = true;
- } else
- return GetNextItemBox(aBox, ++aOffset, aCreated);
- } else {
- result = existingFrame;
- }
-
- mLinkupFrame = nullptr;
- }
- }
- if (!result)
- return nullptr;
- mBottomFrame = result;
- NS_ASSERTION(!result->IsXULBoxFrame() || result->GetParent() == this,
- "returning frame that is not in childlist");
- return result->IsXULBoxFrame() ? result : nullptr;
- }
- bool
- nsListBoxBodyFrame::ContinueReflow(nscoord height)
- {
- #ifdef ACCESSIBILITY
- if (nsIPresShell::IsAccessibilityActive()) {
- // Create all the frames at once so screen readers and
- // onscreen keyboards can see the full list right away
- return true;
- }
- #endif
- if (height <= 0) {
- nsIFrame* lastChild = GetLastFrame();
- nsIFrame* startingPoint = mBottomFrame;
- if (startingPoint == nullptr) {
- // We just want to delete everything but the first item.
- startingPoint = GetFirstFrame();
- }
- if (lastChild != startingPoint) {
- // We have some hangers on (probably caused by shrinking the size of the window).
- // Nuke them.
- nsIFrame* currFrame = startingPoint->GetNextSibling();
- nsBoxLayoutState state(PresContext());
- nsCSSFrameConstructor* fc =
- PresContext()->PresShell()->FrameConstructor();
- fc->BeginUpdate();
- while (currFrame) {
- nsIFrame* nextFrame = currFrame->GetNextSibling();
- RemoveChildFrame(state, currFrame);
- currFrame = nextFrame;
- }
- fc->EndUpdate();
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- return false;
- }
- else
- return true;
- }
- NS_IMETHODIMP
- nsListBoxBodyFrame::ListBoxAppendFrames(nsFrameList& aFrameList)
- {
- // append them after
- nsBoxLayoutState state(PresContext());
- const nsFrameList::Slice& newFrames = mFrames.AppendFrames(nullptr, aFrameList);
- if (mLayoutManager)
- mLayoutManager->ChildrenAppended(this, state, newFrames);
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
-
- return NS_OK;
- }
- NS_IMETHODIMP
- nsListBoxBodyFrame::ListBoxInsertFrames(nsIFrame* aPrevFrame,
- nsFrameList& aFrameList)
- {
- // insert the frames to our info list
- nsBoxLayoutState state(PresContext());
- const nsFrameList::Slice& newFrames =
- mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
- if (mLayoutManager)
- mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- return NS_OK;
- }
- //
- // Called by nsCSSFrameConstructor when a new listitem content is inserted.
- //
- void
- nsListBoxBodyFrame::OnContentInserted(nsIContent* aChildContent)
- {
- if (mRowCount >= 0)
- ++mRowCount;
- // The RDF content builder will build content nodes such that they are all
- // ready when OnContentInserted is first called, meaning the first call
- // to CreateRows will create all the frames, but OnContentInserted will
- // still be called again for each content node - so we need to make sure
- // that the frame for each content node hasn't already been created.
- nsIFrame* childFrame = aChildContent->GetPrimaryFrame();
- if (childFrame)
- return;
- int32_t siblingIndex;
- nsCOMPtr<nsIContent> nextSiblingContent;
- GetListItemNextSibling(aChildContent, getter_AddRefs(nextSiblingContent), siblingIndex);
-
- // if we're inserting our item before the first visible content,
- // then we need to shift all rows down by one
- if (siblingIndex >= 0 && siblingIndex-1 <= mCurrentIndex) {
- mTopFrame = nullptr;
- mRowsToPrepend = 1;
- } else if (nextSiblingContent) {
- // we may be inserting before a frame that is on screen
- nsIFrame* nextSiblingFrame = nextSiblingContent->GetPrimaryFrame();
- mLinkupFrame = nextSiblingFrame;
- }
-
- CreateRows();
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- //
- // Called by nsCSSFrameConstructor when listitem content is removed.
- //
- void
- nsListBoxBodyFrame::OnContentRemoved(nsPresContext* aPresContext,
- nsIContent* aContainer,
- nsIFrame* aChildFrame,
- nsIContent* aOldNextSibling)
- {
- NS_ASSERTION(!aChildFrame || aChildFrame->GetParent() == this,
- "Removing frame that's not our child... Not good");
-
- if (mRowCount >= 0)
- --mRowCount;
- if (aContainer) {
- if (!aChildFrame) {
- // The row we are removing is out of view, so we need to try to
- // determine the index of its next sibling.
- int32_t siblingIndex = -1;
- if (aOldNextSibling) {
- nsCOMPtr<nsIContent> nextSiblingContent;
- GetListItemNextSibling(aOldNextSibling,
- getter_AddRefs(nextSiblingContent),
- siblingIndex);
- }
-
- // if the row being removed is off-screen and above the top frame, we need to
- // adjust our top index and tell the scrollbar to shift up one row.
- if (siblingIndex >= 0 && siblingIndex-1 < mCurrentIndex) {
- NS_PRECONDITION(mCurrentIndex > 0, "mCurrentIndex > 0");
- --mCurrentIndex;
- mYPosition = mCurrentIndex*mRowHeight;
- nsWeakFrame weakChildFrame(aChildFrame);
- VerticalScroll(mYPosition);
- if (!weakChildFrame.IsAlive()) {
- return;
- }
- }
- } else if (mCurrentIndex > 0) {
- // At this point, we know we have a scrollbar, and we need to know
- // if we are scrolled to the last row. In this case, the behavior
- // of the scrollbar is to stay locked to the bottom. Since we are
- // removing visible content, the first visible row will have to move
- // down by one, and we will have to insert a new frame at the top.
- // if the last content node has a frame, we are scrolled to the bottom
- nsIContent* lastChild = nullptr;
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- lastChild = child;
- }
- if (lastChild) {
- nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
- if (lastChildFrame) {
- mTopFrame = nullptr;
- mRowsToPrepend = 1;
- --mCurrentIndex;
- mYPosition = mCurrentIndex*mRowHeight;
- nsWeakFrame weakChildFrame(aChildFrame);
- VerticalScroll(mYPosition);
- if (!weakChildFrame.IsAlive()) {
- return;
- }
- }
- }
- }
- }
- // if we're removing the top row, the new top row is the next row
- if (mTopFrame && mTopFrame == aChildFrame)
- mTopFrame = mTopFrame->GetNextSibling();
- // Go ahead and delete the frame.
- nsBoxLayoutState state(aPresContext);
- if (aChildFrame) {
- RemoveChildFrame(state, aChildFrame);
- }
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eTreeChange,
- NS_FRAME_HAS_DIRTY_CHILDREN);
- }
- void
- nsListBoxBodyFrame::GetListItemContentAt(int32_t aIndex, nsIContent** aContent)
- {
- *aContent = nullptr;
- int32_t itemsFound = 0;
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- ++itemsFound;
- if (itemsFound-1 == aIndex) {
- *aContent = child;
- NS_IF_ADDREF(*aContent);
- return;
- }
- }
- }
- }
- void
- nsListBoxBodyFrame::GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, int32_t& aSiblingIndex)
- {
- *aContent = nullptr;
- aSiblingIndex = -1;
- nsIContent *prevKid = nullptr;
- FlattenedChildIterator iter(mContent);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- if (child->IsXULElement(nsGkAtoms::listitem)) {
- ++aSiblingIndex;
- if (prevKid == aListItem) {
- *aContent = child;
- NS_IF_ADDREF(*aContent);
- return;
- }
- }
- prevKid = child;
- }
- aSiblingIndex = -1; // no match, so there is no next sibling
- }
- void
- nsListBoxBodyFrame::RemoveChildFrame(nsBoxLayoutState &aState,
- nsIFrame *aFrame)
- {
- MOZ_ASSERT(mFrames.ContainsFrame(aFrame));
- MOZ_ASSERT(aFrame != GetContentInsertionFrame());
- #ifdef ACCESSIBILITY
- nsAccessibilityService* accService = nsIPresShell::AccService();
- if (accService) {
- nsIContent* content = aFrame->GetContent();
- accService->ContentRemoved(PresContext()->PresShell(), content);
- }
- #endif
- mFrames.RemoveFrame(aFrame);
- if (mLayoutManager)
- mLayoutManager->ChildrenRemoved(this, aState, aFrame);
- aFrame->Destroy();
- }
- // Creation Routines ///////////////////////////////////////////////////////////////////////
- already_AddRefed<nsBoxLayout> NS_NewListBoxLayout();
- nsIFrame*
- NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
- {
- nsCOMPtr<nsBoxLayout> layout = NS_NewListBoxLayout();
- return new (aPresShell) nsListBoxBodyFrame(aContext, layout);
- }
- NS_IMPL_FRAMEARENA_HELPERS(nsListBoxBodyFrame)
|