1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Pierre Phaneuf <pp@ludusdesign.com>
24 * Mats Palmgren <mats.palmgren@bredband.net>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsReadableUtils.h"
41 #include "nsComboboxControlFrame.h"
42 #include "nsIDOMEventTarget.h"
43 #include "nsFrameManager.h"
44 #include "nsFormControlFrame.h"
45 #include "nsGfxButtonControlFrame.h"
46 #include "nsGkAtoms.h"
47 #include "nsCSSAnonBoxes.h"
48 #include "nsHTMLParts.h"
49 #include "nsIFormControl.h"
50 #include "nsINameSpaceManager.h"
51 #include "nsIDOMElement.h"
52 #include "nsIListControlFrame.h"
53 #include "nsIDOMHTMLCollection.h"
54 #include "nsIDOMHTMLSelectElement.h"
55 #include "nsIDOMHTMLOptionElement.h"
56 #include "nsIDOMNSHTMLOptionCollectn.h"
57 #include "nsIPresShell.h"
58 #include "nsIDeviceContext.h"
60 #include "nsIViewManager.h"
61 #include "nsIScrollableView.h"
62 #include "nsEventDispatcher.h"
63 #include "nsIEventStateManager.h"
64 #include "nsIEventListenerManager.h"
65 #include "nsIDOMNode.h"
66 #include "nsIPrivateDOMEvent.h"
67 #include "nsISelectControlFrame.h"
69 #include "nsISupportsPrimitives.h"
70 #include "nsIComponentManager.h"
71 #include "nsContentUtils.h"
72 #include "nsTextFragment.h"
73 #include "nsCSSFrameConstructor.h"
74 #include "nsIDocument.h"
75 #include "nsINodeInfo.h"
76 #include "nsIScrollableFrame.h"
77 #include "nsListControlFrame.h"
78 #include "nsContentCID.h"
80 #include "nsIAccessibilityService.h"
82 #include "nsIServiceManager.h"
83 #include "nsIDOMNode.h"
84 #include "nsGUIEvent.h"
85 #include "nsAutoPtr.h"
86 #include "nsStyleSet.h"
87 #include "nsNodeInfoManager.h"
88 #include "nsContentCreatorFunctions.h"
89 #include "nsLayoutUtils.h"
90 #include "nsDisplayList.h"
92 #include "nsThemeConstants.h"
95 nsComboboxControlFrame::RedisplayTextEvent::Run()
98 mControlFrame
->HandleRedisplayTextEvent();
104 #define FIX_FOR_BUG_53259
106 // Drop down list event management.
107 // The combo box uses the following strategy for managing the drop-down list.
108 // If the combo box or it's arrow button is clicked on the drop-down list is displayed
109 // If mouse exit's the combo box with the drop-down list displayed the drop-down list
110 // is asked to capture events
111 // The drop-down list will capture all events including mouse down and up and will always
112 // return with ListWasSelected method call regardless of whether an item in the list was
113 // actually selected.
114 // The ListWasSelected code will turn off mouse-capture for the drop-down list.
115 // The drop-down list does not explicitly set capture when it is in the drop-down mode.
118 //XXX: This is temporary. It simulates pseudo states by using a attribute selector on
120 const PRInt32 kSizeNotSet
= -1;
123 * Helper class that listens to the combo boxes button. If the button is pressed the
124 * combo box is toggled to open or close. this is used by Accessibility which presses
125 * that button Programmatically.
127 class nsComboButtonListener
: public nsIDOMMouseListener
132 NS_IMETHOD
HandleEvent(nsIDOMEvent
* anEvent
) { return PR_FALSE
; }
133 NS_IMETHOD
MouseDown(nsIDOMEvent
* aMouseEvent
) { return PR_FALSE
; }
134 NS_IMETHOD
MouseUp(nsIDOMEvent
* aMouseEvent
) { return PR_FALSE
; }
135 NS_IMETHOD
MouseDblClick(nsIDOMEvent
* aMouseEvent
) { return PR_FALSE
; }
136 NS_IMETHOD
MouseOver(nsIDOMEvent
* aMouseEvent
) { return PR_FALSE
; }
137 NS_IMETHOD
MouseOut(nsIDOMEvent
* aMouseEvent
) { return PR_FALSE
; }
139 NS_IMETHOD
MouseClick(nsIDOMEvent
* aMouseEvent
)
141 mComboBox
->ShowDropDown(!mComboBox
->IsDroppedDown());
145 nsComboButtonListener(nsComboboxControlFrame
* aCombobox
)
147 mComboBox
= aCombobox
;
150 virtual ~nsComboButtonListener() {}
152 nsComboboxControlFrame
* mComboBox
;
155 NS_IMPL_ISUPPORTS2(nsComboButtonListener
,
159 // static class data member for Bug 32920
160 nsComboboxControlFrame
* nsComboboxControlFrame::mFocused
= nsnull
;
163 NS_NewComboboxControlFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
, PRUint32 aStateFlags
)
165 nsComboboxControlFrame
* it
= new (aPresShell
) nsComboboxControlFrame(aContext
);
168 // set the state flags (if any are provided)
169 it
->AddStateBits(aStateFlags
);
175 //-----------------------------------------------------------
176 // Reflow Debugging Macros
177 // These let us "see" how many reflow counts are happening
178 //-----------------------------------------------------------
179 #ifdef DO_REFLOW_COUNTER
181 #define MAX_REFLOW_CNT 1024
182 static PRInt32 gTotalReqs
= 0;;
183 static PRInt32 gTotalReflows
= 0;;
184 static PRInt32 gReflowControlCntRQ
[MAX_REFLOW_CNT
];
185 static PRInt32 gReflowControlCnt
[MAX_REFLOW_CNT
];
186 static PRInt32 gReflowInx
= -1;
188 #define REFLOW_COUNTER() \
189 if (mReflowId > -1) \
190 gReflowControlCnt[mReflowId]++;
192 #define REFLOW_COUNTER_REQUEST() \
193 if (mReflowId > -1) \
194 gReflowControlCntRQ[mReflowId]++;
196 #define REFLOW_COUNTER_DUMP(__desc) \
197 if (mReflowId > -1) {\
198 gTotalReqs += gReflowControlCntRQ[mReflowId];\
199 gTotalReflows += gReflowControlCnt[mReflowId];\
200 printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \
201 mReflowId, (__desc), \
202 gReflowControlCnt[mReflowId], \
203 gReflowControlCntRQ[mReflowId],\
204 gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\
207 #define REFLOW_COUNTER_INIT() \
208 if (gReflowInx < MAX_REFLOW_CNT) { \
210 mReflowId = gReflowInx; \
211 gReflowControlCnt[mReflowId] = 0; \
212 gReflowControlCntRQ[mReflowId] = 0; \
218 #define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
219 #define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
220 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
221 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
223 #else //-------------
225 #define REFLOW_COUNTER_REQUEST()
226 #define REFLOW_COUNTER()
227 #define REFLOW_COUNTER_DUMP(__desc)
228 #define REFLOW_COUNTER_INIT()
230 #define REFLOW_DEBUG_MSG(_msg)
231 #define REFLOW_DEBUG_MSG2(_msg1, _msg2)
232 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3)
233 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4)
238 //------------------------------------------
239 // This is for being VERY noisy
240 //------------------------------------------
242 #define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
243 #define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
244 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
245 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
247 #define REFLOW_NOISY_MSG(_msg)
248 #define REFLOW_NOISY_MSG2(_msg1, _msg2)
249 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3)
250 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4)
253 //------------------------------------------
254 // Displays value in pixels or twips
255 //------------------------------------------
257 #define PX(__v) __v / 15
262 //------------------------------------------------------
263 //-- Done with macros
264 //------------------------------------------------------
266 nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext
* aContext
)
267 : nsBlockFrame(aContext
),
270 mListControlFrame
= nsnull
;
271 mDroppedDown
= PR_FALSE
;
272 mDisplayFrame
= nsnull
;
273 mButtonFrame
= nsnull
;
274 mDropdownFrame
= nsnull
;
276 mInRedisplayText
= PR_FALSE
;
278 mRecentSelectedIndex
= NS_SKIP_NOTIFY_INDEX
;
280 REFLOW_COUNTER_INIT()
283 //--------------------------------------------------------------
284 nsComboboxControlFrame::~nsComboboxControlFrame()
286 REFLOW_COUNTER_DUMP("nsCCF");
289 //--------------------------------------------------------------
291 NS_QUERYFRAME_HEAD(nsComboboxControlFrame
)
292 NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame
)
293 NS_QUERYFRAME_ENTRY(nsIFormControlFrame
)
294 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator
)
295 NS_QUERYFRAME_ENTRY(nsISelectControlFrame
)
296 NS_QUERYFRAME_ENTRY(nsIStatefulFrame
)
297 NS_QUERYFRAME_ENTRY(nsIScrollableViewProvider
)
298 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame
)
300 NS_IMPL_QUERY_INTERFACE1(nsComboboxControlFrame
, nsIRollupListener
)
302 NS_IMETHODIMP_(nsrefcnt
)
303 nsComboboxControlFrame::AddRef()
308 NS_IMETHODIMP_(nsrefcnt
)
309 nsComboboxControlFrame::Release()
315 NS_IMETHODIMP
nsComboboxControlFrame::GetAccessible(nsIAccessible
** aAccessible
)
317 nsCOMPtr
<nsIAccessibilityService
> accService
= do_GetService("@mozilla.org/accessibilityService;1");
320 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(mContent
);
321 nsCOMPtr
<nsIWeakReference
> weakShell(do_GetWeakReference(PresContext()->PresShell()));
322 return accService
->CreateHTMLComboboxAccessible(node
, weakShell
, aAccessible
);
325 return NS_ERROR_FAILURE
;
330 nsComboboxControlFrame::SetFocus(PRBool aOn
, PRBool aRepaint
)
332 nsWeakFrame
weakFrame(this);
334 nsListControlFrame::ComboboxFocusSet();
339 mListControlFrame
->ComboboxFinish(mDisplayedIndex
); // might destroy us
340 if (!weakFrame
.IsAlive()) {
344 // May delete |this|.
345 mListControlFrame
->FireOnChange();
348 if (!weakFrame
.IsAlive()) {
352 // This is needed on a temporary basis. It causes the focus
353 // rect to be drawn. This is much faster than ReResolvingStyle
355 Invalidate(nsRect(0,0,mRect
.width
,mRect
.height
));
357 // Make sure the content area gets updated for where the dropdown was
358 // This is only needed for embedding, the focus may go to
359 // the chrome that is not part of the Gecko system (Bug 83493)
360 // XXX this is rather inefficient
361 nsIViewManager
* vm
= PresContext()->GetViewManager();
363 vm
->UpdateAllViews(NS_VMREFRESH_NO_SYNC
);
368 nsComboboxControlFrame::ShowPopup(PRBool aShowPopup
)
370 nsIView
* view
= mDropdownFrame
->GetView();
371 nsIViewManager
* viewManager
= view
->GetViewManager();
374 nsRect rect
= mDropdownFrame
->GetRect();
376 viewManager
->ResizeView(view
, rect
);
377 viewManager
->SetViewVisibility(view
, nsViewVisibility_kShow
);
379 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
380 nsRect
emptyRect(0, 0, 0, 0);
381 viewManager
->ResizeView(view
, emptyRect
);
384 // fire a popup dom event
385 nsEventStatus status
= nsEventStatus_eIgnore
;
386 nsMouseEvent
event(PR_TRUE
, aShowPopup
?
387 NS_XUL_POPUP_SHOWING
: NS_XUL_POPUP_HIDING
, nsnull
,
388 nsMouseEvent::eReal
);
390 nsCOMPtr
<nsIPresShell
> shell
= PresContext()->GetPresShell();
392 shell
->HandleDOMEventWithTarget(mContent
, &event
, &status
);
396 nsComboboxControlFrame::ShowList(nsPresContext
* aPresContext
, PRBool aShowList
)
398 nsCOMPtr
<nsIPresShell
> shell
= PresContext()->GetPresShell();
400 nsWeakFrame
weakFrame(this);
401 ShowPopup(aShowList
); // might destroy us
402 if (!weakFrame
.IsAlive()) {
406 mDroppedDown
= aShowList
;
408 // The listcontrol frame will call back to the nsComboboxControlFrame's
409 // ListWasSelected which will stop the capture.
410 mListControlFrame
->AboutToDropDown();
411 mListControlFrame
->CaptureMouseEvents(PR_TRUE
);
414 // XXXbz so why do we need to flush here, exactly?
415 shell
->GetDocument()->FlushPendingNotifications(Flush_Layout
);
416 if (!weakFrame
.IsAlive()) {
420 nsIFrame
* listFrame
= do_QueryFrame(mListControlFrame
);
422 nsIView
* view
= listFrame
->GetView();
423 NS_ASSERTION(view
, "nsComboboxControlFrame view is null");
425 nsIWidget
* widget
= view
->GetWidget();
427 widget
->CaptureRollupEvents(this, mDroppedDown
, mDroppedDown
);
431 return weakFrame
.IsAlive();
435 nsComboboxControlFrame::ReflowDropdown(nsPresContext
* aPresContext
,
436 const nsHTMLReflowState
& aReflowState
)
438 // All we want out of it later on, really, is the height of a row, so we
439 // don't even need to cache mDropdownFrame's ascent or anything. If we don't
440 // need to reflow it, just bail out here.
441 if (!aReflowState
.ShouldReflowAllKids() &&
442 !NS_SUBTREE_DIRTY(mDropdownFrame
)) {
446 // XXXbz this will, for small-height dropdowns, have extra space on the right
447 // edge for the scrollbar we don't show... but that's the best we can do here
449 nsSize
availSize(aReflowState
.availableWidth
, NS_UNCONSTRAINEDSIZE
);
450 nsHTMLReflowState
kidReflowState(aPresContext
, aReflowState
, mDropdownFrame
,
453 // If the dropdown's intrinsic width is narrower than our specified width,
454 // then expand it out. We want our border-box width to end up the same as
455 // the dropdown's so account for both sets of mComputedBorderPadding.
456 nscoord forcedWidth
= aReflowState
.ComputedWidth() +
457 aReflowState
.mComputedBorderPadding
.LeftRight() -
458 kidReflowState
.mComputedBorderPadding
.LeftRight();
459 kidReflowState
.SetComputedWidth(PR_MAX(kidReflowState
.ComputedWidth(),
462 // ensure we start off hidden
463 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
464 nsIView
* view
= mDropdownFrame
->GetView();
465 nsIViewManager
* viewManager
= view
->GetViewManager();
466 viewManager
->SetViewVisibility(view
, nsViewVisibility_kHide
);
467 nsRect
emptyRect(0, 0, 0, 0);
468 viewManager
->ResizeView(view
, emptyRect
);
471 // Allow the child to move/size/change-visibility its view if it's currently
473 PRInt32 flags
= NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_VISIBILITY
| NS_FRAME_NO_SIZE_VIEW
;
477 nsRect rect
= mDropdownFrame
->GetRect();
478 nsHTMLReflowMetrics desiredSize
;
479 nsReflowStatus ignoredStatus
;
480 nsresult rv
= ReflowChild(mDropdownFrame
, aPresContext
, desiredSize
,
481 kidReflowState
, rect
.x
, rect
.y
, flags
,
484 // Set the child's width and height to it's desired size
485 FinishReflowChild(mDropdownFrame
, aPresContext
, &kidReflowState
,
486 desiredSize
, rect
.x
, rect
.y
, flags
);
491 nsComboboxControlFrame::AbsolutelyPositionDropDown()
493 // Position the dropdown list. It is positioned below the display frame if there is enough
494 // room on the screen to display the entire list. Otherwise it is placed above the display
497 // Note: As first glance, it appears that you could simply get the absolute bounding box for the
498 // dropdown list by first getting its view, then getting the view's nsIWidget, then asking the nsIWidget
499 // for it's AbsoluteBounds. The problem with this approach, is that the dropdown lists y location can
500 // change based on whether the dropdown is placed below or above the display frame.
501 // The approach, taken here is to get use the absolute position of the display frame and use it's location
502 // to determine if the dropdown will go offscreen.
504 // Use the height calculated for the area frame so it includes both
505 // the display and button heights.
506 nscoord dropdownYOffset
= GetRect().height
;
507 nsPresContext
* presContext
= PresContext();
508 // XXX: Enable this code to debug popping up above the display frame, rather than below it
509 nsSize dropdownSize
= mDropdownFrame
->GetSize();
511 nscoord screenHeightInPixels
= 0;
512 if (NS_SUCCEEDED(nsFormControlFrame::GetScreenHeight(presContext
, screenHeightInPixels
))) {
513 // Get the height of the dropdown list in pixels.
514 nscoord absoluteDropDownHeight
= presContext
->AppUnitsToDevPixels(dropdownSize
.height
);
515 // Check to see if the drop-down list will go offscreen
516 if (GetScreenRect().YMost() + absoluteDropDownHeight
> screenHeightInPixels
) {
517 // move the dropdown list up
518 dropdownYOffset
= - (dropdownSize
.height
);
522 nsPoint dropdownPosition
;
523 const nsStyleVisibility
* vis
= GetStyleVisibility();
524 if (vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) {
525 // Align the right edge of the drop-down with the right edge of the control.
526 dropdownPosition
.x
= GetRect().width
- dropdownSize
.width
;
528 dropdownPosition
.x
= 0;
530 dropdownPosition
.y
= dropdownYOffset
;
532 mDropdownFrame
->SetPosition(dropdownPosition
);
535 //----------------------------------------------------------
537 //----------------------------------------------------------
538 #ifdef DO_REFLOW_DEBUG
539 static int myCounter
= 0;
541 static void printSize(char * aDesc
, nscoord aSize
)
543 printf(" %s: ", aDesc
);
544 if (aSize
== NS_UNCONSTRAINEDSIZE
) {
547 printf("%d", PX(aSize
));
552 //-------------------------------------------------------------------
553 //-- Main Reflow for the Combobox
554 //-------------------------------------------------------------------
557 nsComboboxControlFrame::GetIntrinsicWidth(nsIRenderingContext
* aRenderingContext
,
558 nsLayoutUtils::IntrinsicWidthType aType
)
560 // get the scrollbar width, we'll use this later
561 nscoord scrollbarWidth
= 0;
562 nsPresContext
* presContext
= PresContext();
563 if (mListControlFrame
) {
564 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
565 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
567 scrollable
->GetDesiredScrollbarSizes(presContext
, aRenderingContext
).LeftRight();
570 nscoord displayWidth
= 0;
571 if (NS_LIKELY(mDisplayFrame
)) {
572 displayWidth
= nsLayoutUtils::IntrinsicForContainer(aRenderingContext
,
577 if (mDropdownFrame
) {
578 nscoord dropdownContentWidth
;
579 if (aType
== nsLayoutUtils::MIN_WIDTH
) {
580 dropdownContentWidth
= mDropdownFrame
->GetMinWidth(aRenderingContext
);
582 NS_ASSERTION(aType
== nsLayoutUtils::PREF_WIDTH
, "Unexpected type");
583 dropdownContentWidth
= mDropdownFrame
->GetPrefWidth(aRenderingContext
);
585 dropdownContentWidth
= NSCoordSaturatingSubtract(dropdownContentWidth
,
589 displayWidth
= PR_MAX(dropdownContentWidth
, displayWidth
);
592 // add room for the dropmarker button if there is one
593 if (!IsThemed() || presContext
->GetTheme()->ThemeNeedsComboboxDropmarker())
594 displayWidth
+= scrollbarWidth
;
601 nsComboboxControlFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
604 DISPLAY_MIN_WIDTH(this, minWidth
);
605 minWidth
= GetIntrinsicWidth(aRenderingContext
, nsLayoutUtils::MIN_WIDTH
);
610 nsComboboxControlFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
613 DISPLAY_PREF_WIDTH(this, prefWidth
);
614 prefWidth
= GetIntrinsicWidth(aRenderingContext
, nsLayoutUtils::PREF_WIDTH
);
619 nsComboboxControlFrame::Reflow(nsPresContext
* aPresContext
,
620 nsHTMLReflowMetrics
& aDesiredSize
,
621 const nsHTMLReflowState
& aReflowState
,
622 nsReflowStatus
& aStatus
)
624 // Constraints we try to satisfy:
626 // 1) Default width of button is the vertical scrollbar size
627 // 2) If the width of button is bigger than our width, set width of
629 // 3) Default height of button is height of display area
630 // 4) Width of display area is whatever is left over from our width after
631 // allocating width for the button.
632 // 5) Height of display area is GetHeightOfARow() on the
633 // mListControlFrame.
635 if (!mDisplayFrame
|| !mButtonFrame
|| !mDropdownFrame
) {
636 NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!");
637 return NS_ERROR_UNEXPECTED
;
640 // Make sure the displayed text is the same as the selected option, bug 297389.
641 PRInt32 selectedIndex
;
642 nsAutoString selectedOptionText
;
644 selectedIndex
= mListControlFrame
->GetSelectedIndex();
647 // In dropped down mode the "selected index" is the hovered menu item,
648 // we want the last selected item which is |mDisplayedIndex| in this case.
649 selectedIndex
= mDisplayedIndex
;
651 if (selectedIndex
!= -1) {
652 mListControlFrame
->GetOptionText(selectedIndex
, selectedOptionText
);
654 if (mDisplayedOptionText
!= selectedOptionText
) {
655 RedisplayText(selectedIndex
);
658 // First reflow our dropdown so that we know how tall we should be.
659 ReflowDropdown(aPresContext
, aReflowState
);
661 // Get the width of the vertical scrollbar. That will be the width of the
664 const nsStyleDisplay
*disp
= GetStyleDisplay();
665 if (IsThemed(disp
) && !aPresContext
->GetTheme()->ThemeNeedsComboboxDropmarker()) {
669 nsIScrollableFrame
* scrollable
= do_QueryFrame(mListControlFrame
);
670 NS_ASSERTION(scrollable
, "List must be a scrollable frame");
672 scrollable
->GetDesiredScrollbarSizes(PresContext(),
673 aReflowState
.rendContext
).LeftRight();
674 if (buttonWidth
> aReflowState
.ComputedWidth()) {
679 mDisplayWidth
= aReflowState
.ComputedWidth() - buttonWidth
;
681 nsresult rv
= nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, aReflowState
,
683 NS_ENSURE_SUCCESS(rv
, rv
);
685 // Now set the correct width and height on our button. The width we need to
686 // set always, the height only if we had an auto height.
687 nsRect buttonRect
= mButtonFrame
->GetRect();
688 // If we have a non-intrinsic computed height, our kids should have sized
689 // themselves properly on their own.
690 if (aReflowState
.ComputedHeight() == NS_INTRINSICSIZE
) {
691 // The display frame is going to be the right height and width at this
692 // point. Use its height as the button height.
693 nsRect displayRect
= mDisplayFrame
->GetRect();
694 buttonRect
.height
= displayRect
.height
;
695 buttonRect
.y
= displayRect
.y
;
699 nscoord buttonHeight
= mButtonFrame
->GetSize().height
;
700 nscoord displayHeight
= mDisplayFrame
->GetSize().height
;
702 // The button and display area should be equal heights, unless the computed
703 // height on the combobox is too small to fit their borders and padding.
704 NS_ASSERTION(buttonHeight
== displayHeight
||
705 (aReflowState
.ComputedHeight() < buttonHeight
&&
707 mButtonFrame
->GetUsedBorderAndPadding().TopBottom()) ||
708 (aReflowState
.ComputedHeight() < displayHeight
&&
710 mDisplayFrame
->GetUsedBorderAndPadding().TopBottom()),
711 "Different heights?");
715 if (GetStyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
716 // Make sure the right edge of the button frame stays where it is now
717 buttonRect
.x
-= buttonWidth
- buttonRect
.width
;
719 buttonRect
.width
= buttonWidth
;
720 mButtonFrame
->SetRect(buttonRect
);
725 //--------------------------------------------------------------
728 nsComboboxControlFrame::GetType() const
730 return nsGkAtoms::comboboxControlFrame
;
735 nsComboboxControlFrame::GetFrameName(nsAString
& aResult
) const
737 return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult
);
742 //----------------------------------------------------------------------
743 // nsIComboboxControlFrame
744 //----------------------------------------------------------------------
746 nsComboboxControlFrame::ShowDropDown(PRBool aDoDropDown
)
748 if (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
)) {
752 if (!mDroppedDown
&& aDoDropDown
) {
753 if (mListControlFrame
) {
754 mListControlFrame
->SyncViewWithFrame();
756 ShowList(PresContext(), aDoDropDown
); // might destroy us
757 } else if (mDroppedDown
&& !aDoDropDown
) {
758 ShowList(PresContext(), aDoDropDown
); // might destroy us
763 nsComboboxControlFrame::SetDropDown(nsIFrame
* aDropDownFrame
)
765 mDropdownFrame
= aDropDownFrame
;
766 mListControlFrame
= do_QueryFrame(mDropdownFrame
);
770 nsComboboxControlFrame::GetDropDown()
772 return mDropdownFrame
;
775 ///////////////////////////////////////////////////////////////
778 nsComboboxControlFrame::RedisplaySelectedText()
780 return RedisplayText(mListControlFrame
->GetSelectedIndex());
784 nsComboboxControlFrame::RedisplayText(PRInt32 aIndex
)
786 // Get the text to display
788 mListControlFrame
->GetOptionText(aIndex
, mDisplayedOptionText
);
790 mDisplayedOptionText
.Truncate();
792 mDisplayedIndex
= aIndex
;
794 REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n",
795 NS_LossyConvertUTF16toASCII(mDisplayedOptionText
).get());
797 // Send reflow command because the new text maybe larger
799 if (mDisplayContent
) {
800 // Don't call ActuallyDisplayText(PR_TRUE) directly here since that
801 // could cause recursive frame construction. See bug 283117 and the comment in
802 // HandleRedisplayTextEvent() below.
804 // Revoke outstanding events to avoid out-of-order events which could mean
805 // displaying the wrong text.
806 mRedisplayTextEvent
.Revoke();
808 nsRefPtr
<RedisplayTextEvent
> event
= new RedisplayTextEvent(this);
809 rv
= NS_DispatchToCurrentThread(event
);
810 if (NS_SUCCEEDED(rv
))
811 mRedisplayTextEvent
= event
;
817 nsComboboxControlFrame::HandleRedisplayTextEvent()
819 // First, make sure that the content model is up to date and we've
820 // constructed the frames for all our content in the right places.
821 // Otherwise they'll end up under the wrong insertion frame when we
822 // ActuallyDisplayText, since that flushes out the content sink by
823 // calling SetText on a DOM node with aNotify set to true. See bug
825 PresContext()->Document()->
826 FlushPendingNotifications(Flush_ContentAndNotify
);
828 // Redirect frame insertions during this method (see GetContentInsertionFrame())
829 // so that any reframing that the frame constructor forces upon us is inserted
830 // into the correct parent (mDisplayFrame). See bug 282607.
831 NS_PRECONDITION(!mInRedisplayText
, "Nested RedisplayText");
832 mInRedisplayText
= PR_TRUE
;
833 mRedisplayTextEvent
.Forget();
835 ActuallyDisplayText(PR_TRUE
);
836 // XXXbz This should perhaps be eResize. Check.
837 PresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame
,
838 nsIPresShell::eStyleChange
,
841 mInRedisplayText
= PR_FALSE
;
845 nsComboboxControlFrame::ActuallyDisplayText(PRBool aNotify
)
847 if (mDisplayedOptionText
.IsEmpty()) {
848 // Have to use a non-breaking space for line-height calculations
850 static const PRUnichar space
= 0xA0;
851 mDisplayContent
->SetText(&space
, 1, aNotify
);
853 mDisplayContent
->SetText(mDisplayedOptionText
, aNotify
);
858 nsComboboxControlFrame::GetIndexOfDisplayArea()
860 return mDisplayedIndex
;
863 //----------------------------------------------------------------------
864 // nsISelectControlFrame
865 //----------------------------------------------------------------------
867 nsComboboxControlFrame::DoneAddingChildren(PRBool aIsDone
)
869 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
871 return NS_ERROR_FAILURE
;
873 return listFrame
->DoneAddingChildren(aIsDone
);
877 nsComboboxControlFrame::AddOption(nsPresContext
* aPresContext
, PRInt32 aIndex
)
879 if (aIndex
<= mDisplayedIndex
) {
883 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
884 return lcf
->AddOption(aPresContext
, aIndex
);
889 nsComboboxControlFrame::RemoveOption(nsPresContext
* aPresContext
, PRInt32 aIndex
)
891 if (mListControlFrame
->GetNumberOfOptions() > 0) {
892 if (aIndex
< mDisplayedIndex
) {
894 } else if (aIndex
== mDisplayedIndex
) {
895 mDisplayedIndex
= 0; // IE6 compat
896 RedisplayText(mDisplayedIndex
);
900 // If we removed the last option, we need to blank things out
904 nsListControlFrame
* lcf
= static_cast<nsListControlFrame
*>(mDropdownFrame
);
905 return lcf
->RemoveOption(aPresContext
, aIndex
);
909 nsComboboxControlFrame::GetOptionSelected(PRInt32 aIndex
, PRBool
* aValue
)
911 NS_ASSERTION(mDropdownFrame
, "No dropdown frame!");
913 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
914 NS_ASSERTION(listFrame
, "No list frame!");
916 return listFrame
->GetOptionSelected(aIndex
, aValue
);
920 nsComboboxControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex
, PRInt32 aNewIndex
)
922 RedisplayText(aNewIndex
);
923 NS_ASSERTION(mDropdownFrame
, "No dropdown frame!");
925 nsISelectControlFrame
* listFrame
= do_QueryFrame(mDropdownFrame
);
926 NS_ASSERTION(listFrame
, "No list frame!");
928 return listFrame
->OnSetSelectedIndex(aOldIndex
, aNewIndex
);
931 // End nsISelectControlFrame
932 //----------------------------------------------------------------------
935 nsComboboxControlFrame::HandleEvent(nsPresContext
* aPresContext
,
937 nsEventStatus
* aEventStatus
)
939 NS_ENSURE_ARG_POINTER(aEventStatus
);
941 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
944 if (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
)) {
948 // If we have style that affects how we are selected, feed event down to
949 // nsFrame::HandleEvent so that selection takes place when appropriate.
950 const nsStyleUserInterface
* uiStyle
= GetStyleUserInterface();
951 if (uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_NONE
|| uiStyle
->mUserInput
== NS_STYLE_USER_INPUT_DISABLED
)
952 return nsBlockFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
959 nsComboboxControlFrame::SetFormProperty(nsIAtom
* aName
, const nsAString
& aValue
)
961 nsIFormControlFrame
* fcFrame
= do_QueryFrame(mDropdownFrame
);
963 return NS_NOINTERFACE
;
966 return fcFrame
->SetFormProperty(aName
, aValue
);
970 nsComboboxControlFrame::GetFormProperty(nsIAtom
* aName
, nsAString
& aValue
) const
972 nsIFormControlFrame
* fcFrame
= do_QueryFrame(mDropdownFrame
);
974 return NS_ERROR_FAILURE
;
977 return fcFrame
->GetFormProperty(aName
, aValue
);
981 nsComboboxControlFrame::GetContentInsertionFrame() {
982 return mInRedisplayText
? mDisplayFrame
: mDropdownFrame
->GetContentInsertionFrame();
986 nsComboboxControlFrame::CreateAnonymousContent(nsTArray
<nsIContent
*>& aElements
)
988 // The frames used to display the combo box and the button used to popup the dropdown list
989 // are created through anonymous content. The dropdown list is not created through anonymous
990 // content because it's frame is initialized specifically for the drop-down case and it is placed
991 // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the
992 // layout of the display and button.
994 // Note: The value attribute of the display content is set when an item is selected in the dropdown list.
995 // If the content specified below does not honor the value attribute than nothing will be displayed.
997 // For now the content that is created corresponds to two input buttons. It would be better to create the
998 // tag as something other than input, but then there isn't any way to create a button frame since it
999 // isn't possible to set the display type in CSS2 to create a button frame.
1001 // create content used for display
1002 //nsIAtom* tag = NS_NewAtom("mozcombodisplay");
1004 // Add a child text content node for the label
1006 nsNodeInfoManager
*nimgr
= mContent
->NodeInfo()->NodeInfoManager();
1008 NS_NewTextNode(getter_AddRefs(mDisplayContent
), nimgr
);
1009 if (!mDisplayContent
)
1010 return NS_ERROR_OUT_OF_MEMORY
;
1012 // set the value of the text node
1013 mDisplayedIndex
= mListControlFrame
->GetSelectedIndex();
1014 if (mDisplayedIndex
!= -1) {
1015 mListControlFrame
->GetOptionText(mDisplayedIndex
, mDisplayedOptionText
);
1017 ActuallyDisplayText(PR_FALSE
);
1019 if (!aElements
.AppendElement(mDisplayContent
))
1020 return NS_ERROR_OUT_OF_MEMORY
;
1022 nsCOMPtr
<nsINodeInfo
> nodeInfo
;
1023 nodeInfo
= nimgr
->GetNodeInfo(nsGkAtoms::input
, nsnull
, kNameSpaceID_None
);
1025 // create button which drops the list down
1026 NS_NewHTMLElement(getter_AddRefs(mButtonContent
), nodeInfo
, PR_FALSE
);
1027 if (!mButtonContent
)
1028 return NS_ERROR_OUT_OF_MEMORY
;
1030 // make someone to listen to the button. If its pressed by someone like Accessibility
1031 // then open or close the combo box.
1032 mButtonListener
= new nsComboButtonListener(this);
1033 if (!mButtonListener
)
1034 return NS_ERROR_OUT_OF_MEMORY
;
1035 mButtonContent
->AddEventListenerByIID(mButtonListener
,
1036 NS_GET_IID(nsIDOMMouseListener
));
1038 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
1039 NS_LITERAL_STRING("button"), PR_FALSE
);
1040 // Set tabindex="-1" so that the button is not tabbable
1041 mButtonContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::tabindex
,
1042 NS_LITERAL_STRING("-1"), PR_FALSE
);
1044 if (!aElements
.AppendElement(mButtonContent
))
1045 return NS_ERROR_OUT_OF_MEMORY
;
1050 // XXXbz this is a for-now hack. Now that display:inline-block works,
1051 // need to revisit this.
1052 class nsComboboxDisplayFrame
: public nsBlockFrame
{
1054 nsComboboxDisplayFrame (nsStyleContext
* aContext
,
1055 nsComboboxControlFrame
* aComboBox
)
1056 : nsBlockFrame(aContext
),
1057 mComboBox(aComboBox
)
1060 // Need this so that line layout knows that this block's width
1061 // depends on the available width.
1062 virtual nsIAtom
* GetType() const;
1064 virtual PRBool
IsFrameOfType(PRUint32 aFlags
) const
1066 return nsBlockFrame::IsFrameOfType(aFlags
&
1067 ~(nsIFrame::eReplacedContainsBlock
));
1070 NS_IMETHOD
Reflow(nsPresContext
* aPresContext
,
1071 nsHTMLReflowMetrics
& aDesiredSize
,
1072 const nsHTMLReflowState
& aReflowState
,
1073 nsReflowStatus
& aStatus
);
1075 NS_IMETHOD
BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1076 const nsRect
& aDirtyRect
,
1077 const nsDisplayListSet
& aLists
);
1080 nsComboboxControlFrame
* mComboBox
;
1084 nsComboboxDisplayFrame::GetType() const
1086 return nsGkAtoms::comboboxDisplayFrame
;
1090 nsComboboxDisplayFrame::Reflow(nsPresContext
* aPresContext
,
1091 nsHTMLReflowMetrics
& aDesiredSize
,
1092 const nsHTMLReflowState
& aReflowState
,
1093 nsReflowStatus
& aStatus
)
1095 nsHTMLReflowState
state(aReflowState
);
1096 if (state
.ComputedHeight() == NS_INTRINSICSIZE
) {
1097 // Note that the only way we can have a computed height here is if the
1098 // combobox had a specified height. If it didn't, size based on what our
1099 // rows look like, for lack of anything better.
1100 state
.SetComputedHeight(mComboBox
->mListControlFrame
->GetHeightOfARow());
1102 nscoord computedWidth
= mComboBox
->mDisplayWidth
-
1103 state
.mComputedBorderPadding
.LeftRight();
1104 if (computedWidth
< 0) {
1107 state
.SetComputedWidth(computedWidth
);
1109 return nsBlockFrame::Reflow(aPresContext
, aDesiredSize
, state
, aStatus
);
1113 nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1114 const nsRect
& aDirtyRect
,
1115 const nsDisplayListSet
& aLists
)
1117 nsDisplayListCollection set
;
1118 nsresult rv
= nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, set
);
1122 // remove background items if parent frame is themed
1123 if (mComboBox
->IsThemed()) {
1124 set
.BorderBackground()->DeleteAll();
1133 nsComboboxControlFrame::CreateFrameFor(nsIContent
* aContent
)
1135 NS_PRECONDITION(nsnull
!= aContent
, "null ptr");
1137 NS_ASSERTION(mDisplayContent
, "mDisplayContent can't be null!");
1139 if (mDisplayContent
!= aContent
) {
1140 // We only handle the frames for mDisplayContent here
1145 nsIPresShell
*shell
= PresContext()->PresShell();
1146 nsStyleSet
*styleSet
= shell
->StyleSet();
1148 // create the style contexts for the anonymous block frame and text frame
1149 nsRefPtr
<nsStyleContext
> styleContext
;
1150 styleContext
= styleSet
->
1151 ResolvePseudoStyleFor(mContent
,
1152 nsCSSAnonBoxes::mozDisplayComboboxControlFrame
,
1154 if (NS_UNLIKELY(!styleContext
)) {
1158 nsRefPtr
<nsStyleContext
> textStyleContext
;
1159 textStyleContext
= styleSet
->ResolveStyleForNonElement(mStyleContext
);
1160 if (NS_UNLIKELY(!textStyleContext
)) {
1164 // Start by by creating our anonymous block frame
1165 mDisplayFrame
= new (shell
) nsComboboxDisplayFrame(styleContext
, this);
1166 if (NS_UNLIKELY(!mDisplayFrame
)) {
1170 nsresult rv
= mDisplayFrame
->Init(mContent
, this, nsnull
);
1171 if (NS_FAILED(rv
)) {
1172 mDisplayFrame
->Destroy();
1173 mDisplayFrame
= nsnull
;
1177 // Create a text frame and put it inside the block frame
1178 mTextFrame
= NS_NewTextFrame(shell
, textStyleContext
);
1179 if (NS_UNLIKELY(!mTextFrame
)) {
1183 // initialize the text frame
1184 rv
= mTextFrame
->Init(aContent
, mDisplayFrame
, nsnull
);
1185 if (NS_FAILED(rv
)) {
1186 mDisplayFrame
->Destroy();
1187 mDisplayFrame
= nsnull
;
1188 mTextFrame
->Destroy();
1189 mTextFrame
= nsnull
;
1193 mDisplayFrame
->SetInitialChildList(nsnull
, mTextFrame
);
1194 return mDisplayFrame
;
1198 nsComboboxControlFrame::Destroy()
1200 // Revoke any pending RedisplayTextEvent
1201 mRedisplayTextEvent
.Revoke();
1203 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame
*>(this), PR_FALSE
);
1207 nsIFrame
* listFrame
= do_QueryFrame(mListControlFrame
);
1209 nsIView
* view
= listFrame
->GetView();
1210 NS_ASSERTION(view
, "nsComboboxControlFrame view is null");
1212 nsIWidget
* widget
= view
->GetWidget();
1214 widget
->CaptureRollupEvents(this, PR_FALSE
, PR_TRUE
);
1219 // Cleanup frames in popup child list
1220 mPopupFrames
.DestroyFrames();
1221 nsContentUtils::DestroyAnonymousContent(&mDisplayContent
);
1222 nsContentUtils::DestroyAnonymousContent(&mButtonContent
);
1223 nsBlockFrame::Destroy();
1228 nsComboboxControlFrame::GetFirstChild(nsIAtom
* aListName
) const
1230 if (nsGkAtoms::selectPopupList
== aListName
) {
1231 return mPopupFrames
.FirstChild();
1233 return nsBlockFrame::GetFirstChild(aListName
);
1237 nsComboboxControlFrame::SetInitialChildList(nsIAtom
* aListName
,
1238 nsIFrame
* aChildList
)
1240 nsresult rv
= NS_OK
;
1241 if (nsGkAtoms::selectPopupList
== aListName
) {
1242 mPopupFrames
.SetFrames(aChildList
);
1244 rv
= nsBlockFrame::SetInitialChildList(aListName
, aChildList
);
1246 for (nsIFrame
* child
= aChildList
; child
;
1247 child
= child
->GetNextSibling()) {
1248 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryInterface(child
->GetContent());
1249 if (formControl
&& formControl
->GetType() == NS_FORM_INPUT_BUTTON
) {
1250 mButtonFrame
= child
;
1254 NS_ASSERTION(mButtonFrame
, "missing button frame in initial child list");
1259 #define NS_COMBO_FRAME_POPUP_LIST_INDEX (NS_BLOCK_LIST_COUNT)
1262 nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex
) const
1264 // Maintain a separate child list for the dropdown list (i.e. popup listbox)
1265 // This is necessary because we don't want the listbox to be included in the layout
1266 // of the combox's children because it would take up space, when it is suppose to
1267 // be floating above the display.
1268 if (aIndex
< NS_BLOCK_LIST_COUNT
) {
1269 return nsBlockFrame::GetAdditionalChildListName(aIndex
);
1272 if (NS_COMBO_FRAME_POPUP_LIST_INDEX
== aIndex
) {
1273 return nsGkAtoms::selectPopupList
;
1278 //----------------------------------------------------------------------
1280 //----------------------------------------------------------------------
1282 nsComboboxControlFrame::Rollup(nsIContent
** aLastRolledUp
)
1285 *aLastRolledUp
= nsnull
;
1288 nsWeakFrame
weakFrame(this);
1289 mListControlFrame
->AboutToRollup(); // might destroy us
1290 if (!weakFrame
.IsAlive())
1292 ShowDropDown(PR_FALSE
); // might destroy us
1293 if (!weakFrame
.IsAlive())
1295 mListControlFrame
->CaptureMouseEvents(PR_FALSE
);
1301 nsComboboxControlFrame::RollupFromList()
1303 if (ShowList(PresContext(), PR_FALSE
))
1304 mListControlFrame
->CaptureMouseEvents(PR_FALSE
);
1308 nsComboboxControlFrame::UpdateRecentIndex(PRInt32 aIndex
)
1310 PRInt32 index
= mRecentSelectedIndex
;
1311 if (mRecentSelectedIndex
== NS_SKIP_NOTIFY_INDEX
|| aIndex
== NS_SKIP_NOTIFY_INDEX
)
1312 mRecentSelectedIndex
= aIndex
;
1316 class nsDisplayComboboxFocus
: public nsDisplayItem
{
1318 nsDisplayComboboxFocus(nsComboboxControlFrame
* aFrame
)
1319 : nsDisplayItem(aFrame
) {
1320 MOZ_COUNT_CTOR(nsDisplayComboboxFocus
);
1322 #ifdef NS_BUILD_REFCNT_LOGGING
1323 virtual ~nsDisplayComboboxFocus() {
1324 MOZ_COUNT_DTOR(nsDisplayComboboxFocus
);
1328 virtual void Paint(nsDisplayListBuilder
* aBuilder
, nsIRenderingContext
* aCtx
,
1329 const nsRect
& aDirtyRect
);
1330 NS_DISPLAY_DECL_NAME("ComboboxFocus")
1333 void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder
* aBuilder
,
1334 nsIRenderingContext
* aCtx
, const nsRect
& aDirtyRect
)
1336 static_cast<nsComboboxControlFrame
*>(mFrame
)
1337 ->PaintFocus(*aCtx
, aBuilder
->ToReferenceFrame(mFrame
));
1341 nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1342 const nsRect
& aDirtyRect
,
1343 const nsDisplayListSet
& aLists
)
1346 printf("%p paint at (%d, %d, %d, %d)\n", this,
1347 aDirtyRect
.x
, aDirtyRect
.y
, aDirtyRect
.width
, aDirtyRect
.height
);
1350 if (aBuilder
->IsForEventDelivery()) {
1351 // Don't allow children to receive events.
1352 // REVIEW: following old GetFrameForPoint
1353 nsresult rv
= DisplayBorderBackgroundOutline(aBuilder
, aLists
);
1354 NS_ENSURE_SUCCESS(rv
, rv
);
1356 // REVIEW: Our in-flow child frames are inline-level so they will paint in our
1357 // content list, so we don't need to mess with layers.
1358 nsresult rv
= nsBlockFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
1359 NS_ENSURE_SUCCESS(rv
, rv
);
1362 nsPresContext
*presContext
= PresContext();
1363 const nsStyleDisplay
*disp
= GetStyleDisplay();
1364 if ((!IsThemed(disp
) ||
1365 !presContext
->GetTheme()->ThemeDrawsFocusForWidget(presContext
, this, disp
->mAppearance
)) &&
1366 mDisplayFrame
&& IsVisibleForPainting(aBuilder
)) {
1367 nsresult rv
= aLists
.Content()->AppendNewToTop(new (aBuilder
)
1368 nsDisplayComboboxFocus(this));
1369 NS_ENSURE_SUCCESS(rv
, rv
);
1372 return DisplaySelectionOverlay(aBuilder
, aLists
);
1375 void nsComboboxControlFrame::PaintFocus(nsIRenderingContext
& aRenderingContext
,
1378 /* Do we need to do anything? */
1379 if (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
) ||
1383 aRenderingContext
.PushState();
1384 nsRect clipRect
= mDisplayFrame
->GetRect() + aPt
;
1385 aRenderingContext
.SetClipRect(clipRect
, nsClipCombine_kIntersect
);
1387 // REVIEW: Why does the old code paint mDisplayFrame again? We've
1388 // already painted it in the children above. So clipping it here won't do
1391 /////////////////////
1394 aRenderingContext
.SetLineStyle(nsLineStyle_kDotted
);
1395 aRenderingContext
.SetColor(GetStyleColor()->mColor
);
1397 //aRenderingContext.DrawRect(clipRect);
1399 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
1400 clipRect
.width
-= onePixel
;
1401 clipRect
.height
-= onePixel
;
1402 aRenderingContext
.DrawLine(clipRect
.x
, clipRect
.y
,
1403 clipRect
.x
+clipRect
.width
, clipRect
.y
);
1404 aRenderingContext
.DrawLine(clipRect
.x
+clipRect
.width
, clipRect
.y
,
1405 clipRect
.x
+clipRect
.width
, clipRect
.y
+clipRect
.height
);
1406 aRenderingContext
.DrawLine(clipRect
.x
+clipRect
.width
, clipRect
.y
+clipRect
.height
,
1407 clipRect
.x
, clipRect
.y
+clipRect
.height
);
1408 aRenderingContext
.DrawLine(clipRect
.x
, clipRect
.y
+clipRect
.height
,
1409 clipRect
.x
, clipRect
.y
);
1410 aRenderingContext
.DrawLine(clipRect
.x
, clipRect
.y
+clipRect
.height
,
1411 clipRect
.x
, clipRect
.y
);
1413 aRenderingContext
.PopState();
1416 //----------------------------------------------------------------------
1417 //nsIScrollableViewProvider
1418 //----------------------------------------------------------------------
1419 nsIScrollableView
* nsComboboxControlFrame::GetScrollableView()
1421 if (!mDropdownFrame
)
1424 nsIScrollableFrame
* scrollable
= do_QueryFrame(mDropdownFrame
);
1428 return scrollable
->GetScrollableView();
1431 //---------------------------------------------------------
1432 // gets the content (an option) by index and then set it as
1433 // being selected or not selected
1434 //---------------------------------------------------------
1436 nsComboboxControlFrame::OnOptionSelected(nsPresContext
* aPresContext
,
1441 nsISelectControlFrame
*selectFrame
= do_QueryFrame(mListControlFrame
);
1443 selectFrame
->OnOptionSelected(aPresContext
, aIndex
, aSelected
);
1447 RedisplayText(aIndex
);
1449 RedisplaySelectedText();
1450 FireValueChangeEvent(); // Fire after old option is unselected
1457 void nsComboboxControlFrame::FireValueChangeEvent()
1459 // Fire ValueChange event to indicate data value of combo box has changed
1460 nsCOMPtr
<nsIDOMEvent
> event
;
1461 nsPresContext
* presContext
= PresContext();
1462 if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(presContext
, nsnull
,
1463 NS_LITERAL_STRING("Events"),
1464 getter_AddRefs(event
)))) {
1465 event
->InitEvent(NS_LITERAL_STRING("ValueChange"), PR_TRUE
, PR_TRUE
);
1467 nsCOMPtr
<nsIPrivateDOMEvent
> privateEvent(do_QueryInterface(event
));
1468 privateEvent
->SetTrusted(PR_TRUE
);
1469 nsEventDispatcher::DispatchDOMEvent(mContent
, nsnull
, event
, nsnull
,
1475 nsComboboxControlFrame::OnContentReset()
1477 if (mListControlFrame
) {
1478 mListControlFrame
->OnContentReset();
1483 //--------------------------------------------------------
1485 //--------------------------------------------------------
1487 nsComboboxControlFrame::SaveState(SpecialStateID aStateID
,
1488 nsPresState
** aState
)
1490 if (!mListControlFrame
)
1491 return NS_ERROR_FAILURE
;
1493 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1494 return stateful
->SaveState(aStateID
, aState
);
1498 nsComboboxControlFrame::RestoreState(nsPresState
* aState
)
1500 if (!mListControlFrame
)
1501 return NS_ERROR_FAILURE
;
1503 nsIStatefulFrame
* stateful
= do_QueryFrame(mListControlFrame
);
1504 NS_ASSERTION(stateful
, "Must implement nsIStatefulFrame");
1505 return stateful
->RestoreState(aState
);
1510 // Camino uses a native widget for the combobox
1511 // popup, which affects drawing and event
1512 // handling here and in nsListControlFrame.
1517 nsComboboxControlFrame::ToolkitHasNativePopup()
1520 return nsContentUtils::GetBoolPref("ui.use_native_popup_windows");