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