Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / layout / xul / nsListBoxBodyFrame.cpp
blob4af327fae0d65dfebf0e0fd7e562709c268cb43e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsListBoxBodyFrame.h"
8 #include "nsListBoxLayout.h"
10 #include "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 "nsAutoPtr.h"
30 #include "nsStyleSet.h"
31 #include "nsPIBoxObject.h"
32 #include "nsLayoutUtils.h"
33 #include "nsPIListBoxObject.h"
34 #include "nsContentUtils.h"
35 #include "ChildIterator.h"
36 #include "nsRenderingContext.h"
37 #include "prtime.h"
38 #include <algorithm>
40 #ifdef ACCESSIBILITY
41 #include "nsAccessibilityService.h"
42 #endif
44 using namespace mozilla;
45 using namespace mozilla::dom;
47 /////////////// nsListScrollSmoother //////////////////
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.
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
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
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
69 class nsListScrollSmoother MOZ_FINAL : public nsITimerCallback
71 private:
72 virtual ~nsListScrollSmoother();
74 public:
75 NS_DECL_ISUPPORTS
77 explicit nsListScrollSmoother(nsListBoxBodyFrame* aOuter);
79 // nsITimerCallback
80 NS_DECL_NSITIMERCALLBACK
82 void Start();
83 void Stop();
84 bool IsRunning();
86 nsCOMPtr<nsITimer> mRepeatTimer;
87 int32_t mDelta;
88 nsListBoxBodyFrame* mOuter;
89 };
91 nsListScrollSmoother::nsListScrollSmoother(nsListBoxBodyFrame* aOuter)
93 mDelta = 0;
94 mOuter = aOuter;
97 nsListScrollSmoother::~nsListScrollSmoother()
99 Stop();
102 NS_IMETHODIMP
103 nsListScrollSmoother::Notify(nsITimer *timer)
105 Stop();
107 NS_ASSERTION(mOuter, "mOuter is null, see bug #68365");
108 if (!mOuter) return NS_OK;
110 // actually do some work.
111 mOuter->InternalPositionChangedCallback();
112 return NS_OK;
115 bool
116 nsListScrollSmoother::IsRunning()
118 return mRepeatTimer ? true : false;
121 void
122 nsListScrollSmoother::Start()
124 Stop();
125 mRepeatTimer = do_CreateInstance("@mozilla.org/timer;1");
126 mRepeatTimer->InitWithCallback(this, SMOOTH_INTERVAL, nsITimer::TYPE_ONE_SHOT);
129 void
130 nsListScrollSmoother::Stop()
132 if ( mRepeatTimer ) {
133 mRepeatTimer->Cancel();
134 mRepeatTimer = nullptr;
138 NS_IMPL_ISUPPORTS(nsListScrollSmoother, nsITimerCallback)
140 /////////////// nsListBoxBodyFrame //////////////////
142 nsListBoxBodyFrame::nsListBoxBodyFrame(nsIPresShell* aPresShell,
143 nsStyleContext* aContext,
144 nsBoxLayout* aLayoutManager)
145 : nsBoxFrame(aPresShell, aContext, false, aLayoutManager),
146 mTopFrame(nullptr),
147 mBottomFrame(nullptr),
148 mLinkupFrame(nullptr),
149 mScrollSmoother(nullptr),
150 mRowsToPrepend(0),
151 mRowCount(-1),
152 mRowHeight(0),
153 mAvailableHeight(0),
154 mStringWidth(-1),
155 mCurrentIndex(0),
156 mOldIndex(0),
157 mYPosition(0),
158 mTimePerRow(TIME_PER_ROW_INITAL),
159 mRowHeightWasSet(false),
160 mScrolling(false),
161 mAdjustScroll(false),
162 mReflowCallbackPosted(false)
166 nsListBoxBodyFrame::~nsListBoxBodyFrame()
168 NS_IF_RELEASE(mScrollSmoother);
170 #if USE_TIMER_TO_DELAY_SCROLLING
171 StopScrollTracking();
172 mAutoScrollTimer = nullptr;
173 #endif
177 NS_QUERYFRAME_HEAD(nsListBoxBodyFrame)
178 NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
179 NS_QUERYFRAME_ENTRY(nsListBoxBodyFrame)
180 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
182 ////////// nsIFrame /////////////////
184 void
185 nsListBoxBodyFrame::Init(nsIContent* aContent,
186 nsContainerFrame* aParent,
187 nsIFrame* aPrevInFlow)
189 nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
190 // Don't call nsLayoutUtils::GetScrollableFrameFor since we are not its
191 // scrollframe child yet.
192 nsIScrollableFrame* scrollFrame = do_QueryFrame(aParent);
193 if (scrollFrame) {
194 nsIFrame* verticalScrollbar = scrollFrame->GetScrollbarBox(true);
195 nsScrollbarFrame* scrollbarFrame = do_QueryFrame(verticalScrollbar);
196 if (scrollbarFrame) {
197 scrollbarFrame->SetScrollbarMediatorContent(GetContent());
200 nsRefPtr<nsFontMetrics> fm;
201 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
202 mRowHeight = fm->MaxHeight();
205 void
206 nsListBoxBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
208 // make sure we cancel any posted callbacks.
209 if (mReflowCallbackPosted)
210 PresContext()->PresShell()->CancelReflowCallback(this);
212 // Revoke any pending position changed events
213 for (uint32_t i = 0; i < mPendingPositionChangeEvents.Length(); ++i) {
214 mPendingPositionChangeEvents[i]->Revoke();
217 // Make sure we tell our listbox's box object we're being destroyed.
218 if (mBoxObject) {
219 mBoxObject->ClearCachedValues();
222 nsBoxFrame::DestroyFrom(aDestructRoot);
225 nsresult
226 nsListBoxBodyFrame::AttributeChanged(int32_t aNameSpaceID,
227 nsIAtom* aAttribute,
228 int32_t aModType)
230 nsresult rv = NS_OK;
232 if (aAttribute == nsGkAtoms::rows) {
233 PresContext()->PresShell()->
234 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
236 else
237 rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
239 return rv;
243 /* virtual */ void
244 nsListBoxBodyFrame::MarkIntrinsicISizesDirty()
246 mStringWidth = -1;
247 nsBoxFrame::MarkIntrinsicISizesDirty();
250 /////////// nsBox ///////////////
252 NS_IMETHODIMP
253 nsListBoxBodyFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
255 if (mScrolling)
256 aBoxLayoutState.SetPaintingDisabled(true);
258 nsresult rv = nsBoxFrame::DoLayout(aBoxLayoutState);
260 // determine the real height for the scrollable area from the total number
261 // of rows, since non-visible rows don't yet have frames
262 nsRect rect(nsPoint(0, 0), GetSize());
263 nsOverflowAreas overflow(rect, rect);
264 if (mLayoutManager) {
265 nsIFrame* childFrame = mFrames.FirstChild();
266 while (childFrame) {
267 ConsiderChildOverflow(overflow, childFrame);
268 childFrame = childFrame->GetNextSibling();
271 nsSize prefSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
272 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
273 nsRect& o = overflow.Overflow(otype);
274 o.height = std::max(o.height, prefSize.height);
277 FinishAndStoreOverflow(overflow, GetSize());
279 if (mScrolling)
280 aBoxLayoutState.SetPaintingDisabled(false);
282 // if we are scrolled and the row height changed
283 // make sure we are scrolled to a correct index.
284 if (mAdjustScroll)
285 PostReflowCallback();
287 return rv;
290 nsSize
291 nsListBoxBodyFrame::GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState)
293 nsSize result(0, 0);
294 if (nsContentUtils::HasNonEmptyAttr(GetContent(), kNameSpaceID_None,
295 nsGkAtoms::sizemode)) {
296 result = GetPrefSize(aBoxLayoutState);
297 result.height = 0;
298 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
299 if (scrollFrame &&
300 scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
301 nsMargin scrollbars =
302 scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
303 result.width += scrollbars.left + scrollbars.right;
306 return result;
309 nsSize
310 nsListBoxBodyFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
312 nsSize pref = nsBoxFrame::GetPrefSize(aBoxLayoutState);
314 int32_t size = GetFixedRowSize();
315 if (size > -1)
316 pref.height = size*GetRowHeightAppUnits();
318 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
319 if (scrollFrame &&
320 scrollFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) {
321 nsMargin scrollbars = scrollFrame->GetDesiredScrollbarSizes(&aBoxLayoutState);
322 pref.width += scrollbars.left + scrollbars.right;
324 return pref;
327 ///////////// nsIScrollbarMediator ///////////////
329 void
330 nsListBoxBodyFrame::ScrollByPage(nsScrollbarFrame* aScrollbar, int32_t aDirection)
332 MOZ_ASSERT(aScrollbar != nullptr);
333 aScrollbar->SetIncrementToPage(aDirection);
334 nsWeakFrame weakFrame(this);
335 int32_t newPos = aScrollbar->MoveToNewPosition();
336 if (!weakFrame.IsAlive()) {
337 return;
339 UpdateIndex(newPos);
342 void
343 nsListBoxBodyFrame::ScrollByWhole(nsScrollbarFrame* aScrollbar, int32_t aDirection)
345 MOZ_ASSERT(aScrollbar != nullptr);
346 aScrollbar->SetIncrementToWhole(aDirection);
347 nsWeakFrame weakFrame(this);
348 int32_t newPos = aScrollbar->MoveToNewPosition();
349 if (!weakFrame.IsAlive()) {
350 return;
352 UpdateIndex(newPos);
355 void
356 nsListBoxBodyFrame::ScrollByLine(nsScrollbarFrame* aScrollbar, int32_t aDirection)
358 MOZ_ASSERT(aScrollbar != nullptr);
359 aScrollbar->SetIncrementToLine(aDirection);
360 nsWeakFrame weakFrame(this);
361 int32_t newPos = aScrollbar->MoveToNewPosition();
362 if (!weakFrame.IsAlive()) {
363 return;
365 UpdateIndex(newPos);
368 void
369 nsListBoxBodyFrame::RepeatButtonScroll(nsScrollbarFrame* aScrollbar)
371 nsWeakFrame weakFrame(this);
372 int32_t newPos = aScrollbar->MoveToNewPosition();
373 if (!weakFrame.IsAlive()) {
374 return;
376 UpdateIndex(newPos);
379 int32_t
380 nsListBoxBodyFrame::ToRowIndex(nscoord aPos) const
382 return NS_roundf(float(std::max(aPos, 0)) / mRowHeight);
385 void
386 nsListBoxBodyFrame::ThumbMoved(nsScrollbarFrame* aScrollbar,
387 nscoord aOldPos,
388 nscoord aNewPos)
390 if (mScrolling || mRowHeight == 0)
391 return;
393 int32_t newIndex = ToRowIndex(aNewPos);
394 if (newIndex == mCurrentIndex) {
395 return;
397 int32_t rowDelta = newIndex - mCurrentIndex;
399 nsListScrollSmoother* smoother = GetSmoother();
401 // if we can't scroll the rows in time then start a timer. We will eat
402 // events until the user stops moving and the timer stops.
403 if (smoother->IsRunning() || Abs(rowDelta)*mTimePerRow > USER_TIME_THRESHOLD) {
405 smoother->Stop();
407 smoother->mDelta = rowDelta;
409 smoother->Start();
411 return;
414 smoother->Stop();
416 mCurrentIndex = newIndex;
417 smoother->mDelta = 0;
419 if (mCurrentIndex < 0) {
420 mCurrentIndex = 0;
421 return;
423 InternalPositionChanged(rowDelta < 0, Abs(rowDelta));
426 void
427 nsListBoxBodyFrame::VisibilityChanged(bool aVisible)
429 if (mRowHeight == 0)
430 return;
432 int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
433 if (lastPageTopRow < 0)
434 lastPageTopRow = 0;
435 int32_t delta = mCurrentIndex - lastPageTopRow;
436 if (delta > 0) {
437 mCurrentIndex = lastPageTopRow;
438 InternalPositionChanged(true, delta);
442 nsIFrame*
443 nsListBoxBodyFrame::GetScrollbarBox(bool aVertical)
445 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
446 return scrollFrame ? scrollFrame->GetScrollbarBox(true) : nullptr;
449 void
450 nsListBoxBodyFrame::UpdateIndex(int32_t aNewPos)
452 int32_t newIndex = ToRowIndex(nsPresContext::CSSPixelsToAppUnits(aNewPos));
453 if (newIndex == mCurrentIndex) {
454 return;
456 bool up = newIndex < mCurrentIndex;
457 int32_t indexDelta = Abs(newIndex - mCurrentIndex);
458 mCurrentIndex = newIndex;
459 InternalPositionChanged(up, indexDelta);
462 ///////////// nsIReflowCallback ///////////////
464 bool
465 nsListBoxBodyFrame::ReflowFinished()
467 nsAutoScriptBlocker scriptBlocker;
468 // now create or destroy any rows as needed
469 CreateRows();
471 // keep scrollbar in sync
472 if (mAdjustScroll) {
473 VerticalScroll(mYPosition);
474 mAdjustScroll = false;
477 // if the row height changed then mark everything as a style change.
478 // That will dirty the entire listbox
479 if (mRowHeightWasSet) {
480 PresContext()->PresShell()->
481 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
482 int32_t pos = mCurrentIndex * mRowHeight;
483 if (mYPosition != pos)
484 mAdjustScroll = true;
485 mRowHeightWasSet = false;
488 mReflowCallbackPosted = false;
489 return true;
492 void
493 nsListBoxBodyFrame::ReflowCallbackCanceled()
495 mReflowCallbackPosted = false;
498 ///////// nsIListBoxObject ///////////////
500 nsresult
501 nsListBoxBodyFrame::GetRowCount(int32_t* aResult)
503 *aResult = GetRowCount();
504 return NS_OK;
507 nsresult
508 nsListBoxBodyFrame::GetNumberOfVisibleRows(int32_t *aResult)
510 *aResult= mRowHeight ? GetAvailableHeight() / mRowHeight : 0;
511 return NS_OK;
514 nsresult
515 nsListBoxBodyFrame::GetIndexOfFirstVisibleRow(int32_t *aResult)
517 *aResult = mCurrentIndex;
518 return NS_OK;
521 nsresult
522 nsListBoxBodyFrame::EnsureIndexIsVisible(int32_t aRowIndex)
524 if (aRowIndex < 0)
525 return NS_ERROR_ILLEGAL_VALUE;
527 int32_t rows = 0;
528 if (mRowHeight)
529 rows = GetAvailableHeight()/mRowHeight;
530 if (rows <= 0)
531 rows = 1;
532 int32_t bottomIndex = mCurrentIndex + rows;
534 // if row is visible, ignore
535 if (mCurrentIndex <= aRowIndex && aRowIndex < bottomIndex)
536 return NS_OK;
538 int32_t delta;
540 bool up = aRowIndex < mCurrentIndex;
541 if (up) {
542 delta = mCurrentIndex - aRowIndex;
543 mCurrentIndex = aRowIndex;
545 else {
546 // Check to be sure we're not scrolling off the bottom of the tree
547 if (aRowIndex >= GetRowCount())
548 return NS_ERROR_ILLEGAL_VALUE;
550 // Bring it just into view.
551 delta = 1 + (aRowIndex-bottomIndex);
552 mCurrentIndex += delta;
555 // Safe to not go off an event here, since this is coming from the
556 // box object.
557 DoInternalPositionChangedSync(up, delta);
558 return NS_OK;
561 nsresult
562 nsListBoxBodyFrame::ScrollByLines(int32_t aNumLines)
564 int32_t scrollIndex, visibleRows;
565 GetIndexOfFirstVisibleRow(&scrollIndex);
566 GetNumberOfVisibleRows(&visibleRows);
568 scrollIndex += aNumLines;
570 if (scrollIndex < 0)
571 scrollIndex = 0;
572 else {
573 int32_t numRows = GetRowCount();
574 int32_t lastPageTopRow = numRows - visibleRows;
575 if (scrollIndex > lastPageTopRow)
576 scrollIndex = lastPageTopRow;
579 ScrollToIndex(scrollIndex);
581 return NS_OK;
584 // walks the DOM to get the zero-based row index of the content
585 nsresult
586 nsListBoxBodyFrame::GetIndexOfItem(nsIDOMElement* aItem, int32_t* _retval)
588 if (aItem) {
589 *_retval = 0;
590 nsCOMPtr<nsIContent> itemContent(do_QueryInterface(aItem));
592 FlattenedChildIterator iter(mContent);
593 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
594 // we hit a list row, count it
595 if (child->Tag() == nsGkAtoms::listitem) {
596 // is this it?
597 if (child == itemContent)
598 return NS_OK;
600 ++(*_retval);
605 // not found
606 *_retval = -1;
607 return NS_OK;
610 nsresult
611 nsListBoxBodyFrame::GetItemAtIndex(int32_t aIndex, nsIDOMElement** aItem)
613 *aItem = nullptr;
614 if (aIndex < 0)
615 return NS_OK;
617 int32_t itemCount = 0;
618 FlattenedChildIterator iter(mContent);
619 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
620 // we hit a list row, check if it is the one we are looking for
621 if (child->Tag() == nsGkAtoms::listitem) {
622 // is this it?
623 if (itemCount == aIndex) {
624 return CallQueryInterface(child, aItem);
626 ++itemCount;
630 // not found
631 return NS_OK;
634 /////////// nsListBoxBodyFrame ///////////////
636 int32_t
637 nsListBoxBodyFrame::GetRowCount()
639 if (mRowCount < 0)
640 ComputeTotalRowCount();
641 return mRowCount;
644 int32_t
645 nsListBoxBodyFrame::GetFixedRowSize()
647 nsresult dummy;
649 nsAutoString rows;
650 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
651 if (!rows.IsEmpty())
652 return rows.ToInteger(&dummy);
654 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::size, rows);
656 if (!rows.IsEmpty())
657 return rows.ToInteger(&dummy);
659 return -1;
662 void
663 nsListBoxBodyFrame::SetRowHeight(nscoord aRowHeight)
665 if (aRowHeight > mRowHeight) {
666 mRowHeight = aRowHeight;
668 // signal we need to dirty everything
669 // and we want to be notified after reflow
670 // so we can create or destory rows as needed
671 mRowHeightWasSet = true;
672 PostReflowCallback();
676 nscoord
677 nsListBoxBodyFrame::GetAvailableHeight()
679 nsIScrollableFrame* scrollFrame =
680 nsLayoutUtils::GetScrollableFrameFor(this);
681 if (scrollFrame) {
682 return scrollFrame->GetScrollPortRect().height;
684 return 0;
687 nscoord
688 nsListBoxBodyFrame::GetYPosition()
690 return mYPosition;
693 nscoord
694 nsListBoxBodyFrame::ComputeIntrinsicISize(nsBoxLayoutState& aBoxLayoutState)
696 if (mStringWidth != -1)
697 return mStringWidth;
699 nscoord largestWidth = 0;
701 int32_t index = 0;
702 nsCOMPtr<nsIDOMElement> firstRowEl;
703 GetItemAtIndex(index, getter_AddRefs(firstRowEl));
704 nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl));
706 if (firstRowContent) {
707 nsRefPtr<nsStyleContext> styleContext;
708 nsPresContext *presContext = aBoxLayoutState.PresContext();
709 styleContext = presContext->StyleSet()->
710 ResolveStyleFor(firstRowContent->AsElement(), nullptr);
712 nscoord width = 0;
713 nsMargin margin(0,0,0,0);
715 if (styleContext->StylePadding()->GetPadding(margin))
716 width += margin.LeftRight();
717 width += styleContext->StyleBorder()->GetComputedBorder().LeftRight();
718 if (styleContext->StyleMargin()->GetMargin(margin))
719 width += margin.LeftRight();
721 FlattenedChildIterator iter(mContent);
722 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
723 if (child->Tag() == nsGkAtoms::listitem) {
724 nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
725 if (rendContext) {
726 nsAutoString value;
727 uint32_t textCount = child->GetChildCount();
728 for (uint32_t j = 0; j < textCount; ++j) {
729 nsIContent* text = child->GetChildAt(j);
730 if (text && text->IsNodeOfType(nsINode::eTEXT)) {
731 text->AppendTextTo(value);
735 nsRefPtr<nsFontMetrics> fm;
736 nsLayoutUtils::GetFontMetricsForStyleContext(styleContext,
737 getter_AddRefs(fm));
738 rendContext->SetFont(fm);
740 nscoord textWidth =
741 nsLayoutUtils::GetStringWidth(this, rendContext, value.get(), value.Length());
742 textWidth += width;
744 if (textWidth > largestWidth)
745 largestWidth = textWidth;
751 mStringWidth = largestWidth;
752 return mStringWidth;
755 void
756 nsListBoxBodyFrame::ComputeTotalRowCount()
758 mRowCount = 0;
759 FlattenedChildIterator iter(mContent);
760 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
761 if (child->Tag() == nsGkAtoms::listitem) {
762 ++mRowCount;
767 void
768 nsListBoxBodyFrame::PostReflowCallback()
770 if (!mReflowCallbackPosted) {
771 mReflowCallbackPosted = true;
772 PresContext()->PresShell()->PostReflowCallback(this);
776 ////////// scrolling
778 nsresult
779 nsListBoxBodyFrame::ScrollToIndex(int32_t aRowIndex)
781 if (( aRowIndex < 0 ) || (mRowHeight == 0))
782 return NS_OK;
784 int32_t newIndex = aRowIndex;
785 int32_t delta = mCurrentIndex > newIndex ? mCurrentIndex - newIndex : newIndex - mCurrentIndex;
786 bool up = newIndex < mCurrentIndex;
788 // Check to be sure we're not scrolling off the bottom of the tree
789 int32_t lastPageTopRow = GetRowCount() - (GetAvailableHeight() / mRowHeight);
790 if (lastPageTopRow < 0)
791 lastPageTopRow = 0;
793 if (aRowIndex > lastPageTopRow)
794 return NS_OK;
796 mCurrentIndex = newIndex;
798 nsWeakFrame weak(this);
800 // Since we're going to flush anyway, we need to not do this off an event
801 DoInternalPositionChangedSync(up, delta);
803 if (!weak.IsAlive()) {
804 return NS_OK;
807 // This change has to happen immediately.
808 // Flush any pending reflow commands.
809 // XXXbz why, exactly?
810 mContent->GetComposedDoc()->FlushPendingNotifications(Flush_Layout);
812 return NS_OK;
815 nsresult
816 nsListBoxBodyFrame::InternalPositionChangedCallback()
818 nsListScrollSmoother* smoother = GetSmoother();
820 if (smoother->mDelta == 0)
821 return NS_OK;
823 mCurrentIndex += smoother->mDelta;
825 if (mCurrentIndex < 0)
826 mCurrentIndex = 0;
828 return DoInternalPositionChangedSync(smoother->mDelta < 0,
829 smoother->mDelta < 0 ?
830 -smoother->mDelta : smoother->mDelta);
833 nsresult
834 nsListBoxBodyFrame::InternalPositionChanged(bool aUp, int32_t aDelta)
836 nsRefPtr<nsPositionChangedEvent> ev =
837 new nsPositionChangedEvent(this, aUp, aDelta);
838 nsresult rv = NS_DispatchToCurrentThread(ev);
839 if (NS_SUCCEEDED(rv)) {
840 if (!mPendingPositionChangeEvents.AppendElement(ev)) {
841 rv = NS_ERROR_OUT_OF_MEMORY;
842 ev->Revoke();
845 return rv;
848 nsresult
849 nsListBoxBodyFrame::DoInternalPositionChangedSync(bool aUp, int32_t aDelta)
851 nsWeakFrame weak(this);
853 // Process all the pending position changes first
854 nsTArray< nsRefPtr<nsPositionChangedEvent> > temp;
855 temp.SwapElements(mPendingPositionChangeEvents);
856 for (uint32_t i = 0; i < temp.Length(); ++i) {
857 if (weak.IsAlive()) {
858 temp[i]->Run();
860 temp[i]->Revoke();
863 if (!weak.IsAlive()) {
864 return NS_OK;
867 return DoInternalPositionChanged(aUp, aDelta);
870 nsresult
871 nsListBoxBodyFrame::DoInternalPositionChanged(bool aUp, int32_t aDelta)
873 if (aDelta == 0)
874 return NS_OK;
876 nsRefPtr<nsPresContext> presContext(PresContext());
877 nsBoxLayoutState state(presContext);
879 // begin timing how long it takes to scroll a row
880 PRTime start = PR_Now();
882 nsWeakFrame weakThis(this);
883 mContent->GetComposedDoc()->FlushPendingNotifications(Flush_Layout);
884 if (!weakThis.IsAlive()) {
885 return NS_OK;
889 nsAutoScriptBlocker scriptBlocker;
891 int32_t visibleRows = 0;
892 if (mRowHeight)
893 visibleRows = GetAvailableHeight()/mRowHeight;
895 if (aDelta < visibleRows) {
896 int32_t loseRows = aDelta;
897 if (aUp) {
898 // scrolling up, destroy rows from the bottom downwards
899 ReverseDestroyRows(loseRows);
900 mRowsToPrepend += aDelta;
901 mLinkupFrame = nullptr;
903 else {
904 // scrolling down, destroy rows from the top upwards
905 DestroyRows(loseRows);
906 mRowsToPrepend = 0;
909 else {
910 // We have scrolled so much that all of our current frames will
911 // go off screen, so blow them all away. Weeee!
912 nsIFrame *currBox = mFrames.FirstChild();
913 nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
914 fc->BeginUpdate();
915 while (currBox) {
916 nsIFrame *nextBox = currBox->GetNextSibling();
917 RemoveChildFrame(state, currBox);
918 currBox = nextBox;
920 fc->EndUpdate();
923 // clear frame markers so that CreateRows will re-create
924 mTopFrame = mBottomFrame = nullptr;
926 mYPosition = mCurrentIndex*mRowHeight;
927 mScrolling = true;
928 presContext->PresShell()->
929 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_HAS_DIRTY_CHILDREN);
931 if (!weakThis.IsAlive()) {
932 return NS_OK;
934 // Flush calls CreateRows
935 // XXXbz there has to be a better way to do this than flushing!
936 presContext->PresShell()->FlushPendingNotifications(Flush_Layout);
937 if (!weakThis.IsAlive()) {
938 return NS_OK;
941 mScrolling = false;
943 VerticalScroll(mYPosition);
945 PRTime end = PR_Now();
947 int32_t newTime = int32_t(end - start) / aDelta;
949 // average old and new
950 mTimePerRow = (newTime + mTimePerRow)/2;
952 return NS_OK;
955 nsListScrollSmoother*
956 nsListBoxBodyFrame::GetSmoother()
958 if (!mScrollSmoother) {
959 mScrollSmoother = new nsListScrollSmoother(this);
960 NS_ASSERTION(mScrollSmoother, "out of memory");
961 NS_IF_ADDREF(mScrollSmoother);
964 return mScrollSmoother;
967 void
968 nsListBoxBodyFrame::VerticalScroll(int32_t aPosition)
970 nsIScrollableFrame* scrollFrame
971 = nsLayoutUtils::GetScrollableFrameFor(this);
972 if (!scrollFrame) {
973 return;
976 nsPoint scrollPosition = scrollFrame->GetScrollPosition();
978 nsWeakFrame weakFrame(this);
979 scrollFrame->ScrollTo(nsPoint(scrollPosition.x, aPosition),
980 nsIScrollableFrame::INSTANT);
981 if (!weakFrame.IsAlive()) {
982 return;
985 mYPosition = aPosition;
988 ////////// frame and box retrieval
990 nsIFrame*
991 nsListBoxBodyFrame::GetFirstFrame()
993 mTopFrame = mFrames.FirstChild();
994 return mTopFrame;
997 nsIFrame*
998 nsListBoxBodyFrame::GetLastFrame()
1000 return mFrames.LastChild();
1003 bool
1004 nsListBoxBodyFrame::SupportsOrdinalsInChildren()
1006 return false;
1009 ////////// lazy row creation and destruction
1011 void
1012 nsListBoxBodyFrame::CreateRows()
1014 // Get our client rect.
1015 nsRect clientRect;
1016 GetClientRect(clientRect);
1018 // Get the starting y position and the remaining available
1019 // height.
1020 nscoord availableHeight = GetAvailableHeight();
1022 if (availableHeight <= 0) {
1023 bool fixed = (GetFixedRowSize() != -1);
1024 if (fixed)
1025 availableHeight = 10;
1026 else
1027 return;
1030 // get the first tree box. If there isn't one create one.
1031 bool created = false;
1032 nsIFrame* box = GetFirstItemBox(0, &created);
1033 nscoord rowHeight = GetRowHeightAppUnits();
1034 while (box) {
1035 if (created && mRowsToPrepend > 0)
1036 --mRowsToPrepend;
1038 // if the row height is 0 then fail. Wait until someone
1039 // laid out and sets the row height.
1040 if (rowHeight == 0)
1041 return;
1043 availableHeight -= rowHeight;
1045 // should we continue? Is the enought height?
1046 if (!ContinueReflow(availableHeight))
1047 break;
1049 // get the next tree box. Create one if needed.
1050 box = GetNextItemBox(box, 0, &created);
1053 mRowsToPrepend = 0;
1054 mLinkupFrame = nullptr;
1057 void
1058 nsListBoxBodyFrame::DestroyRows(int32_t& aRowsToLose)
1060 // We need to destroy frames until our row count has been properly
1061 // reduced. A reflow will then pick up and create the new frames.
1062 nsIFrame* childFrame = GetFirstFrame();
1063 nsBoxLayoutState state(PresContext());
1065 nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
1066 fc->BeginUpdate();
1067 while (childFrame && aRowsToLose > 0) {
1068 --aRowsToLose;
1070 nsIFrame* nextFrame = childFrame->GetNextSibling();
1071 RemoveChildFrame(state, childFrame);
1073 mTopFrame = childFrame = nextFrame;
1075 fc->EndUpdate();
1077 PresContext()->PresShell()->
1078 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1079 NS_FRAME_HAS_DIRTY_CHILDREN);
1082 void
1083 nsListBoxBodyFrame::ReverseDestroyRows(int32_t& aRowsToLose)
1085 // We need to destroy frames until our row count has been properly
1086 // reduced. A reflow will then pick up and create the new frames.
1087 nsIFrame* childFrame = GetLastFrame();
1088 nsBoxLayoutState state(PresContext());
1090 nsCSSFrameConstructor* fc = PresContext()->PresShell()->FrameConstructor();
1091 fc->BeginUpdate();
1092 while (childFrame && aRowsToLose > 0) {
1093 --aRowsToLose;
1095 nsIFrame* prevFrame;
1096 prevFrame = childFrame->GetPrevSibling();
1097 RemoveChildFrame(state, childFrame);
1099 mBottomFrame = childFrame = prevFrame;
1101 fc->EndUpdate();
1103 PresContext()->PresShell()->
1104 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1105 NS_FRAME_HAS_DIRTY_CHILDREN);
1108 static bool
1109 IsListItemChild(nsListBoxBodyFrame* aParent, nsIContent* aChild,
1110 nsIFrame** aChildFrame)
1112 *aChildFrame = nullptr;
1113 if (!aChild->IsXUL() || aChild->Tag() != nsGkAtoms::listitem) {
1114 return false;
1116 nsIFrame* existingFrame = aChild->GetPrimaryFrame();
1117 if (existingFrame && existingFrame->GetParent() != aParent) {
1118 return false;
1120 *aChildFrame = existingFrame;
1121 return true;
1125 // Get the nsIFrame for the first visible listitem, and if none exists,
1126 // create one.
1128 nsIFrame*
1129 nsListBoxBodyFrame::GetFirstItemBox(int32_t aOffset, bool* aCreated)
1131 if (aCreated)
1132 *aCreated = false;
1134 // Clear ourselves out.
1135 mBottomFrame = mTopFrame;
1137 if (mTopFrame) {
1138 return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
1141 // top frame was cleared out
1142 mTopFrame = GetFirstFrame();
1143 mBottomFrame = mTopFrame;
1145 if (mTopFrame && mRowsToPrepend <= 0) {
1146 return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
1149 // At this point, we either have no frames at all,
1150 // or the user has scrolled upwards, leaving frames
1151 // to be created at the top. Let's determine which
1152 // content needs a new frame first.
1154 nsCOMPtr<nsIContent> startContent;
1155 if (mTopFrame && mRowsToPrepend > 0) {
1156 // We need to insert rows before the top frame
1157 nsIContent* topContent = mTopFrame->GetContent();
1158 nsIContent* topParent = topContent->GetParent();
1159 int32_t contentIndex = topParent->IndexOf(topContent);
1160 contentIndex -= aOffset;
1161 if (contentIndex < 0)
1162 return nullptr;
1163 startContent = topParent->GetChildAt(contentIndex - mRowsToPrepend);
1164 } else {
1165 // This will be the first item frame we create. Use the content
1166 // at the current index, which is the first index scrolled into view
1167 GetListItemContentAt(mCurrentIndex+aOffset, getter_AddRefs(startContent));
1170 if (startContent) {
1171 nsIFrame* existingFrame;
1172 if (!IsListItemChild(this, startContent, &existingFrame)) {
1173 return GetFirstItemBox(++aOffset, aCreated);
1175 if (existingFrame) {
1176 return existingFrame->IsBoxFrame() ? existingFrame : nullptr;
1179 // Either append the new frame, or prepend it (at index 0)
1180 // XXX check here if frame was even created, it may not have been if
1181 // display: none was on listitem content
1182 bool isAppend = mRowsToPrepend <= 0;
1184 nsPresContext* presContext = PresContext();
1185 nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
1186 nsIFrame* topFrame = nullptr;
1187 fc->CreateListBoxContent(presContext, this, nullptr, startContent,
1188 &topFrame, isAppend, false, nullptr);
1189 mTopFrame = topFrame;
1190 if (mTopFrame) {
1191 if (aCreated)
1192 *aCreated = true;
1194 mBottomFrame = mTopFrame;
1196 return mTopFrame->IsBoxFrame() ? mTopFrame : nullptr;
1197 } else
1198 return GetFirstItemBox(++aOffset, 0);
1201 return nullptr;
1205 // Get the nsIFrame for the next visible listitem after aBox, and if none
1206 // exists, create one.
1208 nsIFrame*
1209 nsListBoxBodyFrame::GetNextItemBox(nsIFrame* aBox, int32_t aOffset,
1210 bool* aCreated)
1212 if (aCreated)
1213 *aCreated = false;
1215 nsIFrame* result = aBox->GetNextSibling();
1217 if (!result || result == mLinkupFrame || mRowsToPrepend > 0) {
1218 // No result found. See if there's a content node that wants a frame.
1219 nsIContent* prevContent = aBox->GetContent();
1220 nsIContent* parentContent = prevContent->GetParent();
1222 int32_t i = parentContent->IndexOf(prevContent);
1224 uint32_t childCount = parentContent->GetChildCount();
1225 if (((uint32_t)i + aOffset + 1) < childCount) {
1226 // There is a content node that wants a frame.
1227 nsIContent *nextContent = parentContent->GetChildAt(i + aOffset + 1);
1229 nsIFrame* existingFrame;
1230 if (!IsListItemChild(this, nextContent, &existingFrame)) {
1231 return GetNextItemBox(aBox, ++aOffset, aCreated);
1233 if (!existingFrame) {
1234 // Either append the new frame, or insert it after the current frame
1235 bool isAppend = result != mLinkupFrame && mRowsToPrepend <= 0;
1236 nsIFrame* prevFrame = isAppend ? nullptr : aBox;
1238 nsPresContext* presContext = PresContext();
1239 nsCSSFrameConstructor* fc = presContext->PresShell()->FrameConstructor();
1240 fc->CreateListBoxContent(presContext, this, prevFrame, nextContent,
1241 &result, isAppend, false, nullptr);
1243 if (result) {
1244 if (aCreated)
1245 *aCreated = true;
1246 } else
1247 return GetNextItemBox(aBox, ++aOffset, aCreated);
1248 } else {
1249 result = existingFrame;
1252 mLinkupFrame = nullptr;
1256 if (!result)
1257 return nullptr;
1259 mBottomFrame = result;
1261 NS_ASSERTION(!result->IsBoxFrame() || result->GetParent() == this,
1262 "returning frame that is not in childlist");
1264 return result->IsBoxFrame() ? result : nullptr;
1267 bool
1268 nsListBoxBodyFrame::ContinueReflow(nscoord height)
1270 #ifdef ACCESSIBILITY
1271 if (nsIPresShell::IsAccessibilityActive()) {
1272 // Create all the frames at once so screen readers and
1273 // onscreen keyboards can see the full list right away
1274 return true;
1276 #endif
1278 if (height <= 0) {
1279 nsIFrame* lastChild = GetLastFrame();
1280 nsIFrame* startingPoint = mBottomFrame;
1281 if (startingPoint == nullptr) {
1282 // We just want to delete everything but the first item.
1283 startingPoint = GetFirstFrame();
1286 if (lastChild != startingPoint) {
1287 // We have some hangers on (probably caused by shrinking the size of the window).
1288 // Nuke them.
1289 nsIFrame* currFrame = startingPoint->GetNextSibling();
1290 nsBoxLayoutState state(PresContext());
1292 nsCSSFrameConstructor* fc =
1293 PresContext()->PresShell()->FrameConstructor();
1294 fc->BeginUpdate();
1295 while (currFrame) {
1296 nsIFrame* nextFrame = currFrame->GetNextSibling();
1297 RemoveChildFrame(state, currFrame);
1298 currFrame = nextFrame;
1300 fc->EndUpdate();
1302 PresContext()->PresShell()->
1303 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1304 NS_FRAME_HAS_DIRTY_CHILDREN);
1306 return false;
1308 else
1309 return true;
1312 NS_IMETHODIMP
1313 nsListBoxBodyFrame::ListBoxAppendFrames(nsFrameList& aFrameList)
1315 // append them after
1316 nsBoxLayoutState state(PresContext());
1317 const nsFrameList::Slice& newFrames = mFrames.AppendFrames(nullptr, aFrameList);
1318 if (mLayoutManager)
1319 mLayoutManager->ChildrenAppended(this, state, newFrames);
1320 PresContext()->PresShell()->
1321 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1322 NS_FRAME_HAS_DIRTY_CHILDREN);
1324 return NS_OK;
1327 NS_IMETHODIMP
1328 nsListBoxBodyFrame::ListBoxInsertFrames(nsIFrame* aPrevFrame,
1329 nsFrameList& aFrameList)
1331 // insert the frames to our info list
1332 nsBoxLayoutState state(PresContext());
1333 const nsFrameList::Slice& newFrames =
1334 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
1335 if (mLayoutManager)
1336 mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
1337 PresContext()->PresShell()->
1338 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1339 NS_FRAME_HAS_DIRTY_CHILDREN);
1341 return NS_OK;
1345 // Called by nsCSSFrameConstructor when a new listitem content is inserted.
1347 void
1348 nsListBoxBodyFrame::OnContentInserted(nsPresContext* aPresContext, nsIContent* aChildContent)
1350 if (mRowCount >= 0)
1351 ++mRowCount;
1353 // The RDF content builder will build content nodes such that they are all
1354 // ready when OnContentInserted is first called, meaning the first call
1355 // to CreateRows will create all the frames, but OnContentInserted will
1356 // still be called again for each content node - so we need to make sure
1357 // that the frame for each content node hasn't already been created.
1358 nsIFrame* childFrame = aChildContent->GetPrimaryFrame();
1359 if (childFrame)
1360 return;
1362 int32_t siblingIndex;
1363 nsCOMPtr<nsIContent> nextSiblingContent;
1364 GetListItemNextSibling(aChildContent, getter_AddRefs(nextSiblingContent), siblingIndex);
1366 // if we're inserting our item before the first visible content,
1367 // then we need to shift all rows down by one
1368 if (siblingIndex >= 0 && siblingIndex-1 <= mCurrentIndex) {
1369 mTopFrame = nullptr;
1370 mRowsToPrepend = 1;
1371 } else if (nextSiblingContent) {
1372 // we may be inserting before a frame that is on screen
1373 nsIFrame* nextSiblingFrame = nextSiblingContent->GetPrimaryFrame();
1374 mLinkupFrame = nextSiblingFrame;
1377 CreateRows();
1378 PresContext()->PresShell()->
1379 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1380 NS_FRAME_HAS_DIRTY_CHILDREN);
1384 // Called by nsCSSFrameConstructor when listitem content is removed.
1386 void
1387 nsListBoxBodyFrame::OnContentRemoved(nsPresContext* aPresContext,
1388 nsIContent* aContainer,
1389 nsIFrame* aChildFrame,
1390 nsIContent* aOldNextSibling)
1392 NS_ASSERTION(!aChildFrame || aChildFrame->GetParent() == this,
1393 "Removing frame that's not our child... Not good");
1395 if (mRowCount >= 0)
1396 --mRowCount;
1398 if (aContainer) {
1399 if (!aChildFrame) {
1400 // The row we are removing is out of view, so we need to try to
1401 // determine the index of its next sibling.
1402 int32_t siblingIndex = -1;
1403 if (aOldNextSibling) {
1404 nsCOMPtr<nsIContent> nextSiblingContent;
1405 GetListItemNextSibling(aOldNextSibling,
1406 getter_AddRefs(nextSiblingContent),
1407 siblingIndex);
1410 // if the row being removed is off-screen and above the top frame, we need to
1411 // adjust our top index and tell the scrollbar to shift up one row.
1412 if (siblingIndex >= 0 && siblingIndex-1 < mCurrentIndex) {
1413 NS_PRECONDITION(mCurrentIndex > 0, "mCurrentIndex > 0");
1414 --mCurrentIndex;
1415 mYPosition = mCurrentIndex*mRowHeight;
1416 nsWeakFrame weakChildFrame(aChildFrame);
1417 VerticalScroll(mYPosition);
1418 if (!weakChildFrame.IsAlive()) {
1419 return;
1422 } else if (mCurrentIndex > 0) {
1423 // At this point, we know we have a scrollbar, and we need to know
1424 // if we are scrolled to the last row. In this case, the behavior
1425 // of the scrollbar is to stay locked to the bottom. Since we are
1426 // removing visible content, the first visible row will have to move
1427 // down by one, and we will have to insert a new frame at the top.
1429 // if the last content node has a frame, we are scrolled to the bottom
1430 nsIContent* lastChild = nullptr;
1431 FlattenedChildIterator iter(mContent);
1432 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
1433 lastChild = child;
1436 if (lastChild) {
1437 nsIFrame* lastChildFrame = lastChild->GetPrimaryFrame();
1439 if (lastChildFrame) {
1440 mTopFrame = nullptr;
1441 mRowsToPrepend = 1;
1442 --mCurrentIndex;
1443 mYPosition = mCurrentIndex*mRowHeight;
1444 nsWeakFrame weakChildFrame(aChildFrame);
1445 VerticalScroll(mYPosition);
1446 if (!weakChildFrame.IsAlive()) {
1447 return;
1454 // if we're removing the top row, the new top row is the next row
1455 if (mTopFrame && mTopFrame == aChildFrame)
1456 mTopFrame = mTopFrame->GetNextSibling();
1458 // Go ahead and delete the frame.
1459 nsBoxLayoutState state(aPresContext);
1460 if (aChildFrame) {
1461 RemoveChildFrame(state, aChildFrame);
1464 PresContext()->PresShell()->
1465 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1466 NS_FRAME_HAS_DIRTY_CHILDREN);
1469 void
1470 nsListBoxBodyFrame::GetListItemContentAt(int32_t aIndex, nsIContent** aContent)
1472 *aContent = nullptr;
1474 int32_t itemsFound = 0;
1475 FlattenedChildIterator iter(mContent);
1476 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
1477 if (child->Tag() == nsGkAtoms::listitem) {
1478 ++itemsFound;
1479 if (itemsFound-1 == aIndex) {
1480 *aContent = child;
1481 NS_IF_ADDREF(*aContent);
1482 return;
1488 void
1489 nsListBoxBodyFrame::GetListItemNextSibling(nsIContent* aListItem, nsIContent** aContent, int32_t& aSiblingIndex)
1491 *aContent = nullptr;
1492 aSiblingIndex = -1;
1493 nsIContent *prevKid = nullptr;
1494 FlattenedChildIterator iter(mContent);
1495 for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
1496 if (child->Tag() == nsGkAtoms::listitem) {
1497 ++aSiblingIndex;
1498 if (prevKid == aListItem) {
1499 *aContent = child;
1500 NS_IF_ADDREF(*aContent);
1501 return;
1504 prevKid = child;
1507 aSiblingIndex = -1; // no match, so there is no next sibling
1510 void
1511 nsListBoxBodyFrame::RemoveChildFrame(nsBoxLayoutState &aState,
1512 nsIFrame *aFrame)
1514 MOZ_ASSERT(mFrames.ContainsFrame(aFrame));
1515 MOZ_ASSERT(aFrame != GetContentInsertionFrame());
1517 #ifdef ACCESSIBILITY
1518 nsAccessibilityService* accService = nsIPresShell::AccService();
1519 if (accService) {
1520 nsIContent* content = aFrame->GetContent();
1521 accService->ContentRemoved(PresContext()->PresShell(), content->GetParent(),
1522 content);
1524 #endif
1526 mFrames.RemoveFrame(aFrame);
1527 if (mLayoutManager)
1528 mLayoutManager->ChildrenRemoved(this, aState, aFrame);
1529 aFrame->Destroy();
1532 // Creation Routines ///////////////////////////////////////////////////////////////////////
1534 already_AddRefed<nsBoxLayout> NS_NewListBoxLayout();
1536 nsIFrame*
1537 NS_NewListBoxBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1539 nsCOMPtr<nsBoxLayout> layout = NS_NewListBoxLayout();
1540 return new (aPresShell) nsListBoxBodyFrame(aPresShell, aContext, layout);
1543 NS_IMPL_FRAMEARENA_HELPERS(nsListBoxBodyFrame)