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 "nsComboboxControlFrame.h"
9 #include "mozilla/gfx/2D.h"
10 #include "mozilla/gfx/PathHelpers.h"
12 #include "nsFocusManager.h"
13 #include "nsFormControlFrame.h"
14 #include "nsGkAtoms.h"
15 #include "nsCSSAnonBoxes.h"
16 #include "nsHTMLParts.h"
17 #include "nsIFormControl.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsIListControlFrame.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsIPresShell.h"
22 #include "nsContentList.h"
24 #include "nsViewManager.h"
25 #include "nsIDOMEventListener.h"
26 #include "nsIDOMNode.h"
27 #include "nsISelectControlFrame.h"
28 #include "nsContentUtils.h"
29 #include "nsIDocument.h"
30 #include "nsIScrollableFrame.h"
31 #include "nsListControlFrame.h"
32 #include "nsAutoPtr.h"
33 #include "nsStyleSet.h"
34 #include "nsNodeInfoManager.h"
35 #include "nsContentCreatorFunctions.h"
36 #include "nsLayoutUtils.h"
37 #include "nsDisplayList.h"
39 #include "nsThemeConstants.h"
40 #include "nsRenderingContext.h"
41 #include "mozilla/Likely.h"
43 #include "nsTextNode.h"
44 #include "mozilla/AsyncEventDispatcher.h"
45 #include "mozilla/EventStates.h"
46 #include "mozilla/LookAndFeel.h"
47 #include "mozilla/MouseEvents.h"
48 #include "mozilla/unused.h"
50 using namespace mozilla
;
51 using namespace mozilla::gfx
;
54 nsComboboxControlFrame::RedisplayTextEvent::Run()
57 mControlFrame
->HandleRedisplayTextEvent();
63 #define FIX_FOR_BUG_53259
65 // Drop down list event management.
66 // The combo box uses the following strategy for managing the drop-down list.
67 // If the combo box or its arrow button is clicked on the drop-down list is displayed
68 // If mouse exits the combo box with the drop-down list displayed the drop-down list
69 // is asked to capture events
70 // The drop-down list will capture all events including mouse down and up and will always
71 // return with ListWasSelected method call regardless of whether an item in the list was
73 // The ListWasSelected code will turn off mouse-capture for the drop-down list.
74 // The drop-down list does not explicitly set capture when it is in the drop-down mode.
78 * Helper class that listens to the combo boxes button. If the button is pressed the
79 * combo box is toggled to open or close. this is used by Accessibility which presses
80 * that button Programmatically.
82 class nsComboButtonListener
: public nsIDOMEventListener
85 virtual ~nsComboButtonListener() {}
90 NS_IMETHOD
HandleEvent(nsIDOMEvent
*) MOZ_OVERRIDE
92 mComboBox
->ShowDropDown(!mComboBox
->IsDroppedDown());
96 explicit nsComboButtonListener(nsComboboxControlFrame
* aCombobox
)
98 mComboBox
= aCombobox
;
101 nsComboboxControlFrame
* mComboBox
;
104 NS_IMPL_ISUPPORTS(nsComboButtonListener
,
107 // static class data member for Bug 32920
108 nsComboboxControlFrame
* nsComboboxControlFrame::sFocused
= nullptr;
111 NS_NewComboboxControlFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, nsFrameState aStateFlags
)
113 nsComboboxControlFrame
* it
= new (aPresShell
) nsComboboxControlFrame(aContext
);
116 // set the state flags (if any are provided)
117 it
->AddStateBits(aStateFlags
);
123 NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame
)
125 //-----------------------------------------------------------
126 // Reflow Debugging Macros
127 // These let us "see" how many reflow counts are happening
128 //-----------------------------------------------------------
129 #ifdef DO_REFLOW_COUNTER
131 #define MAX_REFLOW_CNT 1024
132 static int32_t gTotalReqs
= 0;;
133 static int32_t gTotalReflows
= 0;;
134 static int32_t gReflowControlCntRQ
[MAX_REFLOW_CNT
];
135 static int32_t gReflowControlCnt
[MAX_REFLOW_CNT
];
136 static int32_t gReflowInx
= -1;
138 #define REFLOW_COUNTER() \
139 if (mReflowId > -1) \
140 gReflowControlCnt[mReflowId]++;
142 #define REFLOW_COUNTER_REQUEST() \
143 if (mReflowId > -1) \
144 gReflowControlCntRQ[mReflowId]++;
146 #define REFLOW_COUNTER_DUMP(__desc) \
147 if (mReflowId > -1) {\
148 gTotalReqs += gReflowControlCntRQ[mReflowId];\
149 gTotalReflows += gReflowControlCnt[mReflowId];\
150 printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \
151 mReflowId, (__desc), \
152 gReflowControlCnt[mReflowId], \
153 gReflowControlCntRQ[mReflowId],\
154 gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\
157 #define REFLOW_COUNTER_INIT() \
158 if (gReflowInx < MAX_REFLOW_CNT) { \
160 mReflowId = gReflowInx; \
161 gReflowControlCnt[mReflowId] = 0; \
162 gReflowControlCntRQ[mReflowId] = 0; \
168 #define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
169 #define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
170 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
171 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
173 #else //-------------
175 #define REFLOW_COUNTER_REQUEST()
176 #define REFLOW_COUNTER()
177 #define REFLOW_COUNTER_DUMP(__desc)
178 #define REFLOW_COUNTER_INIT()
180 #define REFLOW_DEBUG_MSG(_msg)
181 #define REFLOW_DEBUG_MSG2(_msg1, _msg2)
182 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3)
183 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4)
188 //------------------------------------------
189 // This is for being VERY noisy
190 //------------------------------------------
192 #define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
193 #define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
194 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
195 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
197 #define REFLOW_NOISY_MSG(_msg)
198 #define REFLOW_NOISY_MSG2(_msg1, _msg2)
199 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3)
200 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4)
203 //------------------------------------------
204 // Displays value in pixels or twips
205 //------------------------------------------
207 #define PX(__v) __v / 15
212 //------------------------------------------------------
213 //-- Done with macros
214 //------------------------------------------------------
216 nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext
* aContext
)
217 : nsBlockFrame(aContext
)
218 , mDisplayFrame(nullptr)
219 , mButtonFrame(nullptr)
220 , mDropdownFrame(nullptr)
221 , mListControlFrame(nullptr)
223 , mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX
)
224 , mDisplayedIndex(-1)
225 , mLastDropDownAboveScreenY(nscoord_MIN
)
226 , mLastDropDownBelowScreenY(nscoord_MIN
)
227 , mDroppedDown(false)
228 , mInRedisplayText(false)
229 , mDelayedShowDropDown(false)
231 REFLOW_COUNTER_INIT()
234 //--------------------------------------------------------------
235 nsComboboxControlFrame::~nsComboboxControlFrame()
237 REFLOW_COUNTER_DUMP("nsCCF");
240 //--------------------------------------------------------------
242 NS_QUERYFRAME_HEAD(nsComboboxControlFrame
)
243 NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame
)
244 NS_QUERYFRAME_ENTRY(nsIFormControlFrame
)
245 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
246 NS_QUERYFRAME_ENTRY(nsISelectControlFrame
)
247 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
248 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
252 nsComboboxControlFrame::AccessibleType()
254 return a11y::eHTMLComboboxType
;
259 nsComboboxControlFrame::SetFocus(bool aOn
, bool aRepaint
)
261 nsWeakFrame
weakFrame(this);
263 nsListControlFrame::ComboboxFocusSet();
265 if (mDelayedShowDropDown
) {
266 ShowDropDown(true); // might destroy us
267 if (!weakFrame
.IsAlive()) {
273 mDelayedShowDropDown
= false;
275 mListControlFrame
->ComboboxFinish(mDisplayedIndex
); // might destroy us
276 if (!weakFrame
.IsAlive()) {
280 // May delete |this|.
281 mListControlFrame
->FireOnChange();
284 if (!weakFrame
.IsAlive()) {
288 // This is needed on a temporary basis. It causes the focus
289 // rect to be drawn. This is much faster than ReResolvingStyle
295 nsComboboxControlFrame::ShowPopup(bool aShowPopup
)
297 nsView
* view
= mDropdownFrame
->GetView();
298 nsViewManager
* viewManager
= view
->GetViewManager();
301 nsRect rect
= mDropdownFrame
->GetRect();
303 viewManager
->ResizeView(view
, rect
);
304 viewManager
->SetViewVisibility(view
, nsViewVisibility_kShow
);
306 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
307 nsRect
emptyRect(0, 0, 0, 0);
308 viewManager
->ResizeView(view
, emptyRect
);
311 // fire a popup dom event
312 nsEventStatus status
= nsEventStatus_eIgnore
;
313 WidgetMouseEvent
event(true, aShowPopup
?
314 NS_XUL_POPUP_SHOWING
: NS_XUL_POPUP_HIDING
, nullptr,
315 WidgetMouseEvent::eReal
);
317 nsCOMPtr
<nsIPresShell
> shell
= PresContext()->GetPresShell();
319 shell
->HandleDOMEventWithTarget(mContent
, &event
, &status
);
323 nsComboboxControlFrame::ShowList(bool aShowList
)
325 nsView
* view
= mDropdownFrame
->GetView();
327 NS_ASSERTION(!view
->HasWidget(),
328 "We shouldn't have a widget before we need to display the popup");
330 // Create the widget for the drop-down list
331 view
->GetViewManager()->SetViewFloating(view
, true);
333 nsWidgetInitData widgetData
;
334 widgetData
.mWindowType
= eWindowType_popup
;
335 widgetData
.mBorderStyle
= eBorderStyle_default
;
336 view
->CreateWidgetForPopup(&widgetData
);
338 nsIWidget
* widget
= view
->GetWidget();
340 // We must do this before ShowPopup in case it destroys us (bug 813442).
341 widget
->CaptureRollupEvents(this, false);
345 nsWeakFrame
weakFrame(this);
346 ShowPopup(aShowList
); // might destroy us
347 if (!weakFrame
.IsAlive()) {
351 mDroppedDown
= aShowList
;
352 nsIWidget
* widget
= view
->GetWidget();
354 // The listcontrol frame will call back to the nsComboboxControlFrame's
355 // ListWasSelected which will stop the capture.
356 mListControlFrame
->AboutToDropDown();
357 mListControlFrame
->CaptureMouseEvents(true);
359 widget
->CaptureRollupEvents(this, true);
363 view
->DestroyWidget();
367 return weakFrame
.IsAlive();
370 class nsResizeDropdownAtFinalPosition MOZ_FINAL
371 : public nsIReflowCallback
, public nsRunnable
374 explicit nsResizeDropdownAtFinalPosition(nsComboboxControlFrame
* aFrame
)
377 MOZ_COUNT_CTOR(nsResizeDropdownAtFinalPosition
);
381 ~nsResizeDropdownAtFinalPosition()
383 MOZ_COUNT_DTOR(nsResizeDropdownAtFinalPosition
);
387 virtual bool ReflowFinished() MOZ_OVERRIDE
394 virtual void ReflowCallbackCanceled() MOZ_OVERRIDE
399 NS_IMETHODIMP
Run() MOZ_OVERRIDE
401 if (mFrame
.IsAlive()) {
402 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame())->
403 AbsolutelyPositionDropDown();
412 nsComboboxControlFrame::ReflowDropdown(nsPresContext
* aPresContext
,
413 const nsHTMLReflowState
& aReflowState
)
415 // All we want out of it later on, really, is the height of a row, so we
416 // don't even need to cache mDropdownFrame's ascent or anything. If we don't
417 // need to reflow it, just bail out here.
418 if (!aReflowState
.ShouldReflowAllKids() &&
419 !NS_SUBTREE_DIRTY(mDropdownFrame
)) {
423 // XXXbz this will, for small-height dropdowns, have extra space on the right
424 // edge for the scrollbar we don't show... but that's the best we can do here
426 WritingMode wm
= mDropdownFrame
->GetWritingMode();
427 LogicalSize availSize
= aReflowState
.AvailableSize(wm
);
428 availSize
.BSize(wm
) = NS_UNCONSTRAINEDSIZE
;
429 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, mDropdownFrame
,
432 // If the dropdown's intrinsic width is narrower than our specified width,
433 // then expand it out. We want our border-box width to end up the same as
434 // the dropdown's so account for both sets of mComputedBorderPadding.
435 nscoord forcedWidth
= aReflowState
.ComputedWidth() +
436 aReflowState
.ComputedPhysicalBorderPadding().LeftRight() -
437 kidReflowState
.ComputedPhysicalBorderPadding().LeftRight();
438 kidReflowState
.SetComputedWidth(std::max(kidReflowState
.ComputedWidth(),
441 // ensure we start off hidden
442 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
443 nsView
* view
= mDropdownFrame
->GetView();
444 nsViewManager
* viewManager
= view
->GetViewManager();
445 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
446 nsRect
emptyRect(0, 0, 0, 0);
447 viewManager
->ResizeView(view
, emptyRect
);
450 // Allow the child to move/size/change-visibility its view if it's currently
452 int32_t flags
= NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_VISIBILITY
| NS_FRAME_NO_SIZE_VIEW
;
456 nsRect rect
= mDropdownFrame
->GetRect();
457 nsHTMLReflowMetrics
desiredSize(aReflowState
);
458 nsReflowStatus ignoredStatus
;
459 ReflowChild(mDropdownFrame
, aPresContext
, desiredSize
,
460 kidReflowState
, rect
.x
, rect
.y
, flags
, ignoredStatus
);
462 // Set the child's width and height to its desired size
463 FinishReflowChild(mDropdownFrame
, aPresContext
, desiredSize
,
464 &kidReflowState
, rect
.x
, rect
.y
, flags
);
468 nsComboboxControlFrame::GetCSSTransformTranslation()
470 nsIFrame
* frame
= this;
471 bool is3DTransform
= false;
475 Matrix4x4 ctm
= frame
->GetTransformMatrix(nullptr, &parent
);
477 if (ctm
.Is2D(&matrix
)) {
478 transform
= transform
* matrix
;
480 is3DTransform
= true;
486 if (!is3DTransform
&& !transform
.HasNonTranslation()) {
487 nsPresContext
* pc
= PresContext();
488 // To get the translation introduced only by transforms we subtract the
489 // regular non-transform translation.
490 nsRootPresContext
* rootPC
= pc
->GetRootPresContext();
492 int32_t apd
= pc
->AppUnitsPerDevPixel();
493 translation
.x
= NSFloatPixelsToAppUnits(transform
._31
, apd
);
494 translation
.y
= NSFloatPixelsToAppUnits(transform
._32
, apd
);
495 translation
-= GetOffsetToCrossDoc(rootPC
->PresShell()->GetRootFrame());
501 class nsAsyncRollup
: public nsRunnable
504 explicit nsAsyncRollup(nsComboboxControlFrame
* aFrame
) : mFrame(aFrame
) {}
507 if (mFrame
.IsAlive()) {
508 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame())
516 class nsAsyncResize
: public nsRunnable
519 explicit nsAsyncResize(nsComboboxControlFrame
* aFrame
) : mFrame(aFrame
) {}
522 if (mFrame
.IsAlive()) {
523 nsComboboxControlFrame
* combo
=
524 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame());
525 static_cast<nsListControlFrame
*>(combo
->mDropdownFrame
)->
526 SetSuppressScrollbarUpdate(true);
527 nsCOMPtr
<nsIPresShell
> shell
= mFrame
->PresContext()->PresShell();
528 shell
->FrameNeedsReflow(combo
->mDropdownFrame
, nsIPresShell::eResize
,
530 shell
->FlushPendingNotifications(Flush_Layout
);
531 if (mFrame
.IsAlive()) {
532 combo
= static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame());
533 static_cast<nsListControlFrame
*>(combo
->mDropdownFrame
)->
534 SetSuppressScrollbarUpdate(false);
535 if (combo
->mDelayedShowDropDown
) {
536 combo
->ShowDropDown(true);
546 nsComboboxControlFrame::GetAvailableDropdownSpace(nscoord
* aAbove
,
548 nsPoint
* aTranslation
)
550 // Note: At first glance, it appears that you could simply get the absolute
551 // bounding box for the dropdown list by first getting its view, then getting
552 // the view's nsIWidget, then asking the nsIWidget for its AbsoluteBounds.
553 // The problem with this approach, is that the dropdown lists y location can
554 // change based on whether the dropdown is placed below or above the display
555 // frame. The approach, taken here is to get the absolute position of the
556 // display frame and use its location to determine if the dropdown will go
559 // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms.
560 // In the special case that our transform is only a 2D translation we
561 // introduce this hack so that the dropdown will show up in the right place.
562 *aTranslation
= GetCSSTransformTranslation();
566 nsRect screen
= nsFormControlFrame::GetUsableScreenRect(PresContext());
567 if (mLastDropDownBelowScreenY
== nscoord_MIN
) {
568 nsRect thisScreenRect
= GetScreenRectInAppUnits();
569 mLastDropDownBelowScreenY
= thisScreenRect
.YMost() + aTranslation
->y
;
570 mLastDropDownAboveScreenY
= thisScreenRect
.y
+ aTranslation
->y
;
574 nsPresContext
* pc
= PresContext()->GetToplevelContentDocumentPresContext();
575 nsIFrame
* root
= pc
? pc
->PresShell()->GetRootFrame() : nullptr;
577 minY
= root
->GetScreenRectInAppUnits().y
;
578 if (mLastDropDownBelowScreenY
< minY
) {
579 // Don't allow the drop-down to be placed above the content area.
586 nscoord below
= screen
.YMost() - mLastDropDownBelowScreenY
;
587 nscoord above
= mLastDropDownAboveScreenY
- minY
;
589 // If the difference between the space above and below is less
590 // than a row-height, then we favor the space below.
591 if (above
>= below
) {
592 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
593 nscoord rowHeight
= lcf
->GetHeightOfARow();
594 if (above
< below
+ rowHeight
) {
603 nsComboboxControlFrame::DropDownPositionState
604 nsComboboxControlFrame::AbsolutelyPositionDropDown()
607 nscoord above
, below
;
608 mLastDropDownBelowScreenY
= nscoord_MIN
;
609 GetAvailableDropdownSpace(&above
, &below
, &translation
);
610 if (above
<= 0 && below
<= 0) {
611 if (IsDroppedDown()) {
612 // Hide the view immediately to minimize flicker.
613 nsView
* view
= mDropdownFrame
->GetView();
614 view
->GetViewManager()->SetViewVisibility(view
, nsViewVisibility_kHide
);
615 NS_DispatchToCurrentThread(new nsAsyncRollup(this));
617 return eDropDownPositionSuppressed
;
620 nsSize dropdownSize
= mDropdownFrame
->GetSize();
621 nscoord height
= std::max(above
, below
);
622 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
623 if (height
< dropdownSize
.height
) {
624 if (lcf
->GetNumDisplayRows() > 1) {
625 // The drop-down doesn't fit and currently shows more than 1 row -
626 // schedule a resize to show fewer rows.
627 NS_DispatchToCurrentThread(new nsAsyncResize(this));
628 return eDropDownPositionPendingResize
;
630 } else if (height
> (dropdownSize
.height
+ lcf
->GetHeightOfARow() * 1.5) &&
631 lcf
->GetDropdownCanGrow()) {
632 // The drop-down fits but there is room for at least 1.5 more rows -
633 // schedule a resize to show more rows if it has more rows to show.
634 // (1.5 rows for good measure to avoid any rounding issues that would
635 // lead to a loop of reflow requests)
636 NS_DispatchToCurrentThread(new nsAsyncResize(this));
637 return eDropDownPositionPendingResize
;
640 // Position the drop-down below if there is room, otherwise place it above
641 // if there is room. If there is no room for it on either side then place
642 // it below (to avoid overlapping UI like the URL bar).
643 bool b
= dropdownSize
.height
<= below
|| dropdownSize
.height
> above
;
644 nsPoint
dropdownPosition(0, b
? GetRect().height
: -dropdownSize
.height
);
645 if (StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
646 // Align the right edge of the drop-down with the right edge of the control.
647 dropdownPosition
.x
= GetRect().width
- dropdownSize
.width
;
650 // Don't position the view unless the position changed since it might cause
651 // a call to NotifyGeometryChange() and an infinite loop here.
652 const nsPoint currentPos
= mDropdownFrame
->GetPosition();
653 const nsPoint newPos
= dropdownPosition
+ translation
;
654 if (currentPos
!= newPos
) {
655 mDropdownFrame
->SetPosition(newPos
);
656 nsContainerFrame::PositionFrameView(mDropdownFrame
);
658 return eDropDownPositionFinal
;
662 nsComboboxControlFrame::NotifyGeometryChange()
664 // We don't need to resize if we're not dropped down since ShowDropDown
665 // does that, or if we're dirty then the reflow callback does it,
666 // or if we have a delayed ShowDropDown pending.
667 if (IsDroppedDown() &&
668 !(GetStateBits() & NS_FRAME_IS_DIRTY
) &&
669 !mDelayedShowDropDown
) {
670 // Async because we're likely in a middle of a scroll here so
671 // frame/view positions are in flux.
672 nsRefPtr
<nsResizeDropdownAtFinalPosition
> resize
=
673 new nsResizeDropdownAtFinalPosition(this);
674 NS_DispatchToCurrentThread(resize
);
678 //----------------------------------------------------------
680 //----------------------------------------------------------
681 #ifdef DO_REFLOW_DEBUG
682 static int myCounter
= 0;
684 static void printSize(char * aDesc
, nscoord aSize
)
686 printf(" %s: ", aDesc
);
687 if (aSize
== NS_UNCONSTRAINEDSIZE
) {
690 printf("%d", PX(aSize
));
695 //-------------------------------------------------------------------
696 //-- Main Reflow for the Combobox
697 //-------------------------------------------------------------------
700 nsComboboxControlFrame::GetIntrinsicISize(nsRenderingContext
* aRenderingContext
,
701 nsLayoutUtils::IntrinsicISizeType aType
)
703 // get the scrollbar width, we'll use this later
704 nscoord scrollbarWidth
= 0;
705 nsPresContext
* presContext
= PresContext();
706 if (mListControlFrame
) {
707 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
708 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
709 scrollbarWidth
= scrollable
->GetNondisappearingScrollbarWidth(
710 presContext
, aRenderingContext
);
713 nscoord displayWidth
= 0;
714 if (MOZ_LIKELY(mDisplayFrame
)) {
715 displayWidth
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
720 if (mDropdownFrame
) {
721 nscoord dropdownContentWidth
;
722 bool isUsingOverlayScrollbars
=
723 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars
) != 0;
724 if (aType
== nsLayoutUtils::MIN_ISIZE
) {
725 dropdownContentWidth
= mDropdownFrame
->GetMinISize(aRenderingContext
);
726 if (isUsingOverlayScrollbars
) {
727 dropdownContentWidth
+= scrollbarWidth
;
730 NS_ASSERTION(aType
== nsLayoutUtils::PREF_ISIZE
, "Unexpected type");
731 dropdownContentWidth
= mDropdownFrame
->GetPrefISize(aRenderingContext
);
732 if (isUsingOverlayScrollbars
) {
733 dropdownContentWidth
+= scrollbarWidth
;
736 dropdownContentWidth
= NSCoordSaturatingSubtract(dropdownContentWidth
,
740 displayWidth
= std::max(dropdownContentWidth
, displayWidth
);
743 // add room for the dropmarker button if there is one
745 presContext
->GetTheme()->ThemeNeedsComboboxDropmarker()) &&
746 StyleDisplay()->mAppearance
!= NS_THEME_NONE
) {
747 displayWidth
+= scrollbarWidth
;
755 nsComboboxControlFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
758 DISPLAY_MIN_WIDTH(this, minWidth
);
759 minWidth
= GetIntrinsicISize(aRenderingContext
, nsLayoutUtils::MIN_ISIZE
);
764 nsComboboxControlFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
767 DISPLAY_PREF_WIDTH(this, prefWidth
);
768 prefWidth
= GetIntrinsicISize(aRenderingContext
, nsLayoutUtils::PREF_ISIZE
);
773 nsComboboxControlFrame::Reflow(nsPresContext
* aPresContext
,
774 nsHTMLReflowMetrics
& aDesiredSize
,
775 const nsHTMLReflowState
& aReflowState
,
776 nsReflowStatus
& aStatus
)
778 // Constraints we try to satisfy:
780 // 1) Default width of button is the vertical scrollbar size
781 // 2) If the width of button is bigger than our width, set width of
783 // 3) Default height of button is height of display area
784 // 4) Width of display area is whatever is left over from our width after
785 // allocating width for the button.
786 // 5) Height of display area is GetHeightOfARow() on the
787 // mListControlFrame.
789 if (!mDisplayFrame
|| !mButtonFrame
|| !mDropdownFrame
) {
790 NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!");
794 // Make sure the displayed text is the same as the selected option, bug 297389.
795 int32_t selectedIndex
;
796 nsAutoString selectedOptionText
;
798 selectedIndex
= mListControlFrame
->GetSelectedIndex();
801 // In dropped down mode the "selected index" is the hovered menu item,
802 // we want the last selected item which is |mDisplayedIndex| in this case.
803 selectedIndex
= mDisplayedIndex
;
805 if (selectedIndex
!= -1) {
806 mListControlFrame
->GetOptionText(selectedIndex
, selectedOptionText
);
808 if (mDisplayedOptionText
!= selectedOptionText
) {
809 RedisplayText(selectedIndex
);
812 // First reflow our dropdown so that we know how tall we should be.
813 ReflowDropdown(aPresContext
, aReflowState
);
814 nsRefPtr
<nsResizeDropdownAtFinalPosition
> resize
=
815 new nsResizeDropdownAtFinalPosition(this);
816 if (NS_SUCCEEDED(aPresContext
->PresShell()->PostReflowCallback(resize
))) {
817 // The reflow callback queue doesn't AddRef so we keep it alive until
818 // it's released in its ReflowFinished / ReflowCallbackCanceled.
819 unused
<< resize
.forget();
822 // Get the width of the vertical scrollbar. That will be the width of the
825 const nsStyleDisplay
*disp
= StyleDisplay();
826 if ((IsThemed(disp
) && !aPresContext
->GetTheme()->ThemeNeedsComboboxDropmarker()) ||
827 StyleDisplay()->mAppearance
== NS_THEME_NONE
) {
831 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
832 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
833 buttonWidth
= scrollable
->GetNondisappearingScrollbarWidth(
834 PresContext(), aReflowState
.rendContext
);
835 if (buttonWidth
> aReflowState
.ComputedWidth()) {
840 mDisplayWidth
= aReflowState
.ComputedWidth() - buttonWidth
;
842 nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, aReflowState
, aStatus
);
844 // The button should occupy the same space as a scrollbar
845 nsRect buttonRect
= mButtonFrame
->GetRect();
847 if (StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
848 buttonRect
.x
= aReflowState
.ComputedPhysicalBorderPadding().left
-
849 aReflowState
.ComputedPhysicalPadding().left
;
852 buttonRect
.x
= aReflowState
.ComputedPhysicalBorderPadding().LeftRight() +
854 (aReflowState
.ComputedPhysicalBorderPadding().right
-
855 aReflowState
.ComputedPhysicalPadding().right
);
857 buttonRect
.width
= buttonWidth
;
859 buttonRect
.y
= this->GetUsedBorder().top
;
860 buttonRect
.height
= mDisplayFrame
->GetRect().height
+
861 this->GetUsedPadding().TopBottom();
863 mButtonFrame
->SetRect(buttonRect
);
865 if (!NS_INLINE_IS_BREAK_BEFORE(aStatus
) &&
866 !NS_FRAME_IS_FULLY_COMPLETE(aStatus
)) {
867 // This frame didn't fit inside a fragmentation container. Splitting
868 // a nsComboboxControlFrame makes no sense, so we override the status here.
869 aStatus
= NS_FRAME_COMPLETE
;
873 //--------------------------------------------------------------
876 nsComboboxControlFrame::GetType() const
878 return nsGkAtoms::comboboxControlFrame
;
881 #ifdef DEBUG_FRAME_DUMP
883 nsComboboxControlFrame::GetFrameName(nsAString
& aResult
) const
885 return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult
);
890 //----------------------------------------------------------------------
891 // nsIComboboxControlFrame
892 //----------------------------------------------------------------------
894 nsComboboxControlFrame::ShowDropDown(bool aDoDropDown
)
896 mDelayedShowDropDown
= false;
897 EventStates eventStates
= mContent
->AsElement()->State();
898 if (aDoDropDown
&& eventStates
.HasState(NS_EVENT_STATE_DISABLED
)) {
902 if (!mDroppedDown
&& aDoDropDown
) {
903 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
904 if (!fm
|| fm
->GetFocusedContent() == GetContent()) {
905 DropDownPositionState state
= AbsolutelyPositionDropDown();
906 if (state
== eDropDownPositionFinal
) {
907 ShowList(aDoDropDown
); // might destroy us
908 } else if (state
== eDropDownPositionPendingResize
) {
909 // Delay until after the resize reflow, see nsAsyncResize.
910 mDelayedShowDropDown
= true;
913 // Delay until we get focus, see SetFocus().
914 mDelayedShowDropDown
= true;
916 } else if (mDroppedDown
&& !aDoDropDown
) {
917 ShowList(aDoDropDown
); // might destroy us
922 nsComboboxControlFrame::SetDropDown(nsIFrame
* aDropDownFrame
)
924 mDropdownFrame
= aDropDownFrame
;
925 mListControlFrame
= do_QueryFrame(mDropdownFrame
);
929 nsComboboxControlFrame::GetDropDown()
931 return mDropdownFrame
;
934 ///////////////////////////////////////////////////////////////
937 nsComboboxControlFrame::RedisplaySelectedText()
939 nsAutoScriptBlocker scriptBlocker
;
940 return RedisplayText(mListControlFrame
->GetSelectedIndex());
944 nsComboboxControlFrame::RedisplayText(int32_t aIndex
)
946 // Get the text to display
948 mListControlFrame
->GetOptionText(aIndex
, mDisplayedOptionText
);
950 mDisplayedOptionText
.Truncate();
952 mDisplayedIndex
= aIndex
;
954 REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n",
955 NS_LossyConvertUTF16toASCII(mDisplayedOptionText
).get());
957 // Send reflow command because the new text maybe larger
959 if (mDisplayContent
) {
960 // Don't call ActuallyDisplayText(true) directly here since that
961 // could cause recursive frame construction. See bug 283117 and the comment in
962 // HandleRedisplayTextEvent() below.
964 // Revoke outstanding events to avoid out-of-order events which could mean
965 // displaying the wrong text.
966 mRedisplayTextEvent
.Revoke();
968 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
969 "If we happen to run our redisplay event now, we might kill "
972 nsRefPtr
<RedisplayTextEvent
> event
= new RedisplayTextEvent(this);
973 mRedisplayTextEvent
= event
;
974 if (!nsContentUtils::AddScriptRunner(event
))
975 mRedisplayTextEvent
.Forget();
981 nsComboboxControlFrame::HandleRedisplayTextEvent()
983 // First, make sure that the content model is up to date and we've
984 // constructed the frames for all our content in the right places.
985 // Otherwise they'll end up under the wrong insertion frame when we
986 // ActuallyDisplayText, since that flushes out the content sink by
987 // calling SetText on a DOM node with aNotify set to true. See bug
989 nsWeakFrame
weakThis(this);
990 PresContext()->Document()->
991 FlushPendingNotifications(Flush_ContentAndNotify
);
992 if (!weakThis
.IsAlive())
995 // Redirect frame insertions during this method (see GetContentInsertionFrame())
996 // so that any reframing that the frame constructor forces upon us is inserted
997 // into the correct parent (mDisplayFrame). See bug 282607.
998 NS_PRECONDITION(!mInRedisplayText
, "Nested RedisplayText");
999 mInRedisplayText
= true;
1000 mRedisplayTextEvent
.Forget();
1002 ActuallyDisplayText(true);
1003 // XXXbz This should perhaps be eResize. Check.
1004 PresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame
,
1005 nsIPresShell::eStyleChange
,
1008 mInRedisplayText
= false;
1012 nsComboboxControlFrame::ActuallyDisplayText(bool aNotify
)
1014 if (mDisplayedOptionText
.IsEmpty()) {
1015 // Have to use a non-breaking space for line-height calculations
1017 static const char16_t space
= 0xA0;
1018 mDisplayContent
->SetText(&space
, 1, aNotify
);
1020 mDisplayContent
->SetText(mDisplayedOptionText
, aNotify
);
1025 nsComboboxControlFrame::GetIndexOfDisplayArea()
1027 return mDisplayedIndex
;
1030 //----------------------------------------------------------------------
1031 // nsISelectControlFrame
1032 //----------------------------------------------------------------------
1034 nsComboboxControlFrame::DoneAddingChildren(bool aIsDone
)
1036 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
1038 return NS_ERROR_FAILURE
;
1040 return listFrame
->DoneAddingChildren(aIsDone
);
1044 nsComboboxControlFrame::AddOption(int32_t aIndex
)
1046 if (aIndex
<= mDisplayedIndex
) {
1050 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
1051 return lcf
->AddOption(aIndex
);
1056 nsComboboxControlFrame::RemoveOption(int32_t aIndex
)
1058 nsWeakFrame
weakThis(this);
1059 if (mListControlFrame
->GetNumberOfOptions() > 0) {
1060 if (aIndex
< mDisplayedIndex
) {
1062 } else if (aIndex
== mDisplayedIndex
) {
1063 mDisplayedIndex
= 0; // IE6 compat
1064 RedisplayText(mDisplayedIndex
);
1068 // If we removed the last option, we need to blank things out
1072 if (!weakThis
.IsAlive())
1075 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
1076 return lcf
->RemoveOption(aIndex
);
1080 nsComboboxControlFrame::OnSetSelectedIndex(int32_t aOldIndex
, int32_t aNewIndex
)
1082 nsAutoScriptBlocker scriptBlocker
;
1083 RedisplayText(aNewIndex
);
1084 NS_ASSERTION(mDropdownFrame
, "No dropdown frame!");
1086 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
1087 NS_ASSERTION(listFrame
, "No list frame!");
1089 return listFrame
->OnSetSelectedIndex(aOldIndex
, aNewIndex
);
1092 // End nsISelectControlFrame
1093 //----------------------------------------------------------------------
1096 nsComboboxControlFrame::HandleEvent(nsPresContext
* aPresContext
,
1097 WidgetGUIEvent
* aEvent
,
1098 nsEventStatus
* aEventStatus
)
1100 NS_ENSURE_ARG_POINTER(aEventStatus
);
1102 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
1106 EventStates eventStates
= mContent
->AsElement()->State();
1107 if (eventStates
.HasState(NS_EVENT_STATE_DISABLED
)) {
1111 // If we have style that affects how we are selected, feed event down to
1112 // nsFrame::HandleEvent so that selection takes place when appropriate.
1113 const nsStyleUserInterface
* uiStyle
= StyleUserInterface();
1114 if (uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_NONE
|| uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_DISABLED
)
1115 return nsBlockFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
1122 nsComboboxControlFrame::SetFormProperty(nsIAtom
* aName
, const nsAString
& aValue
)
1124 nsIFormControlFrame
* fcFrame
= do_QueryFrame(mDropdownFrame
);
1126 return NS_NOINTERFACE
;
1129 return fcFrame
->SetFormProperty(aName
, aValue
);
1133 nsComboboxControlFrame::GetContentInsertionFrame() {
1134 return mInRedisplayText
? mDisplayFrame
: mDropdownFrame
->GetContentInsertionFrame();
1138 nsComboboxControlFrame::CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
)
1140 // The frames used to display the combo box and the button used to popup the dropdown list
1141 // are created through anonymous content. The dropdown list is not created through anonymous
1142 // content because its frame is initialized specifically for the drop-down case and it is placed
1143 // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the
1144 // layout of the display and button.
1146 // Note: The value attribute of the display content is set when an item is selected in the dropdown list.
1147 // If the content specified below does not honor the value attribute than nothing will be displayed.
1149 // For now the content that is created corresponds to two input buttons. It would be better to create the
1150 // tag as something other than input, but then there isn't any way to create a button frame since it
1151 // isn't possible to set the display type in CSS2 to create a button frame.
1153 // create content used for display
1154 //nsIAtom* tag = NS_NewAtom("mozcombodisplay");
1156 // Add a child text content node for the label
1158 nsNodeInfoManager
*nimgr
= mContent
->NodeInfo()->NodeInfoManager();
1160 mDisplayContent
= new nsTextNode(nimgr
);
1162 // set the value of the text node
1163 mDisplayedIndex
= mListControlFrame
->GetSelectedIndex();
1164 if (mDisplayedIndex
!= -1) {
1165 mListControlFrame
->GetOptionText(mDisplayedIndex
, mDisplayedOptionText
);
1167 ActuallyDisplayText(false);
1169 if (!aElements
.AppendElement(mDisplayContent
))
1170 return NS_ERROR_OUT_OF_MEMORY
;
1172 mButtonContent
= mContent
->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button
);
1173 if (!mButtonContent
)
1174 return NS_ERROR_OUT_OF_MEMORY
;
1176 // make someone to listen to the button. If its pressed by someone like Accessibility
1177 // then open or close the combo box.
1178 mButtonListener
= new nsComboButtonListener(this);
1179 mButtonContent
->AddEventListener(NS_LITERAL_STRING("click"), mButtonListener
,
1182 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
1183 NS_LITERAL_STRING("button"), false);
1184 // Set tabindex="-1" so that the button is not tabbable
1185 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
,
1186 NS_LITERAL_STRING("-1"), false);
1188 if (!aElements
.AppendElement(mButtonContent
))
1189 return NS_ERROR_OUT_OF_MEMORY
;
1195 nsComboboxControlFrame::AppendAnonymousContentTo(nsTArray
<nsIContent
*>& aElements
,
1198 if (mDisplayContent
) {
1199 aElements
.AppendElement(mDisplayContent
);
1202 if (mButtonContent
) {
1203 aElements
.AppendElement(mButtonContent
);
1207 // XXXbz this is a for-now hack. Now that display:inline-block works,
1208 // need to revisit this.
1209 class nsComboboxDisplayFrame
: public nsBlockFrame
{
1211 NS_DECL_FRAMEARENA_HELPERS
1213 nsComboboxDisplayFrame (nsStyleContext
* aContext
,
1214 nsComboboxControlFrame
* aComboBox
)
1215 : nsBlockFrame(aContext
),
1216 mComboBox(aComboBox
)
1219 // Need this so that line layout knows that this block's width
1220 // depends on the available width.
1221 virtual nsIAtom
* GetType() const MOZ_OVERRIDE
;
1223 virtual bool IsFrameOfType(uint32_t aFlags
) const MOZ_OVERRIDE
1225 return nsBlockFrame::IsFrameOfType(aFlags
&
1226 ~(nsIFrame::eReplacedContainsBlock
));
1229 virtual void Reflow(nsPresContext
* aPresContext
,
1230 nsHTMLReflowMetrics
& aDesiredSize
,
1231 const nsHTMLReflowState
& aReflowState
,
1232 nsReflowStatus
& aStatus
) MOZ_OVERRIDE
;
1234 virtual void BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1235 const nsRect
& aDirtyRect
,
1236 const nsDisplayListSet
& aLists
) MOZ_OVERRIDE
;
1239 nsComboboxControlFrame
* mComboBox
;
1242 NS_IMPL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame
)
1245 nsComboboxDisplayFrame::GetType() const
1247 return nsGkAtoms::comboboxDisplayFrame
;
1251 nsComboboxDisplayFrame::Reflow(nsPresContext
* aPresContext
,
1252 nsHTMLReflowMetrics
& aDesiredSize
,
1253 const nsHTMLReflowState
& aReflowState
,
1254 nsReflowStatus
& aStatus
)
1256 nsHTMLReflowState
state(aReflowState
);
1257 if (state
.ComputedHeight() == NS_INTRINSICSIZE
) {
1258 // Note that the only way we can have a computed height here is if the
1259 // combobox had a specified height. If it didn't, size based on what our
1260 // rows look like, for lack of anything better.
1261 state
.SetComputedHeight(mComboBox
->mListControlFrame
->GetHeightOfARow());
1263 nscoord computedWidth
= mComboBox
->mDisplayWidth
-
1264 state
.ComputedPhysicalBorderPadding().LeftRight();
1265 if (computedWidth
< 0) {
1268 state
.SetComputedWidth(computedWidth
);
1270 return nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, state
, aStatus
);
1274 nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1275 const nsRect
& aDirtyRect
,
1276 const nsDisplayListSet
& aLists
)
1278 nsDisplayListCollection set
;
1279 nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, set
);
1281 // remove background items if parent frame is themed
1282 if (mComboBox
->IsThemed()) {
1283 set
.BorderBackground()->DeleteAll();
1290 nsComboboxControlFrame::CreateFrameFor(nsIContent
* aContent
)
1292 NS_PRECONDITION(nullptr != aContent
, "null ptr");
1294 NS_ASSERTION(mDisplayContent
, "mDisplayContent can't be null!");
1296 if (mDisplayContent
!= aContent
) {
1297 // We only handle the frames for mDisplayContent here
1302 nsIPresShell
*shell
= PresContext()->PresShell();
1303 nsStyleSet
*styleSet
= shell
->StyleSet();
1305 // create the style contexts for the anonymous block frame and text frame
1306 nsRefPtr
<nsStyleContext
> styleContext
;
1307 styleContext
= styleSet
->
1308 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozDisplayComboboxControlFrame
,
1311 nsRefPtr
<nsStyleContext
> textStyleContext
;
1312 textStyleContext
= styleSet
->ResolveStyleForNonElement(mStyleContext
);
1314 // Start by creating our anonymous block frame
1315 mDisplayFrame
= new (shell
) nsComboboxDisplayFrame(styleContext
, this);
1316 mDisplayFrame
->Init(mContent
, this, nullptr);
1318 // Create a text frame and put it inside the block frame
1319 nsIFrame
* textFrame
= NS_NewTextFrame(shell
, textStyleContext
);
1321 // initialize the text frame
1322 textFrame
->Init(aContent
, mDisplayFrame
, nullptr);
1323 mDisplayContent
->SetPrimaryFrame(textFrame
);
1325 nsFrameList
textList(textFrame
, textFrame
);
1326 mDisplayFrame
->SetInitialChildList(kPrincipalList
, textList
);
1327 return mDisplayFrame
;
1331 nsComboboxControlFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
1333 // Revoke any pending RedisplayTextEvent
1334 mRedisplayTextEvent
.Revoke();
1336 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame
*>(this), false);
1339 MOZ_ASSERT(mDropdownFrame
, "mDroppedDown without frame");
1340 nsView
* view
= mDropdownFrame
->GetView();
1342 nsIWidget
* widget
= view
->GetWidget();
1344 widget
->CaptureRollupEvents(this, false);
1348 // Cleanup frames in popup child list
1349 mPopupFrames
.DestroyFramesFrom(aDestructRoot
);
1350 nsContentUtils::DestroyAnonymousContent(&mDisplayContent
);
1351 nsContentUtils::DestroyAnonymousContent(&mButtonContent
);
1352 nsBlockFrame::DestroyFrom(aDestructRoot
);
1356 nsComboboxControlFrame::GetChildList(ChildListID aListID
) const
1358 if (kSelectPopupList
== aListID
) {
1359 return mPopupFrames
;
1361 return nsBlockFrame::GetChildList(aListID
);
1365 nsComboboxControlFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const
1367 nsBlockFrame::GetChildLists(aLists
);
1368 mPopupFrames
.AppendIfNonempty(aLists
, kSelectPopupList
);
1372 nsComboboxControlFrame::SetInitialChildList(ChildListID aListID
,
1373 nsFrameList
& aChildList
)
1375 if (kSelectPopupList
== aListID
) {
1376 mPopupFrames
.SetFrames(aChildList
);
1378 for (nsFrameList::Enumerator
e(aChildList
); !e
.AtEnd(); e
.Next()) {
1379 nsCOMPtr
<nsIFormControl
> formControl
=
1380 do_QueryInterface(e
.get()->GetContent());
1381 if (formControl
&& formControl
->GetType() == NS_FORM_BUTTON_BUTTON
) {
1382 mButtonFrame
= e
.get();
1386 NS_ASSERTION(mButtonFrame
, "missing button frame in initial child list");
1387 nsBlockFrame::SetInitialChildList(aListID
, aChildList
);
1391 //----------------------------------------------------------------------
1393 //----------------------------------------------------------------------
1395 nsComboboxControlFrame::Rollup(uint32_t aCount
, bool aFlush
,
1396 const nsIntPoint
* pos
, nsIContent
** aLastRolledUp
)
1398 if (!mDroppedDown
) {
1402 bool consume
= true;
1406 nsWeakFrame
weakFrame(this);
1407 mListControlFrame
->AboutToRollup(); // might destroy us
1408 if (!weakFrame
.IsAlive()) {
1411 ShowDropDown(false); // might destroy us
1412 if (weakFrame
.IsAlive()) {
1413 mListControlFrame
->CaptureMouseEvents(false);
1416 if (aFlush
&& weakFrame
.IsAlive()) {
1417 // The popup's visibility doesn't update until the minimize animation has
1418 // finished, so call UpdateWidgetGeometry to update it right away.
1419 nsViewManager
* viewManager
= mDropdownFrame
->GetView()->GetViewManager();
1420 viewManager
->UpdateWidgetGeometry();
1427 nsComboboxControlFrame::GetRollupWidget()
1429 nsView
* view
= mDropdownFrame
->GetView();
1431 return view
->GetWidget();
1435 nsComboboxControlFrame::RollupFromList()
1437 if (ShowList(false))
1438 mListControlFrame
->CaptureMouseEvents(false);
1442 nsComboboxControlFrame::UpdateRecentIndex(int32_t aIndex
)
1444 int32_t index
= mRecentSelectedIndex
;
1445 if (mRecentSelectedIndex
== NS_SKIP_NOTIFY_INDEX
|| aIndex
== NS_SKIP_NOTIFY_INDEX
)
1446 mRecentSelectedIndex
= aIndex
;
1450 class nsDisplayComboboxFocus
: public nsDisplayItem
{
1452 nsDisplayComboboxFocus(nsDisplayListBuilder
* aBuilder
,
1453 nsComboboxControlFrame
* aFrame
)
1454 : nsDisplayItem(aBuilder
, aFrame
) {
1455 MOZ_COUNT_CTOR(nsDisplayComboboxFocus
);
1457 #ifdef NS_BUILD_REFCNT_LOGGING
1458 virtual ~nsDisplayComboboxFocus() {
1459 MOZ_COUNT_DTOR(nsDisplayComboboxFocus
);
1463 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1464 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
1465 NS_DISPLAY_DECL_NAME("ComboboxFocus", TYPE_COMBOBOX_FOCUS
)
1468 void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder
* aBuilder
,
1469 nsRenderingContext
* aCtx
)
1471 static_cast<nsComboboxControlFrame
*>(mFrame
)
1472 ->PaintFocus(*aCtx
, ToReferenceFrame());
1476 nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1477 const nsRect
& aDirtyRect
,
1478 const nsDisplayListSet
& aLists
)
1481 printf("%p paint at (%d, %d, %d, %d)\n", this,
1482 aDirtyRect
.x
, aDirtyRect
.y
, aDirtyRect
.width
, aDirtyRect
.height
);
1485 if (aBuilder
->IsForEventDelivery()) {
1486 // Don't allow children to receive events.
1487 // REVIEW: following old GetFrameForPoint
1488 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1490 // REVIEW: Our in-flow child frames are inline-level so they will paint in our
1491 // content list, so we don't need to mess with layers.
1492 nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
1495 // draw a focus indicator only when focus rings should be drawn
1496 nsIDocument
* doc
= mContent
->GetComposedDoc();
1498 nsPIDOMWindow
* window
= doc
->GetWindow();
1499 if (window
&& window
->ShouldShowFocusRing()) {
1500 nsPresContext
*presContext
= PresContext();
1501 const nsStyleDisplay
*disp
= StyleDisplay();
1502 if ((!IsThemed(disp
) ||
1503 !presContext
->GetTheme()->ThemeDrawsFocusForWidget(disp
->mAppearance
)) &&
1504 mDisplayFrame
&& IsVisibleForPainting(aBuilder
)) {
1505 aLists
.Content()->AppendNewToTop(
1506 new (aBuilder
) nsDisplayComboboxFocus(aBuilder
, this));
1511 DisplaySelectionOverlay(aBuilder
, aLists
.Content());
1514 void nsComboboxControlFrame::PaintFocus(nsRenderingContext
& aRenderingContext
,
1517 /* Do we need to do anything? */
1518 EventStates eventStates
= mContent
->AsElement()->State();
1519 if (eventStates
.HasState(NS_EVENT_STATE_DISABLED
) || sFocused
!= this)
1522 gfxContext
* gfx
= aRenderingContext
.ThebesContext();
1525 nsRect clipRect
= mDisplayFrame
->GetRect() + aPt
;
1526 gfx
->Clip(NSRectToSnappedRect(clipRect
,
1527 PresContext()->AppUnitsPerDevPixel(),
1528 *aRenderingContext
.GetDrawTarget()));
1530 // REVIEW: Why does the old code paint mDisplayFrame again? We've
1531 // already painted it in the children above. So clipping it here won't do
1534 /////////////////////
1537 StrokeOptions strokeOptions
;
1538 nsLayoutUtils::InitDashPattern(strokeOptions
, NS_STYLE_BORDER_STYLE_DOTTED
);
1539 ColorPattern
color(ToDeviceColor(StyleColor()->mColor
));
1540 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
1541 clipRect
.width
-= onePixel
;
1542 clipRect
.height
-= onePixel
;
1544 ToRect(nsLayoutUtils::RectToGfxRect(clipRect
, PresContext()->AppUnitsPerDevPixel()));
1545 StrokeSnappedEdgesOfRect(r
, *aRenderingContext
.GetDrawTarget(),
1546 color
, strokeOptions
);
1551 //---------------------------------------------------------
1552 // gets the content (an option) by index and then set it as
1553 // being selected or not selected
1554 //---------------------------------------------------------
1556 nsComboboxControlFrame::OnOptionSelected(int32_t aIndex
, bool aSelected
)
1559 nsISelectControlFrame
*selectFrame
= do_QueryFrame(mListControlFrame
);
1561 selectFrame
->OnOptionSelected(aIndex
, aSelected
);
1565 nsAutoScriptBlocker blocker
;
1566 RedisplayText(aIndex
);
1568 nsWeakFrame
weakFrame(this);
1569 RedisplaySelectedText();
1570 if (weakFrame
.IsAlive()) {
1571 FireValueChangeEvent(); // Fire after old option is unselected
1579 void nsComboboxControlFrame::FireValueChangeEvent()
1581 // Fire ValueChange event to indicate data value of combo box has changed
1582 nsContentUtils::AddScriptRunner(
1583 new AsyncEventDispatcher(mContent
, NS_LITERAL_STRING("ValueChange"), true,
1588 nsComboboxControlFrame::OnContentReset()
1590 if (mListControlFrame
) {
1591 mListControlFrame
->OnContentReset();
1596 //--------------------------------------------------------
1598 //--------------------------------------------------------
1600 nsComboboxControlFrame::SaveState(nsPresState
** aState
)
1602 if (!mListControlFrame
)
1603 return NS_ERROR_FAILURE
;
1605 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1606 return stateful
->SaveState(aState
);
1610 nsComboboxControlFrame::RestoreState(nsPresState
* aState
)
1612 if (!mListControlFrame
)
1613 return NS_ERROR_FAILURE
;
1615 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1616 NS_ASSERTION(stateful
, "Must implement nsIStatefulFrame");
1617 return stateful
->RestoreState(aState
);
1621 // Fennec uses a custom combobox built-in widget.
1626 nsComboboxControlFrame::ToolkitHasNativePopup()
1628 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
1632 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */