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"
7 #include "nsFocusManager.h"
8 #include "nsFormControlFrame.h"
10 #include "nsCSSAnonBoxes.h"
11 #include "nsHTMLParts.h"
12 #include "nsIFormControl.h"
13 #include "nsINameSpaceManager.h"
14 #include "nsIListControlFrame.h"
15 #include "nsPIDOMWindow.h"
16 #include "nsIPresShell.h"
17 #include "nsContentList.h"
19 #include "nsViewManager.h"
20 #include "nsIDOMNode.h"
21 #include "nsISelectControlFrame.h"
22 #include "nsContentUtils.h"
23 #include "nsIDocument.h"
24 #include "nsINodeInfo.h"
25 #include "nsIScrollableFrame.h"
26 #include "nsListControlFrame.h"
27 #include "nsAutoPtr.h"
28 #include "nsStyleSet.h"
29 #include "nsNodeInfoManager.h"
30 #include "nsContentCreatorFunctions.h"
31 #include "nsLayoutUtils.h"
32 #include "nsDisplayList.h"
34 #include "nsAsyncDOMEvent.h"
35 #include "nsRenderingContext.h"
36 #include "mozilla/Likely.h"
38 #include "nsTextNode.h"
39 #include "mozilla/LookAndFeel.h"
40 #include "mozilla/MouseEvents.h"
42 using namespace mozilla
;
45 nsComboboxControlFrame::RedisplayTextEvent::Run()
48 mControlFrame
->HandleRedisplayTextEvent();
54 #define FIX_FOR_BUG_53259
56 // Drop down list event management.
57 // The combo box uses the following strategy for managing the drop-down list.
58 // If the combo box or it's arrow button is clicked on the drop-down list is displayed
59 // If mouse exit's the combo box with the drop-down list displayed the drop-down list
60 // is asked to capture events
61 // The drop-down list will capture all events including mouse down and up and will always
62 // return with ListWasSelected method call regardless of whether an item in the list was
64 // The ListWasSelected code will turn off mouse-capture for the drop-down list.
65 // The drop-down list does not explicitly set capture when it is in the drop-down mode.
69 * Helper class that listens to the combo boxes button. If the button is pressed the
70 * combo box is toggled to open or close. this is used by Accessibility which presses
71 * that button Programmatically.
73 class nsComboButtonListener
: public nsIDOMEventListener
78 NS_IMETHOD
HandleEvent(nsIDOMEvent
*)
80 mComboBox
->ShowDropDown(!mComboBox
->IsDroppedDown());
84 nsComboButtonListener(nsComboboxControlFrame
* aCombobox
)
86 mComboBox
= aCombobox
;
89 virtual ~nsComboButtonListener() {}
91 nsComboboxControlFrame
* mComboBox
;
94 NS_IMPL_ISUPPORTS1(nsComboButtonListener
,
97 // static class data member for Bug 32920
98 nsComboboxControlFrame
* nsComboboxControlFrame::sFocused
= nullptr;
101 NS_NewComboboxControlFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, uint32_t aStateFlags
)
103 nsComboboxControlFrame
* it
= new (aPresShell
) nsComboboxControlFrame(aContext
);
106 // set the state flags (if any are provided)
107 it
->AddStateBits(aStateFlags
);
113 NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame
)
115 //-----------------------------------------------------------
116 // Reflow Debugging Macros
117 // These let us "see" how many reflow counts are happening
118 //-----------------------------------------------------------
119 #ifdef DO_REFLOW_COUNTER
121 #define MAX_REFLOW_CNT 1024
122 static int32_t gTotalReqs
= 0;;
123 static int32_t gTotalReflows
= 0;;
124 static int32_t gReflowControlCntRQ
[MAX_REFLOW_CNT
];
125 static int32_t gReflowControlCnt
[MAX_REFLOW_CNT
];
126 static int32_t gReflowInx
= -1;
128 #define REFLOW_COUNTER() \
129 if (mReflowId > -1) \
130 gReflowControlCnt[mReflowId]++;
132 #define REFLOW_COUNTER_REQUEST() \
133 if (mReflowId > -1) \
134 gReflowControlCntRQ[mReflowId]++;
136 #define REFLOW_COUNTER_DUMP(__desc) \
137 if (mReflowId > -1) {\
138 gTotalReqs += gReflowControlCntRQ[mReflowId];\
139 gTotalReflows += gReflowControlCnt[mReflowId];\
140 printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \
141 mReflowId, (__desc), \
142 gReflowControlCnt[mReflowId], \
143 gReflowControlCntRQ[mReflowId],\
144 gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\
147 #define REFLOW_COUNTER_INIT() \
148 if (gReflowInx < MAX_REFLOW_CNT) { \
150 mReflowId = gReflowInx; \
151 gReflowControlCnt[mReflowId] = 0; \
152 gReflowControlCntRQ[mReflowId] = 0; \
158 #define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
159 #define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
160 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
161 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
163 #else //-------------
165 #define REFLOW_COUNTER_REQUEST()
166 #define REFLOW_COUNTER()
167 #define REFLOW_COUNTER_DUMP(__desc)
168 #define REFLOW_COUNTER_INIT()
170 #define REFLOW_DEBUG_MSG(_msg)
171 #define REFLOW_DEBUG_MSG2(_msg1, _msg2)
172 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3)
173 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4)
178 //------------------------------------------
179 // This is for being VERY noisy
180 //------------------------------------------
182 #define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
183 #define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
184 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
185 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
187 #define REFLOW_NOISY_MSG(_msg)
188 #define REFLOW_NOISY_MSG2(_msg1, _msg2)
189 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3)
190 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4)
193 //------------------------------------------
194 // Displays value in pixels or twips
195 //------------------------------------------
197 #define PX(__v) __v / 15
202 //------------------------------------------------------
203 //-- Done with macros
204 //------------------------------------------------------
206 nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext
* aContext
)
207 : nsBlockFrame(aContext
)
208 , mDisplayFrame(nullptr)
209 , mButtonFrame(nullptr)
210 , mDropdownFrame(nullptr)
211 , mListControlFrame(nullptr)
213 , mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX
)
214 , mDisplayedIndex(-1)
215 , mLastDropDownAboveScreenY(nscoord_MIN
)
216 , mLastDropDownBelowScreenY(nscoord_MIN
)
217 , mDroppedDown(false)
218 , mInRedisplayText(false)
219 , mDelayedShowDropDown(false)
221 REFLOW_COUNTER_INIT()
224 //--------------------------------------------------------------
225 nsComboboxControlFrame::~nsComboboxControlFrame()
227 REFLOW_COUNTER_DUMP("nsCCF");
230 //--------------------------------------------------------------
232 NS_QUERYFRAME_HEAD(nsComboboxControlFrame
)
233 NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame
)
234 NS_QUERYFRAME_ENTRY(nsIFormControlFrame
)
235 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
236 NS_QUERYFRAME_ENTRY(nsISelectControlFrame
)
237 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
238 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
242 nsComboboxControlFrame::AccessibleType()
244 return a11y::eHTMLComboboxType
;
249 nsComboboxControlFrame::SetFocus(bool aOn
, bool aRepaint
)
251 nsWeakFrame
weakFrame(this);
253 nsListControlFrame::ComboboxFocusSet();
255 if (mDelayedShowDropDown
) {
256 ShowDropDown(true); // might destroy us
257 if (!weakFrame
.IsAlive()) {
263 mDelayedShowDropDown
= false;
265 mListControlFrame
->ComboboxFinish(mDisplayedIndex
); // might destroy us
266 if (!weakFrame
.IsAlive()) {
270 // May delete |this|.
271 mListControlFrame
->FireOnChange();
274 if (!weakFrame
.IsAlive()) {
278 // This is needed on a temporary basis. It causes the focus
279 // rect to be drawn. This is much faster than ReResolvingStyle
285 nsComboboxControlFrame::ShowPopup(bool aShowPopup
)
287 nsView
* view
= mDropdownFrame
->GetView();
288 nsViewManager
* viewManager
= view
->GetViewManager();
291 nsRect rect
= mDropdownFrame
->GetRect();
293 viewManager
->ResizeView(view
, rect
);
294 viewManager
->SetViewVisibility(view
, nsViewVisibility_kShow
);
296 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
297 nsRect
emptyRect(0, 0, 0, 0);
298 viewManager
->ResizeView(view
, emptyRect
);
301 // fire a popup dom event
302 nsEventStatus status
= nsEventStatus_eIgnore
;
303 WidgetMouseEvent
event(true, aShowPopup
?
304 NS_XUL_POPUP_SHOWING
: NS_XUL_POPUP_HIDING
, nullptr,
305 WidgetMouseEvent::eReal
);
307 nsCOMPtr
<nsIPresShell
> shell
= PresContext()->GetPresShell();
309 shell
->HandleDOMEventWithTarget(mContent
, &event
, &status
);
313 nsComboboxControlFrame::ShowList(bool aShowList
)
315 nsView
* view
= mDropdownFrame
->GetView();
317 NS_ASSERTION(!view
->HasWidget(),
318 "We shouldn't have a widget before we need to display the popup");
320 // Create the widget for the drop-down list
321 view
->GetViewManager()->SetViewFloating(view
, true);
323 nsWidgetInitData widgetData
;
324 widgetData
.mWindowType
= eWindowType_popup
;
325 widgetData
.mBorderStyle
= eBorderStyle_default
;
326 view
->CreateWidgetForPopup(&widgetData
);
328 nsIWidget
* widget
= view
->GetWidget();
330 // We must do this before ShowPopup in case it destroys us (bug 813442).
331 widget
->CaptureRollupEvents(this, false);
335 nsWeakFrame
weakFrame(this);
336 ShowPopup(aShowList
); // might destroy us
337 if (!weakFrame
.IsAlive()) {
341 mDroppedDown
= aShowList
;
342 nsIWidget
* widget
= view
->GetWidget();
344 // The listcontrol frame will call back to the nsComboboxControlFrame's
345 // ListWasSelected which will stop the capture.
346 mListControlFrame
->AboutToDropDown();
347 mListControlFrame
->CaptureMouseEvents(true);
349 widget
->CaptureRollupEvents(this, true);
353 view
->DestroyWidget();
357 return weakFrame
.IsAlive();
360 class nsResizeDropdownAtFinalPosition
361 : public nsIReflowCallback
, public nsRunnable
364 nsResizeDropdownAtFinalPosition(nsComboboxControlFrame
* aFrame
)
367 MOZ_COUNT_CTOR(nsResizeDropdownAtFinalPosition
);
369 ~nsResizeDropdownAtFinalPosition()
371 MOZ_COUNT_DTOR(nsResizeDropdownAtFinalPosition
);
374 virtual bool ReflowFinished()
381 virtual void ReflowCallbackCanceled()
388 if (mFrame
.IsAlive()) {
389 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame())->
390 AbsolutelyPositionDropDown();
399 nsComboboxControlFrame::ReflowDropdown(nsPresContext
* aPresContext
,
400 const nsHTMLReflowState
& aReflowState
)
402 // All we want out of it later on, really, is the height of a row, so we
403 // don't even need to cache mDropdownFrame's ascent or anything. If we don't
404 // need to reflow it, just bail out here.
405 if (!aReflowState
.ShouldReflowAllKids() &&
406 !NS_SUBTREE_DIRTY(mDropdownFrame
)) {
410 // XXXbz this will, for small-height dropdowns, have extra space on the right
411 // edge for the scrollbar we don't show... but that's the best we can do here
413 nsSize
availSize(aReflowState
.AvailableWidth(), NS_UNCONSTRAINEDSIZE
);
414 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, mDropdownFrame
,
417 // If the dropdown's intrinsic width is narrower than our specified width,
418 // then expand it out. We want our border-box width to end up the same as
419 // the dropdown's so account for both sets of mComputedBorderPadding.
420 nscoord forcedWidth
= aReflowState
.ComputedWidth() +
421 aReflowState
.ComputedPhysicalBorderPadding().LeftRight() -
422 kidReflowState
.ComputedPhysicalBorderPadding().LeftRight();
423 kidReflowState
.SetComputedWidth(std::max(kidReflowState
.ComputedWidth(),
426 // ensure we start off hidden
427 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
428 nsView
* view
= mDropdownFrame
->GetView();
429 nsViewManager
* viewManager
= view
->GetViewManager();
430 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
431 nsRect
emptyRect(0, 0, 0, 0);
432 viewManager
->ResizeView(view
, emptyRect
);
435 // Allow the child to move/size/change-visibility its view if it's currently
437 int32_t flags
= NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_VISIBILITY
| NS_FRAME_NO_SIZE_VIEW
;
441 nsRect rect
= mDropdownFrame
->GetRect();
442 nsHTMLReflowMetrics
desiredSize(aReflowState
.GetWritingMode());
443 nsReflowStatus ignoredStatus
;
444 nsresult rv
= ReflowChild(mDropdownFrame
, aPresContext
, desiredSize
,
445 kidReflowState
, rect
.x
, rect
.y
, flags
,
448 // Set the child's width and height to it's desired size
449 FinishReflowChild(mDropdownFrame
, aPresContext
, &kidReflowState
,
450 desiredSize
, rect
.x
, rect
.y
, flags
);
455 nsComboboxControlFrame::GetCSSTransformTranslation()
457 nsIFrame
* frame
= this;
458 bool is3DTransform
= false;
462 gfx3DMatrix ctm
= frame
->GetTransformMatrix(nullptr, &parent
);
464 if (ctm
.Is2D(&matrix
)) {
465 transform
= transform
* matrix
;
467 is3DTransform
= true;
473 if (!is3DTransform
&& !transform
.HasNonTranslation()) {
474 nsPresContext
* pc
= PresContext();
475 gfxPoint pixelTranslation
= transform
.GetTranslation();
476 int32_t apd
= pc
->AppUnitsPerDevPixel();
477 translation
.x
= NSFloatPixelsToAppUnits(float(pixelTranslation
.x
), apd
);
478 translation
.y
= NSFloatPixelsToAppUnits(float(pixelTranslation
.y
), apd
);
479 // To get the translation introduced only by transforms we subtract the
480 // regular non-transform translation.
481 nsRootPresContext
* rootPC
= pc
->GetRootPresContext();
483 translation
-= GetOffsetToCrossDoc(rootPC
->PresShell()->GetRootFrame());
485 translation
.x
= translation
.y
= 0;
491 class nsAsyncRollup
: public nsRunnable
494 nsAsyncRollup(nsComboboxControlFrame
* aFrame
) : mFrame(aFrame
) {}
497 if (mFrame
.IsAlive()) {
498 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame())
506 class nsAsyncResize
: public nsRunnable
509 nsAsyncResize(nsComboboxControlFrame
* aFrame
) : mFrame(aFrame
) {}
512 if (mFrame
.IsAlive()) {
513 nsComboboxControlFrame
* combo
=
514 static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame());
515 static_cast<nsListControlFrame
*>(combo
->mDropdownFrame
)->
516 SetSuppressScrollbarUpdate(true);
517 nsCOMPtr
<nsIPresShell
> shell
= mFrame
->PresContext()->PresShell();
518 shell
->FrameNeedsReflow(combo
->mDropdownFrame
, nsIPresShell::eResize
,
520 shell
->FlushPendingNotifications(Flush_Layout
);
521 if (mFrame
.IsAlive()) {
522 combo
= static_cast<nsComboboxControlFrame
*>(mFrame
.GetFrame());
523 static_cast<nsListControlFrame
*>(combo
->mDropdownFrame
)->
524 SetSuppressScrollbarUpdate(false);
525 if (combo
->mDelayedShowDropDown
) {
526 combo
->ShowDropDown(true);
536 nsComboboxControlFrame::GetAvailableDropdownSpace(nscoord
* aAbove
,
538 nsPoint
* aTranslation
)
540 // Note: At first glance, it appears that you could simply get the absolute
541 // bounding box for the dropdown list by first getting its view, then getting
542 // the view's nsIWidget, then asking the nsIWidget for its AbsoluteBounds.
543 // The problem with this approach, is that the dropdown lists y location can
544 // change based on whether the dropdown is placed below or above the display
545 // frame. The approach, taken here is to get the absolute position of the
546 // display frame and use its location to determine if the dropdown will go
549 // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms.
550 // In the special case that our transform is only a 2D translation we
551 // introduce this hack so that the dropdown will show up in the right place.
552 *aTranslation
= GetCSSTransformTranslation();
556 nsRect screen
= nsFormControlFrame::GetUsableScreenRect(PresContext());
557 if (mLastDropDownBelowScreenY
== nscoord_MIN
) {
558 nsRect thisScreenRect
= GetScreenRectInAppUnits();
559 mLastDropDownBelowScreenY
= thisScreenRect
.YMost() + aTranslation
->y
;
560 mLastDropDownAboveScreenY
= thisScreenRect
.y
+ aTranslation
->y
;
564 nsPresContext
* pc
= PresContext()->GetToplevelContentDocumentPresContext();
565 nsIFrame
* root
= pc
? pc
->PresShell()->GetRootFrame() : nullptr;
567 minY
= root
->GetScreenRectInAppUnits().y
;
568 if (mLastDropDownBelowScreenY
< minY
) {
569 // Don't allow the drop-down to be placed above the content area.
576 nscoord below
= screen
.YMost() - mLastDropDownBelowScreenY
;
577 nscoord above
= mLastDropDownAboveScreenY
- minY
;
579 // If the difference between the space above and below is less
580 // than a row-height, then we favor the space below.
581 if (above
>= below
) {
582 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
583 nscoord rowHeight
= lcf
->GetHeightOfARow();
584 if (above
< below
+ rowHeight
) {
593 nsComboboxControlFrame::DropDownPositionState
594 nsComboboxControlFrame::AbsolutelyPositionDropDown()
597 nscoord above
, below
;
598 mLastDropDownBelowScreenY
= nscoord_MIN
;
599 GetAvailableDropdownSpace(&above
, &below
, &translation
);
600 if (above
<= 0 && below
<= 0) {
601 if (IsDroppedDown()) {
602 // Hide the view immediately to minimize flicker.
603 nsView
* view
= mDropdownFrame
->GetView();
604 view
->GetViewManager()->SetViewVisibility(view
, nsViewVisibility_kHide
);
605 NS_DispatchToCurrentThread(new nsAsyncRollup(this));
607 return eDropDownPositionSuppressed
;
610 nsSize dropdownSize
= mDropdownFrame
->GetSize();
611 nscoord height
= std::max(above
, below
);
612 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
613 if (height
< dropdownSize
.height
) {
614 if (lcf
->GetNumDisplayRows() > 1) {
615 // The drop-down doesn't fit and currently shows more than 1 row -
616 // schedule a resize to show fewer rows.
617 NS_DispatchToCurrentThread(new nsAsyncResize(this));
618 return eDropDownPositionPendingResize
;
620 } else if (height
> (dropdownSize
.height
+ lcf
->GetHeightOfARow() * 1.5) &&
621 lcf
->GetDropdownCanGrow()) {
622 // The drop-down fits but there is room for at least 1.5 more rows -
623 // schedule a resize to show more rows if it has more rows to show.
624 // (1.5 rows for good measure to avoid any rounding issues that would
625 // lead to a loop of reflow requests)
626 NS_DispatchToCurrentThread(new nsAsyncResize(this));
627 return eDropDownPositionPendingResize
;
630 // Position the drop-down below if there is room, otherwise place it above
631 // if there is room. If there is no room for it on either side then place
632 // it below (to avoid overlapping UI like the URL bar).
633 bool b
= dropdownSize
.height
<= below
|| dropdownSize
.height
> above
;
634 nsPoint
dropdownPosition(0, b
? GetRect().height
: -dropdownSize
.height
);
635 if (StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
636 // Align the right edge of the drop-down with the right edge of the control.
637 dropdownPosition
.x
= GetRect().width
- dropdownSize
.width
;
640 // Don't position the view unless the position changed since it might cause
641 // a call to NotifyGeometryChange() and an infinite loop here.
642 const nsPoint currentPos
= mDropdownFrame
->GetPosition();
643 const nsPoint newPos
= dropdownPosition
+ translation
;
644 if (currentPos
!= newPos
) {
645 mDropdownFrame
->SetPosition(newPos
);
646 nsContainerFrame::PositionFrameView(mDropdownFrame
);
648 return eDropDownPositionFinal
;
652 nsComboboxControlFrame::NotifyGeometryChange()
654 // We don't need to resize if we're not dropped down since ShowDropDown
655 // does that, or if we're dirty then the reflow callback does it,
656 // or if we have a delayed ShowDropDown pending.
657 if (IsDroppedDown() &&
658 !(GetStateBits() & NS_FRAME_IS_DIRTY
) &&
659 !mDelayedShowDropDown
) {
660 // Async because we're likely in a middle of a scroll here so
661 // frame/view positions are in flux.
662 nsRefPtr
<nsResizeDropdownAtFinalPosition
> resize
=
663 new nsResizeDropdownAtFinalPosition(this);
664 NS_DispatchToCurrentThread(resize
);
668 //----------------------------------------------------------
670 //----------------------------------------------------------
671 #ifdef DO_REFLOW_DEBUG
672 static int myCounter
= 0;
674 static void printSize(char * aDesc
, nscoord aSize
)
676 printf(" %s: ", aDesc
);
677 if (aSize
== NS_UNCONSTRAINEDSIZE
) {
680 printf("%d", PX(aSize
));
685 //-------------------------------------------------------------------
686 //-- Main Reflow for the Combobox
687 //-------------------------------------------------------------------
690 nsComboboxControlFrame::GetIntrinsicWidth(nsRenderingContext
* aRenderingContext
,
691 nsLayoutUtils::IntrinsicWidthType aType
)
693 // get the scrollbar width, we'll use this later
694 nscoord scrollbarWidth
= 0;
695 nsPresContext
* presContext
= PresContext();
696 if (mListControlFrame
) {
697 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
698 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
699 scrollbarWidth
= scrollable
->GetNondisappearingScrollbarWidth(
700 presContext
, aRenderingContext
);
703 nscoord displayWidth
= 0;
704 if (MOZ_LIKELY(mDisplayFrame
)) {
705 displayWidth
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
710 if (mDropdownFrame
) {
711 nscoord dropdownContentWidth
;
712 bool isUsingOverlayScrollbars
=
713 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars
) != 0;
714 if (aType
== nsLayoutUtils::MIN_WIDTH
) {
715 dropdownContentWidth
= mDropdownFrame
->GetMinWidth(aRenderingContext
);
716 if (isUsingOverlayScrollbars
) {
717 dropdownContentWidth
+= scrollbarWidth
;
720 NS_ASSERTION(aType
== nsLayoutUtils::PREF_WIDTH
, "Unexpected type");
721 dropdownContentWidth
= mDropdownFrame
->GetPrefWidth(aRenderingContext
);
722 if (isUsingOverlayScrollbars
) {
723 dropdownContentWidth
+= scrollbarWidth
;
726 dropdownContentWidth
= NSCoordSaturatingSubtract(dropdownContentWidth
,
730 displayWidth
= std::max(dropdownContentWidth
, displayWidth
);
733 // add room for the dropmarker button if there is one
734 if (!IsThemed() || presContext
->GetTheme()->ThemeNeedsComboboxDropmarker())
735 displayWidth
+= scrollbarWidth
;
742 nsComboboxControlFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
745 DISPLAY_MIN_WIDTH(this, minWidth
);
746 minWidth
= GetIntrinsicWidth(aRenderingContext
, nsLayoutUtils::MIN_WIDTH
);
751 nsComboboxControlFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
754 DISPLAY_PREF_WIDTH(this, prefWidth
);
755 prefWidth
= GetIntrinsicWidth(aRenderingContext
, nsLayoutUtils::PREF_WIDTH
);
760 nsComboboxControlFrame::Reflow(nsPresContext
* aPresContext
,
761 nsHTMLReflowMetrics
& aDesiredSize
,
762 const nsHTMLReflowState
& aReflowState
,
763 nsReflowStatus
& aStatus
)
765 // Constraints we try to satisfy:
767 // 1) Default width of button is the vertical scrollbar size
768 // 2) If the width of button is bigger than our width, set width of
770 // 3) Default height of button is height of display area
771 // 4) Width of display area is whatever is left over from our width after
772 // allocating width for the button.
773 // 5) Height of display area is GetHeightOfARow() on the
774 // mListControlFrame.
776 if (!mDisplayFrame
|| !mButtonFrame
|| !mDropdownFrame
) {
777 NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!");
778 return NS_ERROR_UNEXPECTED
;
781 // Make sure the displayed text is the same as the selected option, bug 297389.
782 int32_t selectedIndex
;
783 nsAutoString selectedOptionText
;
785 selectedIndex
= mListControlFrame
->GetSelectedIndex();
788 // In dropped down mode the "selected index" is the hovered menu item,
789 // we want the last selected item which is |mDisplayedIndex| in this case.
790 selectedIndex
= mDisplayedIndex
;
792 if (selectedIndex
!= -1) {
793 mListControlFrame
->GetOptionText(selectedIndex
, selectedOptionText
);
795 if (mDisplayedOptionText
!= selectedOptionText
) {
796 RedisplayText(selectedIndex
);
799 // First reflow our dropdown so that we know how tall we should be.
800 ReflowDropdown(aPresContext
, aReflowState
);
801 nsRefPtr
<nsResizeDropdownAtFinalPosition
> resize
=
802 new nsResizeDropdownAtFinalPosition(this);
803 if (NS_SUCCEEDED(aPresContext
->PresShell()->PostReflowCallback(resize
))) {
804 // The reflow callback queue doesn't AddRef so we keep it alive until
805 // it's released in its ReflowFinished / ReflowCallbackCanceled.
809 // Get the width of the vertical scrollbar. That will be the width of the
812 const nsStyleDisplay
*disp
= StyleDisplay();
813 if (IsThemed(disp
) && !aPresContext
->GetTheme()->ThemeNeedsComboboxDropmarker()) {
817 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
818 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
819 buttonWidth
= scrollable
->GetNondisappearingScrollbarWidth(
820 PresContext(), aReflowState
.rendContext
);
821 if (buttonWidth
> aReflowState
.ComputedWidth()) {
826 mDisplayWidth
= aReflowState
.ComputedWidth() - buttonWidth
;
828 nsresult rv
= nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, aReflowState
,
830 NS_ENSURE_SUCCESS(rv
, rv
);
832 // Now set the correct width and height on our button. The width we need to
833 // set always, the height only if we had an auto height.
834 nsRect buttonRect
= mButtonFrame
->GetRect();
835 // If we have a non-intrinsic computed height, our kids should have sized
836 // themselves properly on their own.
837 if (aReflowState
.ComputedHeight() == NS_INTRINSICSIZE
) {
838 // The display frame is going to be the right height and width at this
839 // point. Use its height as the button height.
840 nsRect displayRect
= mDisplayFrame
->GetRect();
841 buttonRect
.height
= displayRect
.height
;
842 buttonRect
.y
= displayRect
.y
;
846 nscoord buttonHeight
= mButtonFrame
->GetSize().height
;
847 nscoord displayHeight
= mDisplayFrame
->GetSize().height
;
849 // The button and display area should be equal heights, unless the computed
850 // height on the combobox is too small to fit their borders and padding.
851 NS_ASSERTION(buttonHeight
== displayHeight
||
852 (aReflowState
.ComputedHeight() < buttonHeight
&&
854 mButtonFrame
->GetUsedBorderAndPadding().TopBottom()) ||
855 (aReflowState
.ComputedHeight() < displayHeight
&&
857 mDisplayFrame
->GetUsedBorderAndPadding().TopBottom()),
858 "Different heights?");
862 if (StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
863 // Make sure the right edge of the button frame stays where it is now
864 buttonRect
.x
-= buttonWidth
- buttonRect
.width
;
866 buttonRect
.width
= buttonWidth
;
867 mButtonFrame
->SetRect(buttonRect
);
872 //--------------------------------------------------------------
875 nsComboboxControlFrame::GetType() const
877 return nsGkAtoms::comboboxControlFrame
;
880 #ifdef DEBUG_FRAME_DUMP
882 nsComboboxControlFrame::GetFrameName(nsAString
& aResult
) const
884 return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult
);
889 //----------------------------------------------------------------------
890 // nsIComboboxControlFrame
891 //----------------------------------------------------------------------
893 nsComboboxControlFrame::ShowDropDown(bool aDoDropDown
)
895 mDelayedShowDropDown
= false;
896 nsEventStates eventStates
= mContent
->AsElement()->State();
897 if (aDoDropDown
&& eventStates
.HasState(NS_EVENT_STATE_DISABLED
)) {
901 if (!mDroppedDown
&& aDoDropDown
) {
902 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
903 if (!fm
|| fm
->GetFocusedContent() == GetContent()) {
904 DropDownPositionState state
= AbsolutelyPositionDropDown();
905 if (state
== eDropDownPositionFinal
) {
906 ShowList(aDoDropDown
); // might destroy us
907 } else if (state
== eDropDownPositionPendingResize
) {
908 // Delay until after the resize reflow, see nsAsyncResize.
909 mDelayedShowDropDown
= true;
912 // Delay until we get focus, see SetFocus().
913 mDelayedShowDropDown
= true;
915 } else if (mDroppedDown
&& !aDoDropDown
) {
916 ShowList(aDoDropDown
); // might destroy us
921 nsComboboxControlFrame::SetDropDown(nsIFrame
* aDropDownFrame
)
923 mDropdownFrame
= aDropDownFrame
;
924 mListControlFrame
= do_QueryFrame(mDropdownFrame
);
928 nsComboboxControlFrame::GetDropDown()
930 return mDropdownFrame
;
933 ///////////////////////////////////////////////////////////////
936 nsComboboxControlFrame::RedisplaySelectedText()
938 nsAutoScriptBlocker scriptBlocker
;
939 return RedisplayText(mListControlFrame
->GetSelectedIndex());
943 nsComboboxControlFrame::RedisplayText(int32_t aIndex
)
945 // Get the text to display
947 mListControlFrame
->GetOptionText(aIndex
, mDisplayedOptionText
);
949 mDisplayedOptionText
.Truncate();
951 mDisplayedIndex
= aIndex
;
953 REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n",
954 NS_LossyConvertUTF16toASCII(mDisplayedOptionText
).get());
956 // Send reflow command because the new text maybe larger
958 if (mDisplayContent
) {
959 // Don't call ActuallyDisplayText(true) directly here since that
960 // could cause recursive frame construction. See bug 283117 and the comment in
961 // HandleRedisplayTextEvent() below.
963 // Revoke outstanding events to avoid out-of-order events which could mean
964 // displaying the wrong text.
965 mRedisplayTextEvent
.Revoke();
967 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
968 "If we happen to run our redisplay event now, we might kill "
971 nsRefPtr
<RedisplayTextEvent
> event
= new RedisplayTextEvent(this);
972 mRedisplayTextEvent
= event
;
973 if (!nsContentUtils::AddScriptRunner(event
))
974 mRedisplayTextEvent
.Forget();
980 nsComboboxControlFrame::HandleRedisplayTextEvent()
982 // First, make sure that the content model is up to date and we've
983 // constructed the frames for all our content in the right places.
984 // Otherwise they'll end up under the wrong insertion frame when we
985 // ActuallyDisplayText, since that flushes out the content sink by
986 // calling SetText on a DOM node with aNotify set to true. See bug
988 nsWeakFrame
weakThis(this);
989 PresContext()->Document()->
990 FlushPendingNotifications(Flush_ContentAndNotify
);
991 if (!weakThis
.IsAlive())
994 // Redirect frame insertions during this method (see GetContentInsertionFrame())
995 // so that any reframing that the frame constructor forces upon us is inserted
996 // into the correct parent (mDisplayFrame). See bug 282607.
997 NS_PRECONDITION(!mInRedisplayText
, "Nested RedisplayText");
998 mInRedisplayText
= true;
999 mRedisplayTextEvent
.Forget();
1001 ActuallyDisplayText(true);
1002 // XXXbz This should perhaps be eResize. Check.
1003 PresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame
,
1004 nsIPresShell::eStyleChange
,
1007 mInRedisplayText
= false;
1011 nsComboboxControlFrame::ActuallyDisplayText(bool aNotify
)
1013 if (mDisplayedOptionText
.IsEmpty()) {
1014 // Have to use a non-breaking space for line-height calculations
1016 static const char16_t space
= 0xA0;
1017 mDisplayContent
->SetText(&space
, 1, aNotify
);
1019 mDisplayContent
->SetText(mDisplayedOptionText
, aNotify
);
1024 nsComboboxControlFrame::GetIndexOfDisplayArea()
1026 return mDisplayedIndex
;
1029 //----------------------------------------------------------------------
1030 // nsISelectControlFrame
1031 //----------------------------------------------------------------------
1033 nsComboboxControlFrame::DoneAddingChildren(bool aIsDone
)
1035 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
1037 return NS_ERROR_FAILURE
;
1039 return listFrame
->DoneAddingChildren(aIsDone
);
1043 nsComboboxControlFrame::AddOption(int32_t aIndex
)
1045 if (aIndex
<= mDisplayedIndex
) {
1049 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
1050 return lcf
->AddOption(aIndex
);
1055 nsComboboxControlFrame::RemoveOption(int32_t aIndex
)
1057 nsWeakFrame
weakThis(this);
1058 if (mListControlFrame
->GetNumberOfOptions() > 0) {
1059 if (aIndex
< mDisplayedIndex
) {
1061 } else if (aIndex
== mDisplayedIndex
) {
1062 mDisplayedIndex
= 0; // IE6 compat
1063 RedisplayText(mDisplayedIndex
);
1067 // If we removed the last option, we need to blank things out
1071 if (!weakThis
.IsAlive())
1074 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
1075 return lcf
->RemoveOption(aIndex
);
1079 nsComboboxControlFrame::OnSetSelectedIndex(int32_t aOldIndex
, int32_t aNewIndex
)
1081 nsAutoScriptBlocker scriptBlocker
;
1082 RedisplayText(aNewIndex
);
1083 NS_ASSERTION(mDropdownFrame
, "No dropdown frame!");
1085 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
1086 NS_ASSERTION(listFrame
, "No list frame!");
1088 return listFrame
->OnSetSelectedIndex(aOldIndex
, aNewIndex
);
1091 // End nsISelectControlFrame
1092 //----------------------------------------------------------------------
1095 nsComboboxControlFrame::HandleEvent(nsPresContext
* aPresContext
,
1096 WidgetGUIEvent
* aEvent
,
1097 nsEventStatus
* aEventStatus
)
1099 NS_ENSURE_ARG_POINTER(aEventStatus
);
1101 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
1105 nsEventStates eventStates
= mContent
->AsElement()->State();
1106 if (eventStates
.HasState(NS_EVENT_STATE_DISABLED
)) {
1110 // If we have style that affects how we are selected, feed event down to
1111 // nsFrame::HandleEvent so that selection takes place when appropriate.
1112 const nsStyleUserInterface
* uiStyle
= StyleUserInterface();
1113 if (uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_NONE
|| uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_DISABLED
)
1114 return nsBlockFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
1121 nsComboboxControlFrame::SetFormProperty(nsIAtom
* aName
, const nsAString
& aValue
)
1123 nsIFormControlFrame
* fcFrame
= do_QueryFrame(mDropdownFrame
);
1125 return NS_NOINTERFACE
;
1128 return fcFrame
->SetFormProperty(aName
, aValue
);
1132 nsComboboxControlFrame::GetContentInsertionFrame() {
1133 return mInRedisplayText
? mDisplayFrame
: mDropdownFrame
->GetContentInsertionFrame();
1137 nsComboboxControlFrame::CreateAnonymousContent(nsTArray
<ContentInfo
>& aElements
)
1139 // The frames used to display the combo box and the button used to popup the dropdown list
1140 // are created through anonymous content. The dropdown list is not created through anonymous
1141 // content because it's frame is initialized specifically for the drop-down case and it is placed
1142 // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the
1143 // layout of the display and button.
1145 // Note: The value attribute of the display content is set when an item is selected in the dropdown list.
1146 // If the content specified below does not honor the value attribute than nothing will be displayed.
1148 // For now the content that is created corresponds to two input buttons. It would be better to create the
1149 // tag as something other than input, but then there isn't any way to create a button frame since it
1150 // isn't possible to set the display type in CSS2 to create a button frame.
1152 // create content used for display
1153 //nsIAtom* tag = NS_NewAtom("mozcombodisplay");
1155 // Add a child text content node for the label
1157 nsNodeInfoManager
*nimgr
= mContent
->NodeInfo()->NodeInfoManager();
1159 mDisplayContent
= new nsTextNode(nimgr
);
1161 // set the value of the text node
1162 mDisplayedIndex
= mListControlFrame
->GetSelectedIndex();
1163 if (mDisplayedIndex
!= -1) {
1164 mListControlFrame
->GetOptionText(mDisplayedIndex
, mDisplayedOptionText
);
1166 ActuallyDisplayText(false);
1168 if (!aElements
.AppendElement(mDisplayContent
))
1169 return NS_ERROR_OUT_OF_MEMORY
;
1171 mButtonContent
= mContent
->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button
);
1172 if (!mButtonContent
)
1173 return NS_ERROR_OUT_OF_MEMORY
;
1175 // make someone to listen to the button. If its pressed by someone like Accessibility
1176 // then open or close the combo box.
1177 mButtonListener
= new nsComboButtonListener(this);
1178 mButtonContent
->AddEventListener(NS_LITERAL_STRING("click"), mButtonListener
,
1181 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
1182 NS_LITERAL_STRING("button"), false);
1183 // Set tabindex="-1" so that the button is not tabbable
1184 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
,
1185 NS_LITERAL_STRING("-1"), false);
1187 if (!aElements
.AppendElement(mButtonContent
))
1188 return NS_ERROR_OUT_OF_MEMORY
;
1194 nsComboboxControlFrame::AppendAnonymousContentTo(nsBaseContentList
& aElements
,
1197 aElements
.MaybeAppendElement(mDisplayContent
);
1198 aElements
.MaybeAppendElement(mButtonContent
);
1201 // XXXbz this is a for-now hack. Now that display:inline-block works,
1202 // need to revisit this.
1203 class nsComboboxDisplayFrame
: public nsBlockFrame
{
1205 NS_DECL_FRAMEARENA_HELPERS
1207 nsComboboxDisplayFrame (nsStyleContext
* aContext
,
1208 nsComboboxControlFrame
* aComboBox
)
1209 : nsBlockFrame(aContext
),
1210 mComboBox(aComboBox
)
1213 // Need this so that line layout knows that this block's width
1214 // depends on the available width.
1215 virtual nsIAtom
* GetType() const;
1217 virtual bool IsFrameOfType(uint32_t aFlags
) const
1219 return nsBlockFrame::IsFrameOfType(aFlags
&
1220 ~(nsIFrame::eReplacedContainsBlock
));
1223 NS_IMETHOD
Reflow(nsPresContext
* aPresContext
,
1224 nsHTMLReflowMetrics
& aDesiredSize
,
1225 const nsHTMLReflowState
& aReflowState
,
1226 nsReflowStatus
& aStatus
);
1228 virtual void BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1229 const nsRect
& aDirtyRect
,
1230 const nsDisplayListSet
& aLists
) MOZ_OVERRIDE
;
1233 nsComboboxControlFrame
* mComboBox
;
1236 NS_IMPL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame
)
1239 nsComboboxDisplayFrame::GetType() const
1241 return nsGkAtoms::comboboxDisplayFrame
;
1245 nsComboboxDisplayFrame::Reflow(nsPresContext
* aPresContext
,
1246 nsHTMLReflowMetrics
& aDesiredSize
,
1247 const nsHTMLReflowState
& aReflowState
,
1248 nsReflowStatus
& aStatus
)
1250 nsHTMLReflowState
state(aReflowState
);
1251 if (state
.ComputedHeight() == NS_INTRINSICSIZE
) {
1252 // Note that the only way we can have a computed height here is if the
1253 // combobox had a specified height. If it didn't, size based on what our
1254 // rows look like, for lack of anything better.
1255 state
.SetComputedHeight(mComboBox
->mListControlFrame
->GetHeightOfARow());
1257 nscoord computedWidth
= mComboBox
->mDisplayWidth
-
1258 state
.ComputedPhysicalBorderPadding().LeftRight();
1259 if (computedWidth
< 0) {
1262 state
.SetComputedWidth(computedWidth
);
1264 return nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, state
, aStatus
);
1268 nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1269 const nsRect
& aDirtyRect
,
1270 const nsDisplayListSet
& aLists
)
1272 nsDisplayListCollection set
;
1273 nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, set
);
1275 // remove background items if parent frame is themed
1276 if (mComboBox
->IsThemed()) {
1277 set
.BorderBackground()->DeleteAll();
1284 nsComboboxControlFrame::CreateFrameFor(nsIContent
* aContent
)
1286 NS_PRECONDITION(nullptr != aContent
, "null ptr");
1288 NS_ASSERTION(mDisplayContent
, "mDisplayContent can't be null!");
1290 if (mDisplayContent
!= aContent
) {
1291 // We only handle the frames for mDisplayContent here
1296 nsIPresShell
*shell
= PresContext()->PresShell();
1297 nsStyleSet
*styleSet
= shell
->StyleSet();
1299 // create the style contexts for the anonymous block frame and text frame
1300 nsRefPtr
<nsStyleContext
> styleContext
;
1301 styleContext
= styleSet
->
1302 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozDisplayComboboxControlFrame
,
1305 nsRefPtr
<nsStyleContext
> textStyleContext
;
1306 textStyleContext
= styleSet
->ResolveStyleForNonElement(mStyleContext
);
1308 // Start by creating our anonymous block frame
1309 mDisplayFrame
= new (shell
) nsComboboxDisplayFrame(styleContext
, this);
1310 mDisplayFrame
->Init(mContent
, this, nullptr);
1312 // Create a text frame and put it inside the block frame
1313 nsIFrame
* textFrame
= NS_NewTextFrame(shell
, textStyleContext
);
1315 // initialize the text frame
1316 textFrame
->Init(aContent
, mDisplayFrame
, nullptr);
1317 mDisplayContent
->SetPrimaryFrame(textFrame
);
1319 nsFrameList
textList(textFrame
, textFrame
);
1320 mDisplayFrame
->SetInitialChildList(kPrincipalList
, textList
);
1321 return mDisplayFrame
;
1325 nsComboboxControlFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
1327 // Revoke any pending RedisplayTextEvent
1328 mRedisplayTextEvent
.Revoke();
1330 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame
*>(this), false);
1333 MOZ_ASSERT(mDropdownFrame
, "mDroppedDown without frame");
1334 nsView
* view
= mDropdownFrame
->GetView();
1336 nsIWidget
* widget
= view
->GetWidget();
1338 widget
->CaptureRollupEvents(this, false);
1342 // Cleanup frames in popup child list
1343 mPopupFrames
.DestroyFramesFrom(aDestructRoot
);
1344 nsContentUtils::DestroyAnonymousContent(&mDisplayContent
);
1345 nsContentUtils::DestroyAnonymousContent(&mButtonContent
);
1346 nsBlockFrame::DestroyFrom(aDestructRoot
);
1350 nsComboboxControlFrame::GetChildList(ChildListID aListID
) const
1352 if (kSelectPopupList
== aListID
) {
1353 return mPopupFrames
;
1355 return nsBlockFrame::GetChildList(aListID
);
1359 nsComboboxControlFrame::GetChildLists(nsTArray
<ChildList
>* aLists
) const
1361 nsBlockFrame::GetChildLists(aLists
);
1362 mPopupFrames
.AppendIfNonempty(aLists
, kSelectPopupList
);
1366 nsComboboxControlFrame::SetInitialChildList(ChildListID aListID
,
1367 nsFrameList
& aChildList
)
1369 nsresult rv
= NS_OK
;
1370 if (kSelectPopupList
== aListID
) {
1371 mPopupFrames
.SetFrames(aChildList
);
1373 for (nsFrameList::Enumerator
e(aChildList
); !e
.AtEnd(); e
.Next()) {
1374 nsCOMPtr
<nsIFormControl
> formControl
=
1375 do_QueryInterface(e
.get()->GetContent());
1376 if (formControl
&& formControl
->GetType() == NS_FORM_BUTTON_BUTTON
) {
1377 mButtonFrame
= e
.get();
1381 NS_ASSERTION(mButtonFrame
, "missing button frame in initial child list");
1382 rv
= nsBlockFrame::SetInitialChildList(aListID
, aChildList
);
1387 //----------------------------------------------------------------------
1389 //----------------------------------------------------------------------
1391 nsComboboxControlFrame::Rollup(uint32_t aCount
, const nsIntPoint
* pos
, nsIContent
** aLastRolledUp
)
1396 nsWeakFrame
weakFrame(this);
1397 mListControlFrame
->AboutToRollup(); // might destroy us
1398 if (!weakFrame
.IsAlive())
1400 ShowDropDown(false); // might destroy us
1401 if (weakFrame
.IsAlive()) {
1402 mListControlFrame
->CaptureMouseEvents(false);
1409 nsComboboxControlFrame::GetRollupWidget()
1411 nsView
* view
= mDropdownFrame
->GetView();
1413 return view
->GetWidget();
1417 nsComboboxControlFrame::RollupFromList()
1419 if (ShowList(false))
1420 mListControlFrame
->CaptureMouseEvents(false);
1424 nsComboboxControlFrame::UpdateRecentIndex(int32_t aIndex
)
1426 int32_t index
= mRecentSelectedIndex
;
1427 if (mRecentSelectedIndex
== NS_SKIP_NOTIFY_INDEX
|| aIndex
== NS_SKIP_NOTIFY_INDEX
)
1428 mRecentSelectedIndex
= aIndex
;
1432 class nsDisplayComboboxFocus
: public nsDisplayItem
{
1434 nsDisplayComboboxFocus(nsDisplayListBuilder
* aBuilder
,
1435 nsComboboxControlFrame
* aFrame
)
1436 : nsDisplayItem(aBuilder
, aFrame
) {
1437 MOZ_COUNT_CTOR(nsDisplayComboboxFocus
);
1439 #ifdef NS_BUILD_REFCNT_LOGGING
1440 virtual ~nsDisplayComboboxFocus() {
1441 MOZ_COUNT_DTOR(nsDisplayComboboxFocus
);
1445 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
1446 nsRenderingContext
* aCtx
);
1447 NS_DISPLAY_DECL_NAME("ComboboxFocus", TYPE_COMBOBOX_FOCUS
)
1450 void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder
* aBuilder
,
1451 nsRenderingContext
* aCtx
)
1453 static_cast<nsComboboxControlFrame
*>(mFrame
)
1454 ->PaintFocus(*aCtx
, ToReferenceFrame());
1458 nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1459 const nsRect
& aDirtyRect
,
1460 const nsDisplayListSet
& aLists
)
1463 printf("%p paint at (%d, %d, %d, %d)\n", this,
1464 aDirtyRect
.x
, aDirtyRect
.y
, aDirtyRect
.width
, aDirtyRect
.height
);
1467 if (aBuilder
->IsForEventDelivery()) {
1468 // Don't allow children to receive events.
1469 // REVIEW: following old GetFrameForPoint
1470 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1472 // REVIEW: Our in-flow child frames are inline-level so they will paint in our
1473 // content list, so we don't need to mess with layers.
1474 nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
1477 // draw a focus indicator only when focus rings should be drawn
1478 nsIDocument
* doc
= mContent
->GetCurrentDoc();
1480 nsPIDOMWindow
* window
= doc
->GetWindow();
1481 if (window
&& window
->ShouldShowFocusRing()) {
1482 nsPresContext
*presContext
= PresContext();
1483 const nsStyleDisplay
*disp
= StyleDisplay();
1484 if ((!IsThemed(disp
) ||
1485 !presContext
->GetTheme()->ThemeDrawsFocusForWidget(disp
->mAppearance
)) &&
1486 mDisplayFrame
&& IsVisibleForPainting(aBuilder
)) {
1487 aLists
.Content()->AppendNewToTop(
1488 new (aBuilder
) nsDisplayComboboxFocus(aBuilder
, this));
1493 DisplaySelectionOverlay(aBuilder
, aLists
.Content());
1496 void nsComboboxControlFrame::PaintFocus(nsRenderingContext
& aRenderingContext
,
1499 /* Do we need to do anything? */
1500 nsEventStates eventStates
= mContent
->AsElement()->State();
1501 if (eventStates
.HasState(NS_EVENT_STATE_DISABLED
) || sFocused
!= this)
1504 aRenderingContext
.PushState();
1505 nsRect clipRect
= mDisplayFrame
->GetRect() + aPt
;
1506 aRenderingContext
.IntersectClip(clipRect
);
1508 // REVIEW: Why does the old code paint mDisplayFrame again? We've
1509 // already painted it in the children above. So clipping it here won't do
1512 /////////////////////
1515 aRenderingContext
.SetLineStyle(nsLineStyle_kDotted
);
1516 aRenderingContext
.SetColor(StyleColor()->mColor
);
1518 //aRenderingContext.DrawRect(clipRect);
1520 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
1521 clipRect
.width
-= onePixel
;
1522 clipRect
.height
-= onePixel
;
1523 aRenderingContext
.DrawLine(clipRect
.TopLeft(), clipRect
.TopRight());
1524 aRenderingContext
.DrawLine(clipRect
.TopRight(), clipRect
.BottomRight());
1525 aRenderingContext
.DrawLine(clipRect
.BottomRight(), clipRect
.BottomLeft());
1526 aRenderingContext
.DrawLine(clipRect
.BottomLeft(), clipRect
.TopLeft());
1528 aRenderingContext
.PopState();
1531 //---------------------------------------------------------
1532 // gets the content (an option) by index and then set it as
1533 // being selected or not selected
1534 //---------------------------------------------------------
1536 nsComboboxControlFrame::OnOptionSelected(int32_t aIndex
, bool aSelected
)
1539 nsISelectControlFrame
*selectFrame
= do_QueryFrame(mListControlFrame
);
1541 selectFrame
->OnOptionSelected(aIndex
, aSelected
);
1545 nsAutoScriptBlocker blocker
;
1546 RedisplayText(aIndex
);
1548 nsWeakFrame
weakFrame(this);
1549 RedisplaySelectedText();
1550 if (weakFrame
.IsAlive()) {
1551 FireValueChangeEvent(); // Fire after old option is unselected
1559 void nsComboboxControlFrame::FireValueChangeEvent()
1561 // Fire ValueChange event to indicate data value of combo box has changed
1562 nsContentUtils::AddScriptRunner(
1563 new nsAsyncDOMEvent(mContent
, NS_LITERAL_STRING("ValueChange"), true,
1568 nsComboboxControlFrame::OnContentReset()
1570 if (mListControlFrame
) {
1571 mListControlFrame
->OnContentReset();
1576 //--------------------------------------------------------
1578 //--------------------------------------------------------
1580 nsComboboxControlFrame::SaveState(nsPresState
** aState
)
1582 if (!mListControlFrame
)
1583 return NS_ERROR_FAILURE
;
1585 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1586 return stateful
->SaveState(aState
);
1590 nsComboboxControlFrame::RestoreState(nsPresState
* aState
)
1592 if (!mListControlFrame
)
1593 return NS_ERROR_FAILURE
;
1595 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1596 NS_ASSERTION(stateful
, "Must implement nsIStatefulFrame");
1597 return stateful
->RestoreState(aState
);
1602 // Camino uses a native widget for the combobox
1603 // popup, which affects drawing and event
1604 // handling here and in nsListControlFrame.
1606 // Also, Fennec use a custom combobox built-in widget
1611 nsComboboxControlFrame::ToolkitHasNativePopup()
1613 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
1617 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */