Back to index

lightning-sunbird  0.9+nobinonly
nsComboboxControlFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Mats Palmgren <mats.palmgren@bredband.net>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsCOMPtr.h"
00040 #include "nsReadableUtils.h"
00041 #include "nsComboboxControlFrame.h"
00042 #include "nsIDOMEventReceiver.h"
00043 #include "nsFrameManager.h"
00044 #include "nsFormControlFrame.h"
00045 #include "nsHTMLAtoms.h"
00046 #include "nsCSSAnonBoxes.h"
00047 #include "nsHTMLParts.h"
00048 #include "nsIFormControl.h"
00049 #include "nsINameSpaceManager.h"
00050 #include "nsLayoutAtoms.h"
00051 #include "nsIDOMElement.h"
00052 #include "nsIListControlFrame.h"
00053 #include "nsIDOMHTMLCollection.h" 
00054 #include "nsIDOMHTMLSelectElement.h" 
00055 #include "nsIDOMHTMLOptionElement.h" 
00056 #include "nsIDOMNSHTMLOptionCollectn.h" 
00057 #include "nsIPresShell.h"
00058 #include "nsIDeviceContext.h"
00059 #include "nsIView.h"
00060 #include "nsIScrollableView.h"
00061 #include "nsIEventStateManager.h"
00062 #include "nsIEventListenerManager.h"
00063 #include "nsIDOMNode.h"
00064 #include "nsIPrivateDOMEvent.h"
00065 #include "nsISupportsArray.h"
00066 #include "nsISelectControlFrame.h"
00067 #include "nsXPCOM.h"
00068 #include "nsISupportsPrimitives.h"
00069 #include "nsIComponentManager.h"
00070 #include "nsITextContent.h"
00071 #include "nsTextFragment.h"
00072 #include "nsCSSFrameConstructor.h"
00073 #include "nsIDocument.h"
00074 #include "nsINodeInfo.h"
00075 #include "nsIScrollableFrame.h"
00076 #include "nsListControlFrame.h"
00077 #include "nsContentCID.h"
00078 #ifdef ACCESSIBILITY
00079 #include "nsIAccessibilityService.h"
00080 #endif
00081 #include "nsIServiceManager.h"
00082 #include "nsIDOMNode.h"
00083 #include "nsGUIEvent.h"
00084 #include "nsAutoPtr.h"
00085 #include "nsStyleSet.h"
00086 #include "nsNodeInfoManager.h"
00087 #include "nsContentCreatorFunctions.h"
00088 
00089 #ifdef MOZ_XUL
00090 #include "nsIXULDocument.h" // Temporary fix for Bug 36558
00091 #endif
00092 
00093 #ifdef DO_NEW_REFLOW
00094 #include "nsIFontMetrics.h"
00095 #endif
00096 
00097 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00098 
00099 class RedisplayTextEvent : public PLEvent
00100 {
00101 public:
00102   RedisplayTextEvent(nsComboboxControlFrame* aComboboxControlFrame);
00103 
00104   void HandleEvent()
00105   {
00106     NS_STATIC_CAST(nsComboboxControlFrame*, owner) ->
00107       HandleRedisplayTextEvent();
00108   }
00109 };
00110 
00111 PR_STATIC_CALLBACK(void*)
00112 HandleRedisplayTextPLEvent(PLEvent* aEvent)
00113 {
00114   NS_ASSERTION(nsnull != aEvent, "Event is null");
00115   RedisplayTextEvent* event = NS_STATIC_CAST(RedisplayTextEvent*, aEvent);
00116 
00117   event->HandleEvent();
00118 
00119   return nsnull;
00120 }
00121 
00122 PR_STATIC_CALLBACK(void)
00123 DestroyRedisplayTextPLEvent(PLEvent* aEvent)
00124 {
00125   NS_ASSERTION(nsnull != aEvent, "Event is null");
00126   RedisplayTextEvent* event = NS_STATIC_CAST(RedisplayTextEvent*, aEvent);
00127 
00128   delete event;
00129 }
00130 
00131 RedisplayTextEvent::RedisplayTextEvent(nsComboboxControlFrame* aComboboxControlFrame)
00132 {
00133   PL_InitEvent(this, aComboboxControlFrame,
00134                ::HandleRedisplayTextPLEvent,
00135                ::DestroyRedisplayTextPLEvent);
00136 }
00137 
00138 class nsPresState;
00139 
00140 #define FIX_FOR_BUG_53259
00141 
00142 // Drop down list event management.
00143 // The combo box uses the following strategy for managing the drop-down list.
00144 // If the combo box or it's arrow button is clicked on the drop-down list is displayed
00145 // If mouse exit's the combo box with the drop-down list displayed the drop-down list
00146 // is asked to capture events
00147 // The drop-down list will capture all events including mouse down and up and will always
00148 // return with ListWasSelected method call regardless of whether an item in the list was
00149 // actually selected.
00150 // The ListWasSelected code will turn off mouse-capture for the drop-down list.
00151 // The drop-down list does not explicitly set capture when it is in the drop-down mode.
00152 
00153 
00154 //XXX: This is temporary. It simulates pseudo states by using a attribute selector on 
00155 
00156 const PRInt32 kSizeNotSet = -1;
00157 
00163 class nsComboButtonListener: public nsIDOMMouseListener
00164 {
00165   public:
00166 
00167   NS_DECL_ISUPPORTS
00168   NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return PR_FALSE; }
00169   NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent) { return PR_FALSE; }
00170   NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return PR_FALSE; }
00171   NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return PR_FALSE; }
00172   NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return PR_FALSE; }
00173   NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return PR_FALSE; }
00174 
00175   NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) 
00176   {
00177     PRBool isDroppedDown;
00178     mComboBox->IsDroppedDown(&isDroppedDown);
00179     mComboBox->ShowDropDown(!isDroppedDown);
00180     return NS_OK; 
00181   }
00182 
00183   nsComboButtonListener(nsComboboxControlFrame* aCombobox) 
00184   { 
00185     mComboBox = aCombobox; 
00186   }
00187 
00188   virtual ~nsComboButtonListener() {}
00189 
00190   nsComboboxControlFrame* mComboBox;
00191 };
00192 
00193 NS_IMPL_ISUPPORTS1(nsComboButtonListener, nsIDOMMouseListener)
00194 
00195 // static class data member for Bug 32920
00196 nsComboboxControlFrame * nsComboboxControlFrame::mFocused = nsnull;
00197 
00198 nsresult
00199 NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags)
00200 {
00201   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00202   if (nsnull == aNewFrame) {
00203     return NS_ERROR_NULL_POINTER;
00204   }
00205   nsComboboxControlFrame* it = new (aPresShell) nsComboboxControlFrame;
00206   if (!it) {
00207     return NS_ERROR_OUT_OF_MEMORY;
00208   }
00209   // set the state flags (if any are provided)
00210   it->AddStateBits(aStateFlags);
00211   *aNewFrame = it;
00212   return NS_OK;
00213 }
00214 
00215 //-----------------------------------------------------------
00216 // Reflow Debugging Macros
00217 // These let us "see" how many reflow counts are happening
00218 //-----------------------------------------------------------
00219 #ifdef DO_REFLOW_COUNTER
00220 
00221 #define MAX_REFLOW_CNT 1024
00222 static PRInt32 gTotalReqs    = 0;;
00223 static PRInt32 gTotalReflows = 0;;
00224 static PRInt32 gReflowControlCntRQ[MAX_REFLOW_CNT];
00225 static PRInt32 gReflowControlCnt[MAX_REFLOW_CNT];
00226 static PRInt32 gReflowInx = -1;
00227 
00228 #define REFLOW_COUNTER() \
00229   if (mReflowId > -1) \
00230     gReflowControlCnt[mReflowId]++;
00231 
00232 #define REFLOW_COUNTER_REQUEST() \
00233   if (mReflowId > -1) \
00234     gReflowControlCntRQ[mReflowId]++;
00235 
00236 #define REFLOW_COUNTER_DUMP(__desc) \
00237   if (mReflowId > -1) {\
00238     gTotalReqs    += gReflowControlCntRQ[mReflowId];\
00239     gTotalReflows += gReflowControlCnt[mReflowId];\
00240     printf("** Id:%5d %s RF: %d RQ: %d   %d/%d  %5.2f\n", \
00241            mReflowId, (__desc), \
00242            gReflowControlCnt[mReflowId], \
00243            gReflowControlCntRQ[mReflowId],\
00244            gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\
00245   }
00246 
00247 #define REFLOW_COUNTER_INIT() \
00248   if (gReflowInx < MAX_REFLOW_CNT) { \
00249     gReflowInx++; \
00250     mReflowId = gReflowInx; \
00251     gReflowControlCnt[mReflowId] = 0; \
00252     gReflowControlCntRQ[mReflowId] = 0; \
00253   } else { \
00254     mReflowId = -1; \
00255   }
00256 
00257 // reflow messages
00258 #define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
00259 #define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
00260 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
00261 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
00262 
00263 #else //-------------
00264 
00265 #define REFLOW_COUNTER_REQUEST() 
00266 #define REFLOW_COUNTER() 
00267 #define REFLOW_COUNTER_DUMP(__desc) 
00268 #define REFLOW_COUNTER_INIT() 
00269 
00270 #define REFLOW_DEBUG_MSG(_msg) 
00271 #define REFLOW_DEBUG_MSG2(_msg1, _msg2) 
00272 #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) 
00273 #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) 
00274 
00275 
00276 #endif
00277 
00278 //------------------------------------------
00279 // This is for being VERY noisy
00280 //------------------------------------------
00281 #ifdef DO_VERY_NOISY
00282 #define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
00283 #define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
00284 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
00285 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
00286 #else
00287 #define REFLOW_NOISY_MSG(_msg) 
00288 #define REFLOW_NOISY_MSG2(_msg1, _msg2) 
00289 #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) 
00290 #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) 
00291 #endif
00292 
00293 //------------------------------------------
00294 // Displays value in pixels or twips
00295 //------------------------------------------
00296 #ifdef DO_PIXELS
00297 #define PX(__v) __v / 15
00298 #else
00299 #define PX(__v) __v 
00300 #endif
00301 
00302 //------------------------------------------
00303 // Asserts if we return a desired size that 
00304 // doesn't correctly match the mComputedWidth
00305 //------------------------------------------
00306 #ifdef DO_UNCONSTRAINED_CHECK
00307 #define UNCONSTRAINED_CHECK() \
00308 if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \
00309   nscoord width = aDesiredSize.width - borderPadding.left - borderPadding.right; \
00310   if (width != aReflowState.mComputedWidth) { \
00311     printf("aDesiredSize.width %d %d != aReflowState.mComputedWidth %d\n", aDesiredSize.width, width, aReflowState.mComputedWidth); \
00312   } \
00313   NS_ASSERTION(width == aReflowState.mComputedWidth, "Returning bad value when constrained!"); \
00314 }
00315 #else
00316 #define UNCONSTRAINED_CHECK()
00317 #endif
00318 //------------------------------------------------------
00319 //-- Done with macros
00320 //------------------------------------------------------
00321 
00322 nsComboboxControlFrame::nsComboboxControlFrame()
00323   : nsAreaFrame() 
00324 {
00325   mPresContext                 = nsnull;
00326   mListControlFrame            = nsnull;
00327   mDroppedDown                 = PR_FALSE;
00328   mDisplayFrame                = nsnull;
00329   mButtonFrame                 = nsnull;
00330   mDropdownFrame               = nsnull;
00331 
00332   mCacheSize.width             = kSizeNotSet;
00333   mCacheSize.height            = kSizeNotSet;
00334   mCachedAscent                = kSizeNotSet;
00335   mCachedMaxElementWidth       = kSizeNotSet;
00336   mCachedAvailableSize.width   = kSizeNotSet;
00337   mCachedAvailableSize.height  = kSizeNotSet;
00338   mCachedUncDropdownSize.width  = kSizeNotSet;
00339   mCachedUncDropdownSize.height = kSizeNotSet;
00340   mCachedUncComboSize.width    = kSizeNotSet;
00341   mCachedUncComboSize.height   = kSizeNotSet;
00342   mItemDisplayWidth             = 0;
00343 
00344   mGoodToGo = PR_FALSE;
00345   mInRedisplayText = PR_FALSE;
00346   mRedisplayTextEventPosted = PR_FALSE;
00347 
00348   mRecentSelectedIndex = NS_SKIP_NOTIFY_INDEX;
00349 
00350   //Shrink the area around it's contents
00351   //SetFlags(NS_BLOCK_SHRINK_WRAP);
00352 
00353   REFLOW_COUNTER_INIT()
00354 }
00355 
00356 //--------------------------------------------------------------
00357 nsComboboxControlFrame::~nsComboboxControlFrame()
00358 {
00359   REFLOW_COUNTER_DUMP("nsCCF");
00360 
00361   NS_IF_RELEASE(mPresContext);
00362 }
00363 
00364 //--------------------------------------------------------------
00365 // Frames are not refcounted, no need to AddRef
00366 NS_IMETHODIMP
00367 nsComboboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00368 {
00369   NS_PRECONDITION(0 != aInstancePtr, "null ptr");
00370   if (NULL == aInstancePtr) {
00371     return NS_ERROR_NULL_POINTER;
00372   }
00373 
00374   if (aIID.Equals(NS_GET_IID(nsIComboboxControlFrame))) {
00375     *aInstancePtr = (void*)(nsIComboboxControlFrame*)this;
00376     return NS_OK;
00377   } else if (aIID.Equals(NS_GET_IID(nsIFormControlFrame))) {
00378     *aInstancePtr = (void*)(nsIFormControlFrame*)this;
00379     return NS_OK;
00380   } else if (aIID.Equals(NS_GET_IID(nsIAnonymousContentCreator))) {                                         
00381     *aInstancePtr = (void*)(nsIAnonymousContentCreator*)this;
00382     return NS_OK;   
00383   } else if (aIID.Equals(NS_GET_IID(nsISelectControlFrame))) {
00384     *aInstancePtr = (void *)(nsISelectControlFrame*)this;
00385     return NS_OK;
00386   } else if (aIID.Equals(NS_GET_IID(nsIStatefulFrame))) {
00387     *aInstancePtr = (void*)(nsIStatefulFrame*)this;
00388     return NS_OK;
00389   } else if (aIID.Equals(NS_GET_IID(nsIRollupListener))) {
00390     *aInstancePtr = (void*)(nsIRollupListener*)this;
00391     return NS_OK;
00392   } else if (aIID.Equals(NS_GET_IID(nsIScrollableViewProvider))) {
00393     *aInstancePtr = (void*)(nsIScrollableViewProvider*)this;
00394     return NS_OK;
00395   } 
00396   
00397   return nsAreaFrame::QueryInterface(aIID, aInstancePtr);
00398 }
00399 
00400 #ifdef ACCESSIBILITY
00401 NS_IMETHODIMP nsComboboxControlFrame::GetAccessible(nsIAccessible** aAccessible)
00402 {
00403   nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
00404 
00405   if (accService) {
00406     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mContent);
00407     return accService->CreateHTMLComboboxAccessible(node, mPresContext->PresShell(), aAccessible);
00408   }
00409 
00410   return NS_ERROR_FAILURE;
00411 }
00412 #endif
00413 
00414 
00415 
00416 NS_IMETHODIMP
00417 nsComboboxControlFrame::Init(nsPresContext*  aPresContext,
00418               nsIContent*      aContent,
00419               nsIFrame*        aParent,
00420               nsStyleContext*  aContext,
00421               nsIFrame*        aPrevInFlow)
00422 {
00423    // Need to hold on the pres context because it is used later in methods
00424    // which don't have it passed in.
00425   mPresContext = aPresContext;
00426   NS_ADDREF(mPresContext);
00427 
00428   mEventQueueService = do_GetService(kEventQueueServiceCID);
00429 
00430   //-------------------------------
00431   // Start - Temporary fix for Bug 36558
00432   //-------------------------------
00433   mGoodToGo = PR_FALSE;
00434   nsIDocument* document = aContent->GetDocument();
00435   if (document) {
00436 #ifdef MOZ_XUL
00437     nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(document));
00438     mGoodToGo = xulDoc?PR_FALSE:PR_TRUE;
00439 #else
00440     mGoodToGo = PR_TRUE;
00441 #endif
00442   }
00443   //-------------------------------
00444   // Done - Temporary fix for Bug 36558
00445   //-------------------------------
00446   
00447   return nsAreaFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00448 }
00449 
00450 //--------------------------------------------------------------
00451 void 
00452 nsComboboxControlFrame::InitializeControl(nsPresContext* aPresContext)
00453 {
00454   nsFormControlHelper::Reset(this, aPresContext);
00455 }
00456 
00457 //--------------------------------------------------------------
00458 NS_IMETHODIMP_(PRInt32)
00459 nsComboboxControlFrame::GetFormControlType() const
00460 {
00461   return NS_FORM_SELECT;
00462 }
00463 
00464 //--------------------------------------------------------------
00465 NS_IMETHODIMP
00466 nsComboboxControlFrame::GetFormContent(nsIContent*& aContent) const
00467 {
00468   aContent = GetContent();
00469   NS_IF_ADDREF(aContent);
00470   return NS_OK;
00471 }
00472 
00473 //--------------------------------------------------------------
00474 nscoord 
00475 nsComboboxControlFrame::GetVerticalBorderWidth(float aPixToTwip) const
00476 {
00477    return 0;
00478 }
00479 
00480 
00481 //--------------------------------------------------------------
00482 nscoord 
00483 nsComboboxControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const
00484 {
00485   return 0;
00486 }
00487 
00488 
00489 //--------------------------------------------------------------
00490 nscoord 
00491 nsComboboxControlFrame::GetVerticalInsidePadding(nsPresContext* aPresContext,
00492                                                  float aPixToTwip, 
00493                                                  nscoord aInnerHeight) const
00494 {
00495    return 0;
00496 }
00497 
00498 //--------------------------------------------------------------
00499 nscoord 
00500 nsComboboxControlFrame::GetHorizontalInsidePadding(nsPresContext* aPresContext,
00501                                                float aPixToTwip, 
00502                                                nscoord aInnerWidth,
00503                                                nscoord aCharWidth) const
00504 {
00505   return 0;
00506 }
00507 
00508 void 
00509 nsComboboxControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
00510 {
00511   nsWeakFrame weakFrame(this);
00512   if (aOn) {
00513     nsListControlFrame::ComboboxFocusSet();
00514     mFocused = this;
00515   } else {
00516     mFocused = nsnull;
00517     if (mDroppedDown) {
00518       mListControlFrame->ComboboxFinish(mDisplayedIndex); // might destroy us
00519       if (!weakFrame.IsAlive()) {
00520         return;
00521       }
00522     }
00523     // May delete |this|.
00524     mListControlFrame->FireOnChange();
00525   }
00526 
00527   if (!weakFrame.IsAlive()) {
00528     return;
00529   }
00530 
00531   // This is needed on a temporary basis. It causes the focus
00532   // rect to be drawn. This is much faster than ReResolvingStyle
00533   // Bug 32920
00534   Invalidate(nsRect(0,0,mRect.width,mRect.height), PR_TRUE);
00535 
00536   // Make sure the content area gets updated for where the dropdown was
00537   // This is only needed for embedding, the focus may go to 
00538   // the chrome that is not part of the Gecko system (Bug 83493)
00539   // XXX this is rather inefficient
00540   nsIViewManager* vm = GetPresContext()->GetViewManager();
00541   if (vm) {
00542     vm->UpdateAllViews(NS_VMREFRESH_NO_SYNC);
00543   }
00544 }
00545 
00546 void
00547 nsComboboxControlFrame::ScrollIntoView(nsPresContext* aPresContext)
00548 {
00549   if (aPresContext) {
00550     nsIPresShell *presShell = aPresContext->GetPresShell();
00551     if (presShell) {
00552       presShell->ScrollFrameIntoView(this,
00553                    NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
00554     }
00555   }
00556 }
00557 
00558 
00559 void
00560 nsComboboxControlFrame::ShowPopup(PRBool aShowPopup)
00561 {
00562   nsIView* view = mDropdownFrame->GetView();
00563   nsIViewManager* viewManager = view->GetViewManager();
00564 
00565   if (aShowPopup) {
00566     nsRect rect = mDropdownFrame->GetRect();
00567     rect.x = rect.y = 0;
00568     viewManager->ResizeView(view, rect);
00569     nsIScrollableView* scrollingView = view->ToScrollableView();
00570     viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
00571   } else {
00572     viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
00573     nsRect emptyRect(0, 0, 0, 0);
00574     viewManager->ResizeView(view, emptyRect);
00575   }
00576 
00577   // fire a popup dom event
00578   nsEventStatus status = nsEventStatus_eIgnore;
00579   nsMouseEvent event(PR_TRUE, aShowPopup ?
00580                      NS_XUL_POPUP_SHOWING : NS_XUL_POPUP_HIDING, nsnull,
00581                      nsMouseEvent::eReal);
00582 
00583   nsCOMPtr<nsIPresShell> shell = mPresContext->GetPresShell();
00584   if (shell) 
00585     shell->HandleDOMEventWithTarget(mContent, &event, &status);
00586 }
00587 
00588 PRBool
00589 nsComboboxControlFrame::ShowList(nsPresContext* aPresContext, PRBool aShowList)
00590 {
00591   nsCOMPtr<nsIPresShell> shell = aPresContext->GetPresShell();
00592 
00593   nsWeakFrame weakFrame(this);
00594   ShowPopup(aShowList);  // might destroy us
00595   if (!weakFrame.IsAlive()) {
00596     return PR_FALSE;
00597   }
00598   
00599   mDroppedDown = aShowList;
00600   if (mDroppedDown) {
00601     // The listcontrol frame will call back to the nsComboboxControlFrame's
00602     // ListWasSelected which will stop the capture.
00603     mListControlFrame->AboutToDropDown();
00604     mListControlFrame->CaptureMouseEvents(aPresContext, PR_TRUE);
00605   }
00606 
00607   // Don't flush anything but reflows lest it destroy us
00608   shell->GetDocument()->FlushPendingNotifications(Flush_OnlyReflow);
00609   if (!weakFrame.IsAlive()) {
00610     NS_ERROR("Flush_OnlyReflow destroyed the frame");
00611     return PR_FALSE;
00612   }
00613 
00614   nsIFrame* listFrame = nsnull;
00615   CallQueryInterface(mListControlFrame, &listFrame);
00616   if (listFrame) {
00617     nsIView* view = listFrame->GetView();
00618     NS_ASSERTION(view, "nsComboboxControlFrame view is null");
00619     if (view) {
00620       nsIWidget* widget = view->GetWidget();
00621       if (widget)
00622         widget->CaptureRollupEvents(this, mDroppedDown, mDroppedDown);
00623     }
00624   }
00625 
00626   return weakFrame.IsAlive();
00627 }
00628 
00629 nsresult
00630 nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame, 
00631                                              nsPresContext*  aPresContext, 
00632                                              nsHTMLReflowMetrics&     aDesiredSize,
00633                                              const nsHTMLReflowState& aReflowState, 
00634                                              nsReflowStatus&          aStatus,
00635                                              nscoord                  aAvailableWidth,
00636                                              nscoord                  aAvailableHeight)
00637 {
00638    // Constrain the child's width and height to aAvailableWidth and aAvailableHeight
00639   nsSize availSize(aAvailableWidth, aAvailableHeight);
00640   nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aFrame,
00641                                    availSize);
00642   kidReflowState.mComputedWidth = aAvailableWidth;
00643   kidReflowState.mComputedHeight = aAvailableHeight;
00644 
00645   // ensure we start off hidden
00646   if (aReflowState.reason == eReflowReason_Initial) {
00647     nsIView* view = mDropdownFrame->GetView();
00648     nsIViewManager* viewManager = view->GetViewManager();
00649     viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
00650     nsRect emptyRect(0, 0, 0, 0);
00651     viewManager->ResizeView(view, emptyRect);
00652   }
00653   
00654   // Allow the child to move/size/change-visibility its view if it's currently
00655   // dropped down
00656   PRInt32 flags = NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW;
00657   if (mDroppedDown) {
00658     flags = 0;
00659   }
00660   nsRect rect = aFrame->GetRect();
00661   nsresult rv = ReflowChild(aFrame, aPresContext, aDesiredSize, kidReflowState,
00662                             rect.x, rect.y, flags, aStatus);
00663  
00664    // Set the child's width and height to it's desired size
00665   FinishReflowChild(aFrame, aPresContext, &kidReflowState, aDesiredSize, 
00666                     rect.x, rect.y, flags);
00667   return rv;
00668 }
00669 
00670 // Suggest a size for the child frame. 
00671 // Only frames which implement the nsIFormControlFrame interface and
00672 // honor the SetSuggestedSize method will be placed and sized correctly.
00673 
00674 void 
00675 nsComboboxControlFrame::SetChildFrameSize(nsIFrame* aFrame, nscoord aWidth, nscoord aHeight) 
00676 {
00677   nsIFormControlFrame* fcFrame = nsnull;
00678   nsresult result = aFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
00679   if (NS_SUCCEEDED(result) && (nsnull != fcFrame)) {
00680     fcFrame->SetSuggestedSize(aWidth, aHeight); 
00681   }
00682 }
00683 
00684 nsresult 
00685 nsComboboxControlFrame::GetPrimaryComboFrame(nsPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame)
00686 {
00687    // Get the primary frame from the presentation shell.
00688   nsIPresShell *presShell = aPresContext->GetPresShell();
00689   if (presShell) {
00690     presShell->GetPrimaryFrameFor(aContent, aFrame);
00691   }
00692   return NS_OK;
00693 }
00694 
00695 nsresult 
00696 nsComboboxControlFrame::PositionDropdown(nsPresContext* aPresContext, 
00697                                          nscoord aHeight, 
00698                                          nsRect aAbsoluteTwipsRect, 
00699                                          nsRect aAbsolutePixelRect)
00700 {
00701    // Position the dropdown list. It is positioned below the display frame if there is enough
00702    // room on the screen to display the entire list. Otherwise it is placed above the display
00703    // frame.
00704 
00705    // Note: As first glance, it appears that you could simply get the absolute bounding box for the
00706    // dropdown list by first getting it's view, then getting the view's nsIWidget, then asking the nsIWidget
00707    // for it's AbsoluteBounds. The problem with this approach, is that the dropdown lists y location can
00708    // change based on whether the dropdown is placed below or above the display frame.
00709    // The approach, taken here is to get use the absolute position of the display frame and use it's location
00710    // to determine if the dropdown will go offscreen.
00711 
00712    // Use the height calculated for the area frame so it includes both
00713    // the display and button heights.
00714   nsresult rv = NS_OK;
00715   nscoord dropdownYOffset = aHeight;
00716 // XXX: Enable this code to debug popping up above the display frame, rather than below it
00717   nsRect dropdownRect = mDropdownFrame->GetRect();
00718 
00719   nscoord screenHeightInPixels = 0;
00720   if (NS_SUCCEEDED(nsFormControlFrame::GetScreenHeight(aPresContext, screenHeightInPixels))) {
00721      // Get the height of the dropdown list in pixels.
00722      float t2p;
00723      t2p = aPresContext->TwipsToPixels();
00724      nscoord absoluteDropDownHeight = NSTwipsToIntPixels(dropdownRect.height, t2p);
00725     
00726       // Check to see if the drop-down list will go offscreen
00727     if (NS_SUCCEEDED(rv) && ((aAbsolutePixelRect.y + aAbsolutePixelRect.height + absoluteDropDownHeight) > screenHeightInPixels)) {
00728       // move the dropdown list up
00729       dropdownYOffset = - (dropdownRect.height);
00730     }
00731   }
00732  
00733   dropdownRect.x = 0;
00734   dropdownRect.y = dropdownYOffset; 
00735 
00736   mDropdownFrame->SetRect(dropdownRect);
00737   return rv;
00738 }
00739 
00740 
00742 // Experimental Reflow
00744 #if defined(DO_NEW_REFLOW) || defined(DO_REFLOW_COUNTER)
00745 //---------------------------------------------------------
00746 // Returns the nsIDOMHTMLOptionElement for a given index 
00747 // in the select's collection
00748 //---------------------------------------------------------
00749 static nsIDOMHTMLOptionElement* 
00750 GetOption(nsIDOMHTMLOptionsCollection& aCollection, PRInt32 aIndex)
00751 {
00752   nsIDOMNode* node = nsnull;
00753   if (NS_SUCCEEDED(aCollection.Item(aIndex, &node))) {
00754     if (nsnull != node) {
00755       nsIDOMHTMLOptionElement* option = nsnull;
00756       node->QueryInterface(NS_GET_IID(nsIDOMHTMLOptionElement), (void**)&option);
00757       NS_RELEASE(node);
00758       return option;
00759     }
00760   }
00761   return nsnull;
00762 }
00763 //---------------------------------------------------------
00764 // for a given piece of content it returns nsIDOMHTMLSelectElement object
00765 // or null 
00766 //---------------------------------------------------------
00767 static nsIDOMHTMLSelectElement* 
00768 GetSelect(nsIContent * aContent)
00769 {
00770   nsIDOMHTMLSelectElement* selectElement = nsnull;
00771   nsresult result = aContent->QueryInterface(NS_GET_IID(nsIDOMHTMLSelectElement),
00772                                              (void**)&selectElement);
00773   if (NS_SUCCEEDED(result) && selectElement) {
00774     return selectElement;
00775   } else {
00776     return nsnull;
00777   }
00778 }
00779 //---------------------------------------------------------
00780 //---------------------------------------------------------
00781 // This returns the collection for nsIDOMHTMLSelectElement or
00782 // the nsIContent object is the select is null  (AddRefs)
00783 //---------------------------------------------------------
00784 static nsIDOMHTMLOptionsCollection* 
00785 GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull)
00786 {
00787   nsIDOMHTMLOptionsCollection* options = nsnull;
00788   if (!aSelect) {
00789     nsCOMPtr<nsIDOMHTMLSelectElement> selectElement = getter_AddRefs(GetSelect(aContent));
00790     if (selectElement) {
00791       selectElement->GetOptions(&options);  // AddRefs (1)
00792     }
00793   } else {
00794     aSelect->GetOptions(&options); // AddRefs (1)
00795   }
00796   return options;
00797 }
00798 
00799 #ifdef DO_NEW_REFLOW
00800 NS_IMETHODIMP 
00801 nsComboboxControlFrame::ReflowItems(nsPresContext* aPresContext,
00802                                     const nsHTMLReflowState& aReflowState,
00803                                     nsHTMLReflowMetrics& aDesiredSize) 
00804 {
00805   //printf("*****************\n");
00806   nscoord visibleHeight = 0;
00807   nsCOMPtr<nsIFontMetrics> fontMet;
00808   nsresult res = nsFormControlHelper::GetFrameFontFM(mDisplayFrame, getter_AddRefs(fontMet));
00809   if (fontMet) {
00810     fontMet->GetHeight(visibleHeight);
00811   }
00812  
00813   nsAutoString maxStr;
00814   nscoord maxWidth = 0;
00815   //nsIRenderingContext * rc = aReflowState.rendContext;
00816   nsresult rv = NS_ERROR_FAILURE; 
00817   nsCOMPtr<nsIDOMHTMLOptionsCollection> options = getter_AddRefs(GetOptions(mContent));
00818   if (options) {
00819     PRUint32 numOptions;
00820     options->GetLength(&numOptions);
00821     //printf("--- Num of Items %d ---\n", numOptions);
00822     for (PRUint32 i=0;i<numOptions;i++) {
00823       nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = getter_AddRefs(GetOption(*options, i));
00824       if (optionElement) {
00825         nsAutoString text;
00826         rv = optionElement->GetLabel(text);
00827         if (NS_CONTENT_ATTR_HAS_VALUE != rv || text.IsEmpty()) {
00828           if (NS_OK == optionElement->GetText(text)) {
00829             nscoord width;
00830             aReflowState.rendContext->GetWidth(text, width);
00831             if (width > maxWidth) {
00832               maxStr = text;
00833               maxWidth = width;
00834             }
00835             //maxWidth = PR_MAX(width, maxWidth);
00836             //printf("[%d] - %d %s \n", i, width, NS_LossyConvertUCS2toASCII(text).get());
00837           }
00838         }          
00839       }
00840     }
00841   }
00842   if (maxWidth == 0) {
00843     maxWidth = 11 * 15;
00844   }
00845   char * str = ToNewCString(maxStr);
00846   printf("id: %d maxWidth %d [%s]\n", mReflowId, maxWidth, str);
00847   delete [] str;
00848 
00849   // get the borderPadding for the display area
00850   nsMargin dspBorderPadding(0, 0, 0, 0);
00851   mDisplayFrame->CalcBorderPadding(dspBorderPadding);
00852 
00853   nscoord frmWidth  = maxWidth+dspBorderPadding.left+dspBorderPadding.right+
00854                       aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right;
00855   nscoord frmHeight = visibleHeight+dspBorderPadding.top+dspBorderPadding.bottom+
00856                       aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom;
00857 
00858 #if 0
00859   aDesiredSize.width  = frmWidth;
00860   aDesiredSize.height = frmHeight;
00861 #else
00862   printf("Size frm:%d,%d   DS:%d,%d   DIF:%d,%d(tp)  %d,%d(px)\n", 
00863          frmWidth, frmHeight, 
00864          aDesiredSize.width, aDesiredSize.height,
00865          frmWidth-aDesiredSize.width, frmHeight-aDesiredSize.height,
00866          (frmWidth-aDesiredSize.width)/15, (frmHeight-aDesiredSize.height)/15);
00867 #endif
00868   return NS_OK;
00869 }
00870 #endif
00871 
00872 #endif
00873 
00874 //------------------------------------------------------------------
00875 // This Method reflow just the contents of the ComboBox
00876 // The contents are a Block frame containing a Text Frame - This is the display area
00877 // and then the GfxButton - The dropdown button
00878 //--------------------------------------------------------------------------
00879 void 
00880 nsComboboxControlFrame::ReflowCombobox(nsPresContext *         aPresContext,
00881                                            const nsHTMLReflowState& aReflowState,
00882                                            nsHTMLReflowMetrics&     aDesiredSize,
00883                                            nsReflowStatus&          aStatus,
00884                                            nsIFrame *               aDisplayFrame,
00885                                            nsIFrame *               aDropDownBtn,
00886                                            nscoord&                 aDisplayWidth,
00887                                            nscoord                  aBtnWidth,
00888                                            const nsMargin&          aBorderPadding,
00889                                            nscoord                  aFallBackHgt,
00890                                            PRBool                   aCheckHeight)
00891 {
00892   // start out by using the cached height
00893   // XXX later this will change when we better handle constrained height 
00894   nscoord dispHeight = mCacheSize.height - aBorderPadding.top - aBorderPadding.bottom;
00895   nscoord dispWidth  = aDisplayWidth;
00896 
00897   REFLOW_NOISY_MSG3("+++1 AdjustCombo DW:%d DH:%d  ", PX(dispWidth), PX(dispHeight));
00898   REFLOW_NOISY_MSG3("BW:%d  BH:%d  ", PX(aBtnWidth), PX(dispHeight));
00899   REFLOW_NOISY_MSG3("mCacheSize.height:%d - %d\n", PX(mCacheSize.height), PX((aBorderPadding.top + aBorderPadding.bottom)));
00900 
00901   // get the border and padding for the DisplayArea (block frame & textframe)
00902   nsMargin dspBorderPadding(0, 0, 0, 0);
00903   mDisplayFrame->CalcBorderPadding(dspBorderPadding);
00904 
00905   // adjust the height
00906   if (mCacheSize.height == kSizeNotSet) {
00907     if (aFallBackHgt == kSizeNotSet) {
00908       NS_ASSERTION(aFallBackHgt != kSizeNotSet, "Fallback can't be kSizeNotSet when mCacheSize.height == kSizeNotSet");
00909     } else {
00910       dispHeight = aFallBackHgt;
00911       REFLOW_NOISY_MSG2("+++3 Adding (dspBorderPadding.top + dspBorderPadding.bottom): %d\n", (dspBorderPadding.top + dspBorderPadding.bottom));
00912       dispHeight += (dspBorderPadding.top + dspBorderPadding.bottom);
00913     }
00914   }
00915 
00916   // Fix for Bug 58220 (part of it)
00917   // make sure we size correctly if the CSS width is set to something really small like 0, 1, or 2 pixels
00918   nscoord computedWidth = aReflowState.mComputedWidth + aBorderPadding.left + aBorderPadding.right;
00919   if ((aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && computedWidth <= 0) || aReflowState.mComputedWidth == 0) {
00920     nsRect buttonRect(0,0,0,0);
00921     nsRect displayRect(0,0,0,0);
00922     aBtnWidth = 0;
00923     aDisplayFrame->SetRect(displayRect);
00924     aDropDownBtn->SetRect(buttonRect);
00925     SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height);
00926     aDesiredSize.width = 0;
00927     aDesiredSize.height = dispHeight + aBorderPadding.top + aBorderPadding.bottom;
00928     // XXX What about ascent and descent?
00929     return;
00930   }
00931 
00932   REFLOW_NOISY_MSG3("+++2 AdjustCombo DW:%d DH:%d  ", PX(dispWidth), PX(dispHeight));
00933   REFLOW_NOISY_MSG3(" BW:%d  BH:%d\n", PX(aBtnWidth), PX(dispHeight));
00934 
00935   // This sets the button to be a specific size
00936   // so no matter what it reflows at these values
00937   SetChildFrameSize(aDropDownBtn, aBtnWidth, dispHeight);
00938 
00939 #ifdef FIX_FOR_BUG_53259
00940   // Make sure we obey min/max-width and min/max-height
00941   if (dispWidth > aReflowState.mComputedMaxWidth) {
00942     dispWidth = aReflowState.mComputedMaxWidth - aBorderPadding.left - aBorderPadding.right;
00943   }
00944   if (dispWidth < aReflowState.mComputedMinWidth) {
00945     dispWidth = aReflowState.mComputedMinWidth - aBorderPadding.left - aBorderPadding.right;
00946   }
00947 
00948   if (dispHeight > aReflowState.mComputedMaxHeight) {
00949     dispHeight = aReflowState.mComputedMaxHeight - aBorderPadding.top - aBorderPadding.bottom;
00950   }
00951   if (dispHeight < aReflowState.mComputedMinHeight) {
00952     dispHeight = aReflowState.mComputedMinHeight - aBorderPadding.top - aBorderPadding.bottom;
00953   }
00954 #endif
00955 
00956   // Make sure we get the reflow reason right. If an incremental
00957   // reflow arrives that's targeted directly at the top-level combobox
00958   // frame, then we can't pass it down to the children ``as is'':
00959   // we're the last frame in the reflow command's chain. So, convert
00960   // it to a resize reflow.
00961   nsReflowReason reason = aReflowState.reason;
00962   if (reason == eReflowReason_Incremental) {
00963     if (aReflowState.path->mReflowCommand)
00964       reason = eReflowReason_Resize;
00965   }
00966 
00967   // now that we know what the overall display width & height will be
00968   // set up a new reflow state and reflow the area frame at that size
00969   nsSize availSize(dispWidth + aBorderPadding.left + aBorderPadding.right, 
00970                    dispHeight + aBorderPadding.top + aBorderPadding.bottom);
00971   nsHTMLReflowState kidReflowState(aReflowState);
00972   kidReflowState.availableWidth  = availSize.width;
00973   kidReflowState.availableHeight = availSize.height;
00974   kidReflowState.mComputedWidth  = dispWidth;
00975   kidReflowState.mComputedHeight = dispHeight;
00976   kidReflowState.reason          = reason;
00977 
00978 #ifdef IBMBIDI
00979   const nsStyleVisibility* vis = GetStyleVisibility();
00980 
00981   // M14 didn't calculate the RightEdge in the reflow
00982   // Unless we set the width to some thing other than unrestricted
00983   // the code changed this may not be the best place to put it
00984   // in this->Reflow like this :
00985   //
00986   // Reflow display + button
00987   // nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus);
00988 
00989   if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
00990   {
00991     kidReflowState.mComputedWidth = 0;
00992   }
00993 #endif // IBMBIDI
00994 
00995   // do reflow
00996   nsAreaFrame::Reflow(aPresContext, aDesiredSize, kidReflowState, aStatus);
00997 
00999   // The DisplayFrame is a Block frame containing a TextFrame
01000   // and it is completely anonymous, so we must manually reflow it
01001   nsHTMLReflowMetrics txtKidSize(PR_TRUE);
01002   nsSize txtAvailSize(dispWidth - aBtnWidth, dispHeight);
01003   nsHTMLReflowState   txtKidReflowState(aPresContext, aReflowState, aDisplayFrame, txtAvailSize, reason);
01004 
01005   aDisplayFrame->WillReflow(aPresContext);
01006   //aDisplayFrame->SetPosition(nsPoint(dspBorderPadding.left + aBorderPadding.left, dspBorderPadding.top + aBorderPadding.top));
01007   aDisplayFrame->SetPosition(nsPoint(aBorderPadding.left, aBorderPadding.top));
01008   nsAreaFrame::PositionFrameView(aDisplayFrame);
01009   nsReflowStatus status;
01010   nsresult rv = aDisplayFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status);
01011   if (NS_FAILED(rv)) return;
01012 
01014   // If we are Constrained then the AreaFrame Reflow is the correct size
01015   // if we are unconstrained then 
01016   //if (aReflowState.mComputedWidth == NS_UNCONSTRAINEDSIZE) {
01017   //  aDesiredSize.width += txtKidSize.width;
01018   //}
01019 
01020   // Apparently, XUL lays out differently than HTML 
01021   // (the code above works for HTML and not XUL), 
01022   // so instead of using the above calculation
01023   // I just set it to what it should be.
01024   aDesiredSize.width = availSize.width;
01025   //aDesiredSize.height = availSize.height;
01026 
01027   // now we need to adjust layout, because the AreaFrame
01028   // doesn't position things exactly where we want them
01029   nscoord insideHeight = aDesiredSize.height - aBorderPadding.top - aBorderPadding.bottom;
01030 
01031   // If the css width has been set to something very small
01032   //i.e. smaller than the dropdown button, set the button's width to zero
01033   if (aBtnWidth > dispWidth) {
01034     aBtnWidth = 0;
01035   }
01036   // set the display rect to be left justifed and 
01037   // fills the entire area except the button
01038   nscoord x = aBorderPadding.left;
01039   nsRect displayRect(x, aBorderPadding.top, PR_MAX(dispWidth - aBtnWidth, 0), insideHeight);
01040   aDisplayFrame->SetRect(displayRect);
01041   x += displayRect.width;
01042 
01043   // right justify the button
01044   nsRect buttonRect(x, aBorderPadding.top, aBtnWidth, insideHeight);
01045 #ifdef IBMBIDI
01046   if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
01047   {
01048     if (buttonRect.x > displayRect.x)
01049     {
01050       buttonRect.x = displayRect.x;
01051       displayRect.x += buttonRect.width;
01052       aDisplayFrame->SetRect(displayRect);
01053     }
01054   }
01055 #endif // IBMBIDI
01056   aDropDownBtn->SetRect(buttonRect);
01057 
01058   // since we have changed the height of the button 
01059   // make sure it has these new values
01060   SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height);
01061   
01062   // This is a last minute adjustment, if the CSS width was set and 
01063   // we calculated it to be a little big, then make sure we are no bigger the computed size
01064   // this only comes into play when the css width has been set to something smaller than
01065   // the dropdown arrow
01066   if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && aDesiredSize.width > computedWidth) {
01067     aDesiredSize.width = computedWidth;
01068   }
01069 
01070   REFLOW_NOISY_MSG3("**AdjustCombobox - Reflow: WW: %d  HH: %d\n", aDesiredSize.width, aDesiredSize.height);
01071 
01072   if (aDesiredSize.mComputeMEW) {
01073     aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit());
01074   }
01075 
01076   aDesiredSize.ascent =
01077     txtKidSize.ascent + aReflowState.mComputedBorderPadding.top;
01078   aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent;
01079   
01080   // Now cache the available height as our height without border and padding
01081   // This sets up the optimization for if a new available width comes in and we are equal or
01082   // less than it we can bail
01083   if (aDesiredSize.width != mCacheSize.width || aDesiredSize.height != mCacheSize.height) {
01084     if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) {
01085       mCachedAvailableSize.width  = aDesiredSize.width - (aBorderPadding.left + aBorderPadding.right);
01086     }
01087     if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
01088       mCachedAvailableSize.height = aDesiredSize.height - (aBorderPadding.top + aBorderPadding.bottom);
01089     }
01090     nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent,
01091                                          mCachedMaxElementWidth, aDesiredSize);
01092   }
01093 
01095   // This is an experimental reflow that is turned off in the build
01096 #ifdef DO_NEW_REFLOW
01097   ReflowItems(aPresContext, aReflowState, aDesiredSize);
01098 #endif
01099 
01100 }
01101 
01102 //----------------------------------------------------------
01103 // 
01104 //----------------------------------------------------------
01105 #ifdef DO_REFLOW_DEBUG
01106 static int myCounter = 0;
01107 
01108 static void printSize(char * aDesc, nscoord aSize) 
01109 {
01110   printf(" %s: ", aDesc);
01111   if (aSize == NS_UNCONSTRAINEDSIZE) {
01112     printf("UC");
01113   } else {
01114     printf("%d", PX(aSize));
01115   }
01116 }
01117 #endif
01118 
01119 //-------------------------------------------------------------------
01120 //-- Main Reflow for the Combobox
01121 //-------------------------------------------------------------------
01122 NS_IMETHODIMP 
01123 nsComboboxControlFrame::Reflow(nsPresContext*          aPresContext, 
01124                                nsHTMLReflowMetrics&     aDesiredSize,
01125                                const nsHTMLReflowState& aReflowState, 
01126                                nsReflowStatus&          aStatus)
01127 {
01128   DO_GLOBAL_REFLOW_COUNT("nsComboboxControlFrame", aReflowState.reason);
01129   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
01130 
01131   aStatus = NS_FRAME_COMPLETE;
01132 
01133   REFLOW_COUNTER_REQUEST();
01134 
01135 #ifdef DO_REFLOW_DEBUG
01136   printf("-------------Starting Combobox Reflow ----------------------------\n");
01137   printf("%p ** Id: %d nsCCF::Reflow %d R: ", this, mReflowId, myCounter++);
01138   switch (aReflowState.reason) {
01139     case eReflowReason_Initial:
01140       printf("Ini");break;
01141     case eReflowReason_Incremental:
01142       printf("Inc");break;
01143     case eReflowReason_Resize:
01144       printf("Rsz");break;
01145     case eReflowReason_StyleChange:
01146       printf("Sty");break;
01147     case eReflowReason_Dirty:
01148       printf("Drt ");
01149       break;
01150     default:printf("<unknown>%d", aReflowState.reason);break;
01151   }
01152   
01153   printSize("AW", aReflowState.availableWidth);
01154   printSize("AH", aReflowState.availableHeight);
01155   printSize("CW", aReflowState.mComputedWidth);
01156   printSize("CH", aReflowState.mComputedHeight);
01157 
01158   nsCOMPtr<nsIDOMHTMLOptionsCollection> optionsTemp = getter_AddRefs(GetOptions(mContent));
01159   PRUint32 numOptions;
01160   optionsTemp->GetLength(&numOptions);
01161   printSize("NO", (nscoord)numOptions);
01162 
01163   printf(" *\n");
01164 
01165 #endif
01166 
01167 
01168   PRBool bailOnWidth;
01169   PRBool bailOnHeight;
01170 
01171   // Do initial check to see if we can bail out
01172   // If it is an Initial or Incremental Reflow we never bail out here
01173   // XXX right now we only bail if the width meets the criteria
01174   //
01175   // We bail:
01176   //   if mComputedWidth == NS_UNCONSTRAINEDSIZE and
01177   //      availableWidth == NS_UNCONSTRAINEDSIZE and 
01178   //      we have cached an available size
01179   //
01180   // We bail:
01181   //   if mComputedWidth == NS_UNCONSTRAINEDSIZE and
01182   //      availableWidth != NS_UNCONSTRAINEDSIZE and 
01183   //      availableWidth minus its border equals our cached available size
01184   //
01185   // We bail:
01186   //   if mComputedWidth != NS_UNCONSTRAINEDSIZE and
01187   //      cached availableSize.width == aReflowState.mComputedWidth and 
01188   //      cached AvailableSize.width == aCacheSize.width
01189   //
01190   // NOTE: this returns whether we are doing an Incremental reflow
01191   nsFormControlFrame::SkipResizeReflow(mCacheSize,
01192                                        mCachedAscent,
01193                                        mCachedMaxElementWidth,
01194                                        mCachedAvailableSize, 
01195                                        aDesiredSize, aReflowState, 
01196                                        aStatus, 
01197                                        bailOnWidth, bailOnHeight);
01198   if (bailOnWidth) {
01199 #ifdef DO_REFLOW_DEBUG // check or size
01200     nsMargin borderPadding(0, 0, 0, 0);
01201     CalcBorderPadding(borderPadding);
01202     UNCONSTRAINED_CHECK();
01203 #endif
01204     REFLOW_DEBUG_MSG3("^** Done nsCCF DW: %d  DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
01205     NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01206     NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01207     aDesiredSize.mOverflowArea.x      = 0;
01208     aDesiredSize.mOverflowArea.y      = 0;
01209     aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01210     aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01211     FinishAndStoreOverflow(&aDesiredSize);
01212     return NS_OK;
01213   }
01214 
01215   if (eReflowReason_Initial == aReflowState.reason) {
01216     if (NS_FAILED(CreateDisplayFrame(aPresContext))) {
01217       return NS_ERROR_FAILURE;
01218     }
01219   }
01220 
01221   // Go get all of the important frame
01222   nsresult rv = NS_OK;
01223   // Don't try to do any special sizing and positioning unless all of the frames
01224   // have been created.
01225   if ((nsnull == mDisplayFrame) ||
01226      (nsnull == mButtonFrame) ||
01227      (nsnull == mDropdownFrame)) 
01228   {
01229      // Since combobox frames are missing just do a normal area frame reflow
01230     return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
01231   }
01232 
01233   // Make sure the displayed text is the same as the selected option, bug 297389.
01234   PRInt32 selectedIndex;
01235   nsAutoString selectedOptionText;
01236   if (!mDroppedDown) {
01237     mListControlFrame->GetSelectedIndex(&selectedIndex);
01238   }
01239   else {
01240     // In dropped down mode the "selected index" is the hovered menu item,
01241     // we want the last selected item which is |mDisplayedIndex| in this case.
01242     selectedIndex = mDisplayedIndex;
01243   }
01244   if (selectedIndex != -1) {
01245     mListControlFrame->GetOptionText(selectedIndex, selectedOptionText);
01246   }
01247   if (mDisplayedOptionText != selectedOptionText) {
01248     RedisplayText(selectedIndex);
01249   }
01250 
01251   // We should cache this instead getting it everytime
01252   // the default size of the of scrollbar
01253   // that will be the default width of the dropdown button
01254   // the height will be the height of the text
01255   float w, h;
01256   // Get the width in Device pixels times p2t
01257   aPresContext->DeviceContext()->GetScrollBarDimensions(w, h);
01258   nscoord scrollbarWidth = NSToCoordRound(w);
01259   
01260   // set up a new reflow state for use throughout
01261   nsHTMLReflowState firstPassState(aReflowState);
01262   nsHTMLReflowMetrics dropdownDesiredSize(nsnull);
01263 
01264   // Check to see if this a fully unconstrained reflow
01265   PRBool fullyUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE;
01266 
01267   PRBool forceReflow = PR_FALSE;
01268 
01269   // Only reflow the display and button 
01270   // if they are the target of the incremental reflow, unless they change size. 
01271   if (eReflowReason_Incremental == aReflowState.reason) {
01272     nsHTMLReflowCommand *command = firstPassState.path->mReflowCommand;
01273 
01274     // Check to see if we are the target of the Incremental Reflow
01275     if (command) {
01276       // We need to check here to see if we can get away with just reflowing
01277       // the combobox and not the dropdown
01278       REFLOW_DEBUG_MSG("-----------------Target is Combobox------------\n");
01279 
01280       // If the mComputedWidth matches our cached display width 
01281       // then we get away with bailing out
01282       PRBool doFullReflow = firstPassState.mComputedWidth != NS_UNCONSTRAINEDSIZE &&
01283                             firstPassState.mComputedWidth != mItemDisplayWidth;
01284       if (!doFullReflow) {
01285         // OK, so we got lucky and the size didn't change
01286         // so do a simple reflow and bail out
01287         REFLOW_DEBUG_MSG("------------Reflowing AreaFrame and bailing----\n\n");
01288         ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, 
01289                            mDisplayFrame, mButtonFrame, mItemDisplayWidth, 
01290                            scrollbarWidth, aReflowState.mComputedBorderPadding);
01291         REFLOW_COUNTER();
01292         UNCONSTRAINED_CHECK();
01293         REFLOW_DEBUG_MSG3("&** Done nsCCF DW: %d  DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
01294         NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01295         NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01296         aDesiredSize.mOverflowArea.x      = 0;
01297         aDesiredSize.mOverflowArea.y      = 0;
01298         aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01299         aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01300       }
01301       else {
01302         // Nope, something changed that affected our size 
01303         // so we need to do a full reflow and resize ourself
01304         REFLOW_DEBUG_MSG("------------Do Full Reflow----\n\n");
01305         firstPassState.reason = eReflowReason_StyleChange;
01306         firstPassState.path = nsnull;
01307         forceReflow = PR_TRUE;
01308       }
01309     }
01310 
01311     // See if any of the children are targets, as well.
01312     nsReflowPath::iterator iter = aReflowState.path->FirstChild();
01313     nsReflowPath::iterator end = aReflowState.path->EndChildren();
01314     for ( ; iter != end; ++iter) {
01315       // Now, see if our target is the dropdown
01316       // If so, maybe an items was added or some style changed etc.
01317       //               OR
01318       // We get an Incremental reflow on the dropdown when it is being 
01319       // shown or hidden.
01320       if (*iter == mDropdownFrame) {
01321         REFLOW_DEBUG_MSG("---------Target is Dropdown (Clearing Unc DD Size)---\n");
01322         // Nope, we were unlucky so now we do a full reflow
01323         mCachedUncDropdownSize.width  = kSizeNotSet;
01324         mCachedUncDropdownSize.height = kSizeNotSet;       
01325         REFLOW_DEBUG_MSG("---- Doing Full Reflow\n");
01326         // This is an incremental reflow targeted at the dropdown list
01327         // and it didn't have anything to do with being show or hidden.
01328         // 
01329         // The incremental reflow will not get to the dropdown list 
01330         // because it is in the "popup" list 
01331         // when this flow of control drops out of this if it will do a reflow
01332         // on the AreaFrame which SHOULD make it get tothe drop down 
01333         // except that it is in the popup list, so we have it reflowed as
01334         // a StyleChange, this is not as effecient as doing an Incremental
01335         //
01336         // At this point we want to by pass the reflow optimization in the dropdown
01337         // because we aren't why it is getting an incremental reflow, but we do
01338         // know that it needs to be resized or restyled
01339         //mListControlFrame->SetOverrideReflowOptimization(PR_TRUE);
01340 
01341       } else if (*iter == mDisplayFrame || *iter == mButtonFrame) {
01342         REFLOW_DEBUG_MSG2("-----------------Target is %s------------\n", (*iter == mDisplayFrame?"DisplayItem Frame":"DropDown Btn Frame"));
01343         // The incremental reflow is targeted at either the block or the button
01344         REFLOW_DEBUG_MSG("---- Doing AreaFrame Reflow and then bailing out\n");
01345         // Do simple reflow and bail out
01346         ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, 
01347                        mDisplayFrame, mButtonFrame, 
01348                        mItemDisplayWidth, scrollbarWidth,
01349                        aReflowState.mComputedBorderPadding,
01350                        kSizeNotSet, PR_TRUE);
01351         REFLOW_DEBUG_MSG3("+** Done nsCCF DW: %d  DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
01352         REFLOW_COUNTER();
01353         UNCONSTRAINED_CHECK();
01354         NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01355         NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01356         aDesiredSize.mOverflowArea.x      = 0;
01357         aDesiredSize.mOverflowArea.y      = 0;
01358         aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01359         aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01360         continue;
01361       } else {
01362         nsIFrame * plainLstFrame;
01363         if (NS_SUCCEEDED(mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void**)&plainLstFrame))) {
01364           nsIFrame * frame = plainLstFrame->GetFirstChild(nsnull);
01365           nsIScrollableFrame * scrollFrame;
01366           if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsIScrollableFrame), (void**)&scrollFrame))) {
01367             plainLstFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
01368 
01369             aDesiredSize.width  = mCacheSize.width;
01370             aDesiredSize.height = mCacheSize.height;
01371             aDesiredSize.ascent = mCachedAscent;
01372             aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent;
01373 
01374             if (aDesiredSize.mComputeMEW) {
01375               aDesiredSize.mMaxElementWidth = mCachedMaxElementWidth;
01376             }
01377             NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01378             NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01379             aDesiredSize.mOverflowArea.x      = 0;
01380             aDesiredSize.mOverflowArea.y      = 0;
01381             aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01382             aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01383             continue;
01384           }
01385         }
01386 
01387         // Here the target of the reflow was a child of the dropdown list
01388         // so we must do a full reflow
01389         REFLOW_DEBUG_MSG("-----------------Target is Dropdown's Child (Option Item)------------\n");
01390         REFLOW_DEBUG_MSG("---- Doing Reflow as StyleChange\n");
01391       }
01392       firstPassState.reason = eReflowReason_StyleChange;
01393       firstPassState.path = nsnull;
01394       mListControlFrame->SetOverrideReflowOptimization(PR_TRUE);
01395       forceReflow = PR_TRUE;
01396     }
01397   }
01398 
01399 #ifdef IBMBIDI
01400   else if (eReflowReason_StyleChange == aReflowState.reason) {
01401     forceReflow = PR_TRUE;
01402   }
01403 #endif // IBMBIDI
01404 
01405   // Here is another special optimization
01406   // Only reflow the dropdown if it has never been reflowed unconstrained
01407   //
01408   // Or someone up above here may want to force it to be reflowed 
01409   // by setting one or both of these to kSizeNotSet
01410   if ((mCachedUncDropdownSize.width == kSizeNotSet && 
01411        mCachedUncDropdownSize.height == kSizeNotSet) || forceReflow) {
01412     REFLOW_DEBUG_MSG3("---Re %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); 
01413 
01414     // Tell it we are doing the first pass, which means it will
01415     // do the unconstained reflow and skip the second reflow this time around
01416     nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
01417     lcf->SetPassId(1);
01418     // A width has not been specified for the select so size the display area
01419     // to match the width of the longest item in the drop-down list. The dropdown
01420     // list has already been reflowed and sized to shrink around its contents above.
01421     ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, 
01422                           aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 
01423     lcf->SetPassId(0); // reset it back
01424 
01425     if (forceReflow) {
01426       mCachedUncDropdownSize.width  = dropdownDesiredSize.width;
01427       mCachedUncDropdownSize.height = dropdownDesiredSize.height;
01428     }
01429   } else {
01430     // Here we pretended we did an unconstrained reflow
01431     // so we set the cached values and continue on
01432     REFLOW_DEBUG_MSG3("--- Using Cached ListBox Size %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); 
01433     dropdownDesiredSize.width  = mCachedUncDropdownSize.width;
01434     dropdownDesiredSize.height = mCachedUncDropdownSize.height;
01435   }
01436 
01438   // XXX - I need to clean this nect part up a little it is very redundant
01439 
01440   // Check here to if this is a mComputed unconstrained reflow
01441   PRBool computedUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE;
01442   if (computedUnconstrained && !forceReflow) {
01443     // Because Incremental reflows aren't actually getting to the dropdown
01444     // we cache the size from when it did a fully unconstrained reflow
01445     // we then check to see if the size changed at all, 
01446     // if not then bail out we don't need to worry
01447     if (mCachedUncDropdownSize.width == kSizeNotSet && mCachedUncDropdownSize.height == kSizeNotSet) {
01448       mCachedUncDropdownSize.width  = dropdownDesiredSize.width;
01449       mCachedUncDropdownSize.height = dropdownDesiredSize.height;
01450       REFLOW_DEBUG_MSG3("---1 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); 
01451 
01452     } else if (mCachedUncDropdownSize.width == dropdownDesiredSize.width &&
01453                mCachedUncDropdownSize.height == dropdownDesiredSize.height) {
01454 
01455       if (mCachedUncComboSize.width != kSizeNotSet && mCachedUncComboSize.height != kSizeNotSet) {
01456         REFLOW_DEBUG_MSG3("--- Bailing because of mCachedUncDropdownSize %d,%d\n\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); 
01457         aDesiredSize.width  = mCachedUncComboSize.width;
01458         aDesiredSize.height = mCachedUncComboSize.height;
01459         aDesiredSize.ascent = mCachedAscent;
01460         aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent;
01461 
01462         if (aDesiredSize.mComputeMEW) {
01463           aDesiredSize.mMaxElementWidth = mCachedMaxElementWidth;
01464         }
01465         UNCONSTRAINED_CHECK();
01466         REFLOW_DEBUG_MSG3("#** Done nsCCF DW: %d  DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
01467         NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01468         NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01469         aDesiredSize.mOverflowArea.x      = 0;
01470         aDesiredSize.mOverflowArea.y      = 0;
01471         aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01472         aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01473         FinishAndStoreOverflow(&aDesiredSize);
01474         return NS_OK;
01475       }
01476     } else {
01477       mCachedUncDropdownSize.width  = dropdownDesiredSize.width;
01478       mCachedUncDropdownSize.height = dropdownDesiredSize.height;
01479     }
01480   }
01481   // clean up stops here
01483 
01484   // So this point we know we flowed the dropdown unconstrained
01485   // now we get to figure out how big we need to be and 
01486   // 
01487   // We don't reflow the combobox here at the new size
01488   // we cache its new size and reflow it on the dropdown
01489   nsSize size;
01490   PRInt32 length = 0;
01491   mListControlFrame->GetNumberOfOptions(&length);
01492 
01493   // dropdownRect will hold the content size (minus border padding) 
01494   // for the display area
01495   nsRect dropdownRect = mDropdownFrame->GetRect();
01496   if (eReflowReason_Resize == aReflowState.reason) {
01497     dropdownRect.Deflate(aReflowState.mComputedBorderPadding);
01498   }
01499 
01500   // Get maximum size of the largest item in the dropdown
01501   // The height of the display frame will be that height
01502   // the width will be the same as 
01503   // the dropdown width (minus its borderPadding) OR
01504   // a caculation off the mComputedWidth from reflow
01505   mListControlFrame->GetMaximumSize(size);
01506 
01507   // the variable "size" will now be 
01508   // the default size of the dropdown btn
01509   if (scrollbarWidth > 0) {
01510     size.width = scrollbarWidth;
01511   }
01512 
01513   // Get the border and padding for the dropdown
01514   nsMargin dropBorderPadding(0, 0, 0, 0);
01515   mDropdownFrame->CalcBorderPadding(dropBorderPadding);
01516 
01517   // get the borderPadding for the display area
01518   nsMargin dspBorderPadding(0, 0, 0, 0);
01519   mDisplayFrame->CalcBorderPadding(dspBorderPadding);
01520 
01521   // Substract dropdown's borderPadding from the width of the dropdown rect
01522   // to get the size of the content area
01523   //
01524   // the height will come from the mDisplayFrame's height
01525   // declare a size for the item display frame
01526 
01527   //Set the desired size for the button and display frame
01528   if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) {
01529     REFLOW_DEBUG_MSG("Unconstrained.....\n");
01530     REFLOW_DEBUG_MSG4("*B mItemDisplayWidth %d  dropdownRect.width:%d dropdownRect.w+h %d\n", PX(mItemDisplayWidth), PX(dropdownRect.width), PX((dropBorderPadding.left + dropBorderPadding.right)));
01531 
01532     // Start with the dropdown rect's width (at this stage, it's the
01533     // natural width of the content in the list, i.e., the width of
01534     // the widest content, i.e. the preferred width for the display
01535     // frame) and add room for the button, which is assumed to match
01536     // the width of the scrollbar (note that the scrollbarWidth is
01537     // passed as aBtnWidth to ReflowCombobox).  (When the dropdown was
01538     // an nsScrollFrame the scrollbar width seems to have already been
01539     // added to its unconstrained width.)
01540     mItemDisplayWidth = dropdownRect.width + scrollbarWidth;
01541 
01542     REFLOW_DEBUG_MSG2("*  mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
01543 
01544     // mItemDisplayWidth must be the size of the "display" frame including it's 
01545     // border and padding, but NOT including the comboboxes border and padding
01546     mItemDisplayWidth += dspBorderPadding.left + dspBorderPadding.right;
01547     mItemDisplayWidth -= aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right;
01548 
01549     REFLOW_DEBUG_MSG2("*A mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
01550 
01551   } else {
01552     REFLOW_DEBUG_MSG("Constrained.....\n");
01553     if (firstPassState.mComputedWidth > 0) {
01554       // Compute the display item's width from reflow's mComputedWidth
01555       // mComputedWidth has already excluded border and padding
01556       // so subtract off the button's size
01557       REFLOW_DEBUG_MSG3("B mItemDisplayWidth %d    %d\n", PX(mItemDisplayWidth), PX(dspBorderPadding.right));
01558       // Display Frame's width comes from the mComputedWidth and therefore implies that it
01559       // includes the "display" frame's border and padding.
01560       mItemDisplayWidth = firstPassState.mComputedWidth;
01561       REFLOW_DEBUG_MSG2("A mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
01562       REFLOW_DEBUG_MSG4("firstPassState.mComputedWidth %d -  size.width %d dspBorderPadding.right %d\n", PX(firstPassState.mComputedWidth), PX(size.width), PX(dspBorderPadding.right));
01563     }
01564   }
01565 
01566   // Fix for Bug 44788 (remove this comment later)
01567   if (firstPassState.mComputedHeight > 0 && NS_UNCONSTRAINEDSIZE != firstPassState.mComputedHeight) {
01568     size.height = firstPassState.mComputedHeight;
01569   }
01570 
01571   if (mCacheSize.height != size.height) {
01572     // if the cached height is not equal to the current height,
01573     // the cached height is reset.
01574     mCacheSize.height = kSizeNotSet;
01575   }
01576 
01577   // this reflows and makes and last minute adjustments
01578   ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, 
01579                      mDisplayFrame, mButtonFrame, mItemDisplayWidth, scrollbarWidth, 
01580                      aReflowState.mComputedBorderPadding, size.height);
01581 
01582   // The dropdown was reflowed UNCONSTRAINED before, now we need to reflow it
01583   // so that all children match the desired width.
01584   // The dropdown MUST always be either the same size as the combo or larger
01585   // if necessary. Note that individual children can be narrower in case they
01586   // are constrained by 'width', 'max-width' etc.
01587   if (eReflowReason_Initial == firstPassState.reason) {
01588     firstPassState.reason = eReflowReason_Resize;
01589   }
01590   REFLOW_DEBUG_MSG3("*** Reflowing ListBox to width: %d it was %d\n", PX(aDesiredSize.width), PX(dropdownDesiredSize.width));
01591 
01592   // Tell it we are doing the second pass, which means we will skip
01593   // doing the unconstained reflow, we already know that size
01594   nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
01595   lcf->SetPassId(2);
01596   // Reflow the dropdown list to be
01597   // MAX(width of the display + button, width of the widest option). bug 305705.
01598   const nscoord availableWidth =
01599     PR_MAX(aDesiredSize.width, dropdownDesiredSize.width -
01600                                aReflowState.mComputedBorderPadding.left -
01601                                aReflowState.mComputedBorderPadding.right);
01602   ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize,
01603                         firstPassState, aStatus,
01604                         availableWidth, NS_UNCONSTRAINEDSIZE);
01605   lcf->SetPassId(0);
01606 
01607   // Set the max element size to be the same as the desired element size.
01608   if (aDesiredSize.mComputeMEW) {
01609     aDesiredSize.SetMEWToActualWidth(aReflowState.mStylePosition->mWidth.GetUnit());
01610   }
01611 
01612 #if 0
01613   COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) 
01614 #endif
01615 
01616   // cache the availabe size to be our desired size minus the borders
01617   // this is so if our cached available size is ever equal to or less 
01618   // than the real available size we can bail out
01619   if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) {
01620     mCachedAvailableSize.width  = aDesiredSize.width -
01621       (aReflowState.mComputedBorderPadding.left +
01622        aReflowState.mComputedBorderPadding.right);
01623   }
01624   if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
01625     mCachedAvailableSize.height = aDesiredSize.height -
01626       (aReflowState.mComputedBorderPadding.top +
01627        aReflowState.mComputedBorderPadding.bottom);
01628   }
01629 
01630   nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedAscent, mCachedMaxElementWidth, aDesiredSize);
01631 
01632   REFLOW_DEBUG_MSG3("** Done nsCCF DW: %d  DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
01633   REFLOW_COUNTER();
01634   UNCONSTRAINED_CHECK();
01635 
01636   // If this was a fully unconstrained reflow we cache 
01637   // the combobox's unconstrained size
01638   if (fullyUnconstrained) {
01639     mCachedUncComboSize.width = aDesiredSize.width;
01640     mCachedUncComboSize.height = aDesiredSize.height;
01641   }
01642 
01643   NS_ASSERTION(aDesiredSize.width != kSizeNotSet,  "aDesiredSize.width != kSizeNotSet");
01644   NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
01645   aDesiredSize.mOverflowArea.x      = 0;
01646   aDesiredSize.mOverflowArea.y      = 0;
01647   aDesiredSize.mOverflowArea.width  = aDesiredSize.width;
01648   aDesiredSize.mOverflowArea.height = aDesiredSize.height;
01649 
01650   FinishAndStoreOverflow(&aDesiredSize);
01651   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
01652   return rv;
01653 
01654 }
01655 
01656 //--------------------------------------------------------------
01657 NS_IMETHODIMP
01658 nsComboboxControlFrame::GetName(nsAString* aResult)
01659 {
01660   return nsFormControlHelper::GetName(mContent, aResult);
01661 }
01662 
01663 NS_IMETHODIMP
01664 nsComboboxControlFrame::GetFrameForPoint(const nsPoint& aPoint,
01665                                          nsFramePaintLayer aWhichLayer,
01666                                          nsIFrame** aFrame)
01667 {
01668   // The button is getting the hover events so...
01669   // None of the children frames of the combobox get
01670   // the events. (like the button frame), that way
01671   // all event based style rules affect the combobox 
01672   // and not the any of the child frames.  (The inability
01673   // of the parent to be in the :hover state at the same
01674   // time as its children is really a bug (#5693 / #33736)
01675   // in the implementation of :hover.)
01676   
01677   // It would be theoretically more elegant to check the
01678   // children when not disabled, and then use event
01679   // capturing.  It would correctly handle situations (obscure!!)
01680   // where the children were visible but the parent was not.
01681   // Now the functionality of the OPTIONs depends on the SELECT
01682   // being visible.  Oh well...
01683 
01684   if ( mRect.Contains(aPoint) &&
01685        (aWhichLayer == NS_FRAME_PAINT_LAYER_FOREGROUND) ) {
01686     if (GetStyleVisibility()->IsVisible()) {
01687       *aFrame = this;
01688       return NS_OK;
01689     }
01690   }
01691   return NS_ERROR_FAILURE;
01692 }
01693 
01694 
01695 //--------------------------------------------------------------
01696 
01697 #ifdef NS_DEBUG
01698 NS_IMETHODIMP
01699 nsComboboxControlFrame::GetFrameName(nsAString& aResult) const
01700 {
01701   return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult);
01702 }
01703 #endif
01704 
01705 
01706 //----------------------------------------------------------------------
01707 // nsIComboboxControlFrame
01708 //----------------------------------------------------------------------
01709 NS_IMETHODIMP
01710 nsComboboxControlFrame::ShowDropDown(PRBool aDoDropDown) 
01711 { 
01712   if (nsFormControlHelper::GetDisabled(mContent)) {
01713     return NS_OK;
01714   }
01715 
01716   if (!mDroppedDown && aDoDropDown) {
01717     if (mListControlFrame) {
01718       mListControlFrame->SyncViewWithFrame();
01719     }
01720     ShowList(mPresContext, aDoDropDown); // might destroy us
01721     return NS_OK;
01722   } else if (mDroppedDown && !aDoDropDown) {
01723     ShowList(mPresContext, aDoDropDown); // might destroy us
01724     return NS_OK;
01725   }
01726 
01727   return NS_ERROR_FAILURE;
01728 }
01729 
01730 NS_IMETHODIMP
01731 nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame)
01732 {
01733   mDropdownFrame = aDropDownFrame;
01734  
01735   if (NS_OK != CallQueryInterface(mDropdownFrame, &mListControlFrame)) {
01736     return NS_ERROR_FAILURE;
01737   }
01738 
01739   return NS_OK;
01740 }
01741 
01742 NS_IMETHODIMP
01743 nsComboboxControlFrame::GetDropDown(nsIFrame** aDropDownFrame) 
01744 {
01745   if (nsnull == aDropDownFrame) {
01746     return NS_ERROR_FAILURE;
01747   }
01748 
01749   *aDropDownFrame = mDropdownFrame;
01750  
01751   return NS_OK;
01752 }
01753 
01754 NS_IMETHODIMP
01755 nsComboboxControlFrame::AbsolutelyPositionDropDown()
01756 {
01757   nsRect absoluteTwips;
01758   nsRect absolutePixels;
01759 
01760   if (NS_SUCCEEDED(nsFormControlFrame::GetAbsoluteFramePosition(mPresContext, this,  absoluteTwips, absolutePixels))) {
01761     PositionDropdown(mPresContext, GetRect().height, absoluteTwips, absolutePixels);
01762   }
01763   return NS_OK;
01764 }
01765 
01766 NS_IMETHODIMP
01767 nsComboboxControlFrame::GetAbsoluteRect(nsRect* aRect)
01768 {
01769   nsRect absoluteTwips;
01770   return nsFormControlFrame::GetAbsoluteFramePosition(mPresContext, this,  absoluteTwips, *aRect);
01771 }
01772 
01774 
01775 NS_IMETHODIMP
01776 nsComboboxControlFrame::RedisplaySelectedText()
01777 {
01778   PRInt32 selectedIndex;
01779   mListControlFrame->GetSelectedIndex(&selectedIndex);
01780 
01781   return RedisplayText(selectedIndex);
01782 }
01783 
01784 nsresult
01785 nsComboboxControlFrame::RedisplayText(PRInt32 aIndex)
01786 {
01787   // Get the text to display
01788   if (aIndex != -1) {
01789     mListControlFrame->GetOptionText(aIndex, mDisplayedOptionText);
01790   } else {
01791     mDisplayedOptionText.Truncate();
01792   }
01793   mDisplayedIndex = aIndex;
01794 
01795   REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n",
01796                     NS_LossyConvertUCS2toASCII(mDisplayedOptionText).get());
01797 
01798   // Send reflow command because the new text maybe larger
01799   nsresult rv = NS_OK;
01800   if (mDisplayContent && mEventQueueService) {
01801     // Don't call ActuallyDisplayText(PR_TRUE) directly here since that
01802     // could cause recursive frame construction. See bug 283117 and the comment in
01803     // HandleRedisplayTextEvent() below.
01804     nsCOMPtr<nsIEventQueue> eventQueue;
01805     rv = mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
01806                                                   getter_AddRefs(eventQueue));
01807     if (eventQueue) {
01808       RedisplayTextEvent* event = new RedisplayTextEvent(this);
01809       if (event) {
01810         // Revoke outstanding events to avoid out-of-order events which could mean
01811         // displaying the wrong text.
01812         if (mRedisplayTextEventPosted) {
01813           eventQueue->RevokeEvents(this);
01814           mRedisplayTextEventPosted = PR_FALSE;
01815         }
01816 
01817         rv = eventQueue->PostEvent(event);
01818 
01819         if (NS_SUCCEEDED(rv)) {
01820           mRedisplayTextEventPosted = PR_TRUE;
01821         } else {
01822           PL_DestroyEvent(event);
01823         }
01824       } else {
01825         rv = NS_ERROR_OUT_OF_MEMORY;
01826       }
01827     }
01828   }
01829   return rv;
01830 }
01831 
01832 void
01833 nsComboboxControlFrame::HandleRedisplayTextEvent()
01834 {
01835   // First, make sure that the content model is up to date and we've
01836   // constructed the frames for all our content in the right places.
01837   // Otherwise they'll end up under the wrong insertion frame when we
01838   // ActuallyDisplayText, since that flushes out the content sink by
01839   // calling SetText on a DOM node with aNotify set to true.  See bug
01840   // 289730.
01841   GetPresContext()->GetDocument()->
01842     FlushPendingNotifications(Flush_ContentAndNotify);
01843   
01844   // Redirect frame insertions during this method (see GetContentInsertionFrame())
01845   // so that any reframing that the frame constructor forces upon us is inserted
01846   // into the correct parent (mDisplayFrame). See bug 282607.
01847   NS_PRECONDITION(!mInRedisplayText, "Nested RedisplayText");
01848   mInRedisplayText = PR_TRUE;
01849   mRedisplayTextEventPosted = PR_FALSE;
01850 
01851   ActuallyDisplayText(PR_TRUE);
01852   mDisplayFrame->AddStateBits(NS_FRAME_IS_DIRTY);
01853   ReflowDirtyChild(GetPresContext()->PresShell(), mDisplayFrame);
01854 
01855   mInRedisplayText = PR_FALSE;
01856 }
01857 
01858 void
01859 nsComboboxControlFrame::ActuallyDisplayText(PRBool aNotify)
01860 {
01861   if (mDisplayedOptionText.IsEmpty()) {
01862     // Have to use a non-breaking space for line-height calculations
01863     // to be right
01864     static const PRUnichar space = 0xA0;
01865     mDisplayContent->SetText(&space, 1, aNotify);
01866   } else {
01867     mDisplayContent->SetText(mDisplayedOptionText, aNotify);
01868   }
01869 }
01870 
01871 NS_IMETHODIMP
01872 nsComboboxControlFrame::GetIndexOfDisplayArea(PRInt32* aDisplayedIndex)
01873 {
01874   NS_ENSURE_ARG_POINTER(aDisplayedIndex);
01875   *aDisplayedIndex = mDisplayedIndex;
01876   return NS_OK;
01877 }
01878 
01879 //----------------------------------------------------------------------
01880 // nsISelectControlFrame
01881 //----------------------------------------------------------------------
01882 NS_IMETHODIMP
01883 nsComboboxControlFrame::DoneAddingChildren(PRBool aIsDone)
01884 {
01885   nsISelectControlFrame* listFrame = nsnull;
01886   nsresult rv = NS_ERROR_FAILURE;
01887   if (mDropdownFrame != nsnull) {
01888     rv = CallQueryInterface(mDropdownFrame, &listFrame);
01889     if (listFrame) {
01890       rv = listFrame->DoneAddingChildren(aIsDone);
01891     }
01892   }
01893   return rv;
01894 }
01895 
01896 NS_IMETHODIMP
01897 nsComboboxControlFrame::AddOption(nsPresContext* aPresContext, PRInt32 aIndex)
01898 {
01899   if (aIndex <= mDisplayedIndex) {
01900     ++mDisplayedIndex;
01901   }
01902 
01903   nsListControlFrame* lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
01904   return lcf->AddOption(aPresContext, aIndex);
01905 }
01906   
01907 
01908 NS_IMETHODIMP
01909 nsComboboxControlFrame::RemoveOption(nsPresContext* aPresContext, PRInt32 aIndex)
01910 {
01911   PRInt32 len;
01912   mListControlFrame->GetNumberOfOptions(&len);
01913   if (len > 0) {
01914     if (aIndex < mDisplayedIndex) {
01915       --mDisplayedIndex;
01916     } else if (aIndex == mDisplayedIndex) {
01917       mDisplayedIndex = 0; // IE6 compat
01918       RedisplayText(mDisplayedIndex);
01919     }
01920   }
01921   else {
01922     // If we removed the last option, we need to blank things out
01923     RedisplayText(-1);
01924   }
01925 
01926   nsListControlFrame* lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
01927   return lcf->RemoveOption(aPresContext, aIndex);
01928 }
01929 
01930 NS_IMETHODIMP
01931 nsComboboxControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue)
01932 {
01933   nsISelectControlFrame* listFrame = nsnull;
01934   NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
01935 
01936   CallQueryInterface(mDropdownFrame, &listFrame);
01937   NS_ASSERTION(listFrame, "No list frame!");
01938 
01939   return listFrame->GetOptionSelected(aIndex, aValue);
01940 }
01941 
01942 //---------------------------------------------------------
01943 // Used by layout to determine if we have a fake option
01944 NS_IMETHODIMP
01945 nsComboboxControlFrame::GetDummyFrame(nsIFrame** aFrame)
01946 {
01947   nsISelectControlFrame* listFrame = nsnull;
01948   NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
01949 
01950   CallQueryInterface(mDropdownFrame, &listFrame);
01951   NS_ASSERTION(listFrame, "No list frame!");
01952 
01953   return listFrame->GetDummyFrame(aFrame);
01954 }
01955 
01956 NS_IMETHODIMP
01957 nsComboboxControlFrame::SetDummyFrame(nsIFrame* aFrame)
01958 {
01959   nsISelectControlFrame* listFrame = nsnull;
01960   NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
01961 
01962   CallQueryInterface(mDropdownFrame, &listFrame);
01963   NS_ASSERTION(listFrame, "No list frame!");
01964 
01965   return listFrame->SetDummyFrame(aFrame);
01966 }
01967 
01968 NS_IMETHODIMP
01969 nsComboboxControlFrame::OnSetSelectedIndex(PRInt32 aOldIndex, PRInt32 aNewIndex)
01970 {
01971   nsISelectControlFrame* listFrame = nsnull;
01972   NS_ASSERTION(mDropdownFrame, "No dropdown frame!");
01973 
01974   CallQueryInterface(mDropdownFrame, &listFrame);
01975   NS_ASSERTION(listFrame, "No list frame!");
01976 
01977   return listFrame->OnSetSelectedIndex(aOldIndex, aNewIndex);
01978 }
01979 
01980 // End nsISelectControlFrame
01981 //----------------------------------------------------------------------
01982 
01983 NS_IMETHODIMP 
01984 nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext, 
01985                                        nsGUIEvent*     aEvent,
01986                                        nsEventStatus*  aEventStatus)
01987 {
01988   NS_ENSURE_ARG_POINTER(aEventStatus);
01989   // temp fix until Bug 124990 gets fixed
01990   if (aPresContext->IsPaginated() && NS_IS_MOUSE_EVENT(aEvent)) {
01991     return NS_OK;
01992   }
01993 
01994   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
01995     return NS_OK;
01996   }
01997   if (nsFormControlHelper::GetDisabled(mContent)) {
01998     return NS_OK;
01999   }
02000 
02001   // If we have style that affects how we are selected, feed event down to
02002   // nsFrame::HandleEvent so that selection takes place when appropriate.
02003   const nsStyleUserInterface* uiStyle = GetStyleUserInterface();
02004   if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE || uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED)
02005     return nsAreaFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
02006     
02007   return NS_OK;
02008 }
02009 
02010 
02011 NS_IMETHODIMP 
02012 nsComboboxControlFrame::SetProperty(nsPresContext* aPresContext, nsIAtom* aName, const nsAString& aValue)
02013 {
02014   nsIFormControlFrame* fcFrame = nsnull;
02015   nsresult result = CallQueryInterface(mDropdownFrame, &fcFrame);
02016   if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
02017     return fcFrame->SetProperty(aPresContext, aName, aValue);
02018   }
02019   return result;
02020 }
02021 
02022 NS_IMETHODIMP 
02023 nsComboboxControlFrame::GetProperty(nsIAtom* aName, nsAString& aValue)
02024 {
02025   nsIFormControlFrame* fcFrame = nsnull;
02026   nsresult result = CallQueryInterface(mDropdownFrame, &fcFrame);
02027   if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
02028     return fcFrame->GetProperty(aName, aValue);
02029   }
02030   return result;
02031 }
02032 
02033 nsIFrame*
02034 nsComboboxControlFrame::GetContentInsertionFrame() {
02035   return mInRedisplayText ? mDisplayFrame : mDropdownFrame->GetContentInsertionFrame();
02036 }
02037 
02038 NS_IMETHODIMP 
02039 nsComboboxControlFrame::CreateDisplayFrame(nsPresContext* aPresContext)
02040 {
02041   if (mGoodToGo) {
02042     return NS_OK;
02043   }
02044 
02045   nsIPresShell *shell = aPresContext->PresShell();
02046   nsStyleSet *styleSet = shell->StyleSet();
02047 
02048   nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR);
02049   if (NS_FAILED(rv)) { return rv; }
02050   if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; }
02051 
02052   // create the style context for the anonymous frame
02053   nsRefPtr<nsStyleContext> styleContext;
02054   styleContext = styleSet->ResolvePseudoStyleFor(mContent, 
02055                                                  nsCSSAnonBoxes::mozDisplayComboboxControlFrame,
02056                                                  mStyleContext);
02057   if (!styleContext) { return NS_ERROR_NULL_POINTER; }
02058 
02059   // create a text frame and put it inside the block frame
02060   rv = NS_NewTextFrame(shell, &mTextFrame);
02061   if (NS_FAILED(rv)) { return rv; }
02062   if (!mTextFrame) { return NS_ERROR_NULL_POINTER; }
02063   nsRefPtr<nsStyleContext> textStyleContext;
02064   textStyleContext = styleSet->ResolveStyleForNonElement(styleContext);
02065   if (!textStyleContext) { return NS_ERROR_NULL_POINTER; }
02066   nsCOMPtr<nsIContent> content(do_QueryInterface(mDisplayContent));
02067   mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull);
02068   mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull);
02069 
02070   aPresContext->FrameManager()->SetPrimaryFrameFor(content, mTextFrame);
02071 
02072   rv = mDisplayFrame->Init(aPresContext, mContent, this, styleContext, nsnull);
02073   if (NS_FAILED(rv)) { return rv; }
02074 
02075   mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame);
02076 
02077   return NS_OK;
02078 }
02079 
02080 
02081 NS_IMETHODIMP
02082 nsComboboxControlFrame::CreateAnonymousContent(nsPresContext* aPresContext,
02083                                                nsISupportsArray& aChildList)
02084 {
02085   // The frames used to display the combo box and the button used to popup the dropdown list
02086   // are created through anonymous content. The dropdown list is not created through anonymous
02087   // content because it's frame is initialized specifically for the drop-down case and it is placed
02088   // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the
02089   // layout of the display and button. 
02090   //
02091   // Note: The value attribute of the display content is set when an item is selected in the dropdown list.
02092   // If the content specified below does not honor the value attribute than nothing will be displayed.
02093   // In addition, if the frame created by content below for does not implement the nsIFormControlFrame 
02094   // interface and honor the SetSuggestedSize method the placement and size of the display area will not
02095   // match what is normally desired for a combobox.
02096 
02097 
02098   // For now the content that is created corresponds to two input buttons. It would be better to create the
02099   // tag as something other than input, but then there isn't any way to create a button frame since it
02100   // isn't possible to set the display type in CSS2 to create a button frame.
02101 
02102     // create content used for display
02103   //nsIAtom* tag = NS_NewAtom("mozcombodisplay");
02104 
02105   // Add a child text content node for the label
02106 
02107   nsNodeInfoManager *nimgr = mContent->GetNodeInfo()->NodeInfoManager();
02108 
02109   nsCOMPtr<nsITextContent> labelContent;
02110   NS_NewTextNode(getter_AddRefs(labelContent), nimgr);
02111 
02112   if (labelContent) {
02113     // set the value of the text node
02114     mDisplayContent.swap(labelContent);
02115     mListControlFrame->GetSelectedIndex(&mDisplayedIndex);
02116     if (mDisplayedIndex != -1) {
02117       mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionText);
02118     }
02119     ActuallyDisplayText(PR_FALSE);
02120 
02121     nsCOMPtr<nsINodeInfo> nodeInfo;
02122     nimgr->GetNodeInfo(nsHTMLAtoms::input, nsnull, kNameSpaceID_None,
02123                        getter_AddRefs(nodeInfo));
02124 
02125     aChildList.AppendElement(mDisplayContent);
02126 
02127     // create button which drops the list down
02128     nsCOMPtr<nsIContent> btnContent;
02129     nsresult rv = NS_NewHTMLElement(getter_AddRefs(btnContent), nodeInfo);
02130     NS_ENSURE_SUCCESS(rv, rv);
02131 
02132     // make someone to listen to the button. If its pressed by someone like Accessibility
02133     // then open or close the combo box.
02134     nsCOMPtr<nsIDOMEventReceiver> eventReceiver(do_QueryInterface(btnContent));
02135     if (eventReceiver) {
02136        mButtonListener = new nsComboButtonListener(this);
02137        eventReceiver->AddEventListenerByIID(mButtonListener, NS_GET_IID(nsIDOMMouseListener));
02138     }
02139 
02140     btnContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::type, NS_LITERAL_STRING("button"), PR_FALSE);
02141     // Set tabindex="-1" so that the button is not tabbable
02142     btnContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex,
02143                         NS_LITERAL_STRING("-1"), PR_FALSE);
02144 
02145     aChildList.AppendElement(btnContent);
02146   }
02147 
02148   return NS_OK;
02149 }
02150 
02151 NS_IMETHODIMP 
02152 nsComboboxControlFrame::CreateFrameFor(nsPresContext*   aPresContext,
02153                                        nsIContent *      aContent,
02154                                        nsIFrame**        aFrame) 
02155 { 
02156   NS_PRECONDITION(nsnull != aFrame, "null ptr");
02157   NS_PRECONDITION(nsnull != aContent, "null ptr");
02158   NS_PRECONDITION(nsnull != aPresContext, "null ptr");
02159 
02160   *aFrame = nsnull;
02161   NS_ASSERTION(mDisplayContent != nsnull, "mDisplayContent can't be null!");
02162 
02163   if (!mGoodToGo) {
02164     return NS_ERROR_FAILURE;
02165   }
02166 
02167   nsCOMPtr<nsIContent> content(do_QueryInterface(mDisplayContent));
02168   if (aContent == content.get()) {
02169     // Get PresShell
02170     nsIPresShell *shell = aPresContext->PresShell();
02171     nsStyleSet *styleSet = shell->StyleSet();
02172 
02173     // Start by by creating a containing frame
02174     nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR);
02175     if (NS_FAILED(rv))  { return rv; }
02176     if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; }
02177 
02178     // create the style context for the anonymous block frame
02179     nsRefPtr<nsStyleContext> styleContext;
02180     styleContext = styleSet->ResolvePseudoStyleFor(mContent, 
02181                                                    nsCSSAnonBoxes::mozDisplayComboboxControlFrame,
02182                                                    mStyleContext);
02183     if (!styleContext) { return NS_ERROR_NULL_POINTER; }
02184 
02185     // Create a text frame and put it inside the block frame
02186     rv = NS_NewTextFrame(shell, &mTextFrame);
02187     if (NS_FAILED(rv)) { return rv; }
02188     if (!mTextFrame)   { return NS_ERROR_NULL_POINTER; }
02189     nsRefPtr<nsStyleContext> textStyleContext;
02190     textStyleContext = styleSet->ResolveStyleForNonElement(styleContext);
02191     if (!textStyleContext) { return NS_ERROR_NULL_POINTER; }
02192 
02193     // initialize the text frame
02194     mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull);
02195     mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull);
02196 
02197     /*nsCOMPtr<nsIFrameManager> frameManager;
02198     rv = shell->GetFrameManager(getter_AddRefs(frameManager));
02199     if (NS_FAILED(rv)) { return rv; }
02200     if (!frameManager) { return NS_ERROR_NULL_POINTER; }
02201     frameManager->SetPrimaryFrameFor(content, mTextFrame);
02202     */
02203 
02204     rv = mDisplayFrame->Init(aPresContext, mContent, this, styleContext, nsnull);
02205     if (NS_FAILED(rv)) { return rv; }
02206 
02207     mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame);
02208     *aFrame = mDisplayFrame;
02209     return NS_OK;
02210   }
02211 
02212   return NS_ERROR_FAILURE;
02213 }
02214 
02215 
02216 
02217 
02218 NS_IMETHODIMP 
02219 nsComboboxControlFrame::SetSuggestedSize(nscoord aWidth, nscoord aHeight)
02220 {
02221   return NS_OK;
02222 }
02223 
02224 
02225 
02226 NS_IMETHODIMP
02227 nsComboboxControlFrame::Destroy(nsPresContext* aPresContext)
02228 {
02229   // Revoke queued RedisplayTextEvents
02230   if (mEventQueueService) {
02231     nsCOMPtr<nsIEventQueue> eventQueue;
02232     mEventQueueService->GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
02233                                              getter_AddRefs(eventQueue));
02234     if (eventQueue) {
02235       eventQueue->RevokeEvents(this);
02236     }
02237   }
02238 
02239   nsFormControlFrame::RegUnRegAccessKey(mPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_FALSE);
02240 
02241   if (mDroppedDown) {
02242     // Get parent view
02243     nsIFrame * listFrame;
02244     if (NS_OK == mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void **)&listFrame)) {
02245       nsIView* view = listFrame->GetView();
02246       NS_ASSERTION(view, "nsComboboxControlFrame view is null");
02247       if (view) {
02248          nsIWidget* widget = view->GetWidget();
02249         if (widget)
02250           widget->CaptureRollupEvents(this, PR_FALSE, PR_TRUE);
02251       }
02252     }
02253   }
02254 
02255    // Cleanup frames in popup child list
02256   mPopupFrames.DestroyFrames(aPresContext);
02257 
02258   if (!mGoodToGo) {
02259     if (mDisplayFrame) {
02260       aPresContext->PresShell()->FrameConstructor()->
02261         RemoveMappingsForFrameSubtree(mDisplayFrame, nsnull);
02262       mDisplayFrame->Destroy(aPresContext);
02263       mDisplayFrame=nsnull;
02264     }
02265   }
02266 
02267   return nsAreaFrame::Destroy(aPresContext);
02268 }
02269 
02270 
02271 nsIFrame*
02272 nsComboboxControlFrame::GetFirstChild(nsIAtom* aListName) const
02273 {
02274   if (nsLayoutAtoms::popupList == aListName) {
02275     return mPopupFrames.FirstChild();
02276   }
02277   return nsAreaFrame::GetFirstChild(aListName);
02278 }
02279 
02280 NS_IMETHODIMP
02281 nsComboboxControlFrame::SetInitialChildList(nsPresContext* aPresContext,
02282                                                nsIAtom*        aListName,
02283                                                nsIFrame*       aChildList)
02284 {
02285   nsresult rv = NS_OK;
02286   if (nsLayoutAtoms::popupList == aListName) {
02287     mPopupFrames.SetFrames(aChildList);
02288   } else {
02289     rv = nsAreaFrame::SetInitialChildList(aPresContext, aListName, aChildList);
02290 
02291     for (nsIFrame * child = aChildList; child;
02292          child = child->GetNextSibling()) {
02293       nsIFormControlFrame* fcFrame = nsnull;
02294       CallQueryInterface(child, &fcFrame);
02295       if (fcFrame) {
02296         if (fcFrame->GetFormControlType() == NS_FORM_INPUT_BUTTON) {
02297           mButtonFrame = child;
02298         }
02299       } else {
02300         mDisplayFrame = child;
02301       }
02302     }
02303   }
02304   return rv;
02305 }
02306 
02307 nsIAtom*
02308 nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex) const
02309 {
02310    // Maintain a separate child list for the dropdown list (i.e. popup listbox)
02311    // This is necessary because we don't want the listbox to be included in the layout
02312    // of the combox's children because it would take up space, when it is suppose to
02313    // be floating above the display.
02314   if (aIndex <= NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX) {
02315     return nsAreaFrame::GetAdditionalChildListName(aIndex);
02316   }
02317   
02318   if (NS_COMBO_FRAME_POPUP_LIST_INDEX == aIndex) {
02319     return nsLayoutAtoms::popupList;
02320   }
02321   return nsnull;
02322 }
02323 
02324 PRIntn
02325 nsComboboxControlFrame::GetSkipSides() const
02326 {    
02327     // Don't skip any sides during border rendering
02328   return 0;
02329 }
02330 
02331 
02332 //----------------------------------------------------------------------
02333   //nsIRollupListener
02334 //----------------------------------------------------------------------
02335 NS_IMETHODIMP 
02336 nsComboboxControlFrame::Rollup()
02337 {
02338   if (mDroppedDown) {
02339     nsWeakFrame weakFrame(this);
02340     mListControlFrame->AboutToRollup(); // might destroy us
02341     if (!weakFrame.IsAlive())
02342       return NS_OK;
02343     ShowDropDown(PR_FALSE); // might destroy us
02344     if (!weakFrame.IsAlive())
02345       return NS_OK;
02346     mListControlFrame->CaptureMouseEvents(mPresContext, PR_FALSE);
02347   }
02348   return NS_OK;
02349 }
02350 
02351 NS_IMETHODIMP
02352 nsComboboxControlFrame::RollupFromList(nsPresContext* aPresContext)
02353 {
02354   if (ShowList(aPresContext, PR_FALSE))
02355     mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
02356   return NS_OK;
02357 }
02358 
02359 NS_IMETHODIMP_(PRInt32)
02360 nsComboboxControlFrame::UpdateRecentIndex(PRInt32 aIndex)
02361 {
02362   PRInt32 index = mRecentSelectedIndex;
02363   if (mRecentSelectedIndex == NS_SKIP_NOTIFY_INDEX || aIndex == NS_SKIP_NOTIFY_INDEX)
02364     mRecentSelectedIndex = aIndex;
02365   return index;
02366 }
02367 
02368 NS_METHOD 
02369 nsComboboxControlFrame::Paint(nsPresContext*     aPresContext,
02370                              nsIRenderingContext& aRenderingContext,
02371                              const nsRect&        aDirtyRect,
02372                              nsFramePaintLayer    aWhichLayer,
02373                              PRUint32             aFlags)
02374 {
02375   PRBool isVisible;
02376   if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && !isVisible) {
02377     return NS_OK;
02378   }
02379 #ifdef NOISY
02380   printf("%p paint layer %d at (%d, %d, %d, %d)\n", this, aWhichLayer, 
02381     aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
02382 #endif
02383   // We paint everything in the foreground so that the form control's
02384   // parents cannot paint over it in other passes (bug 95826).
02385   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
02386     nsAreaFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,
02387                        NS_FRAME_PAINT_LAYER_BACKGROUND);
02388     nsAreaFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,
02389                        NS_FRAME_PAINT_LAYER_FLOATS);
02390     nsAreaFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,
02391                        NS_FRAME_PAINT_LAYER_FOREGROUND);
02392 
02393     // nsITheme should take care of drawing the focus border, but currently does so only on Mac.
02394     // If all of the nsITheme implementations are fixed to draw the focus border correctly,
02395     // this #ifdef should be replaced with a -moz-appearance / ThemeSupportsWidget() check.
02396 
02397     if (!ToolkitHasNativePopup() && mDisplayFrame) {
02398       aRenderingContext.PushState();
02399       nsRect clipRect = mDisplayFrame->GetRect();
02400       aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
02401       PaintChild(aPresContext, aRenderingContext, aDirtyRect, 
02402                  mDisplayFrame, NS_FRAME_PAINT_LAYER_BACKGROUND);
02403       PaintChild(aPresContext, aRenderingContext, aDirtyRect, 
02404                  mDisplayFrame, NS_FRAME_PAINT_LAYER_FOREGROUND);
02405 
02407       // draw focus
02408       // XXX This is only temporary
02409       // Only paint the focus if we're visible
02410       if (GetStyleVisibility()->IsVisible()) {
02411         if (!nsFormControlHelper::GetDisabled(mContent) && mFocused == this) {
02412           aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
02413           aRenderingContext.SetColor(0);
02414         } else {
02415           aRenderingContext.SetColor(GetStyleBackground()->mBackgroundColor);
02416           aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
02417         }
02418         //aRenderingContext.DrawRect(clipRect);
02419         float p2t = aPresContext->PixelsToTwips();
02420         nscoord onePixel = NSIntPixelsToTwips(1, p2t);
02421         clipRect.width -= onePixel;
02422         clipRect.height -= onePixel;
02423         aRenderingContext.DrawLine(clipRect.x, clipRect.y, 
02424                                    clipRect.x+clipRect.width, clipRect.y);
02425         aRenderingContext.DrawLine(clipRect.x+clipRect.width, clipRect.y, 
02426                                    clipRect.x+clipRect.width, clipRect.y+clipRect.height);
02427         aRenderingContext.DrawLine(clipRect.x+clipRect.width, clipRect.y+clipRect.height, 
02428                                    clipRect.x, clipRect.y+clipRect.height);
02429         aRenderingContext.DrawLine(clipRect.x, clipRect.y+clipRect.height, 
02430                                    clipRect.x, clipRect.y);
02431         aRenderingContext.DrawLine(clipRect.x, clipRect.y+clipRect.height, 
02432                                    clipRect.x, clipRect.y);
02433       }
02435       aRenderingContext.PopState();
02436     }
02437   }
02438   
02439   // Call to the base class to draw selection borders when appropriate
02440   return nsFrame::Paint(aPresContext,aRenderingContext,aDirtyRect,aWhichLayer);
02441 }
02442 
02443 //----------------------------------------------------------------------
02444   //nsIScrollableViewProvider
02445 //----------------------------------------------------------------------
02446 nsIScrollableView* nsComboboxControlFrame::GetScrollableView()
02447 {
02448   if (!mDropdownFrame)
02449     return nsnull;
02450 
02451   nsIScrollableFrame* scrollable = nsnull;
02452   nsresult rv = CallQueryInterface(mDropdownFrame, &scrollable);
02453   if (NS_FAILED(rv))
02454     return nsnull;
02455 
02456   return scrollable->GetScrollableView();
02457 }
02458 
02459 //---------------------------------------------------------
02460 // gets the content (an option) by index and then set it as
02461 // being selected or not selected
02462 //---------------------------------------------------------
02463 NS_IMETHODIMP
02464 nsComboboxControlFrame::OnOptionSelected(nsPresContext* aPresContext,
02465                                          PRInt32 aIndex,
02466                                          PRBool aSelected)
02467 {
02468   if (mDroppedDown) {
02469     nsCOMPtr<nsISelectControlFrame> selectFrame
02470                                      = do_QueryInterface(mListControlFrame);
02471     if (selectFrame) {
02472       selectFrame->OnOptionSelected(aPresContext, aIndex, aSelected);
02473     }
02474   } else {
02475     if (aSelected) {
02476       RedisplayText(aIndex);
02477     } else {
02478       RedisplaySelectedText();
02479       FireValueChangeEvent(); // Fire after old option is unselected
02480     }
02481   }
02482 
02483   return NS_OK;
02484 }
02485 
02486 void nsComboboxControlFrame::FireValueChangeEvent()
02487 {
02488   // Fire ValueChange event to indicate data value of combo box has changed
02489   nsCOMPtr<nsIDOMEvent> event;
02490   nsCOMPtr<nsIEventListenerManager> manager;
02491   mContent->GetListenerManager(getter_AddRefs(manager));
02492   nsPresContext* presContext = GetPresContext();
02493   if (manager &&
02494       NS_SUCCEEDED(manager->CreateEvent(presContext, nsnull, NS_LITERAL_STRING("Events"), getter_AddRefs(event)))) {
02495     event->InitEvent(NS_LITERAL_STRING("ValueChange"), PR_TRUE, PR_TRUE);
02496 
02497     nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
02498     privateEvent->SetTrusted(PR_TRUE);
02499 
02500     PRBool defaultActionEnabled;
02501     presContext->EventStateManager()->DispatchNewEvent(mContent, event,
02502                                                        &defaultActionEnabled);
02503   }
02504 }
02505 
02506 NS_IMETHODIMP
02507 nsComboboxControlFrame::OnContentReset()
02508 {
02509   if (mListControlFrame) {
02510     nsCOMPtr<nsIFormControlFrame> formControl =
02511       do_QueryInterface(mListControlFrame);
02512     formControl->OnContentReset();
02513   }
02514   return NS_OK;
02515 }
02516 
02517 
02518 //--------------------------------------------------------
02519 // nsIStatefulFrame
02520 //--------------------------------------------------------
02521 NS_IMETHODIMP
02522 nsComboboxControlFrame::SaveState(nsPresContext* aPresContext,
02523                                   nsPresState** aState)
02524 {
02525   nsCOMPtr<nsIStatefulFrame> stateful(do_QueryInterface(mListControlFrame));
02526   NS_ASSERTION(stateful, "Couldn't cast list frame to stateful frame!!!");
02527   if (stateful) {
02528     return stateful->SaveState(aPresContext, aState);
02529   }
02530   return NS_OK;
02531 }
02532 
02533 NS_IMETHODIMP
02534 nsComboboxControlFrame::RestoreState(nsPresContext* aPresContext,
02535                                      nsPresState* aState)
02536 {
02537   if (!mListControlFrame)
02538     return NS_ERROR_FAILURE;
02539 
02540   nsIStatefulFrame* stateful;
02541   nsresult rv = CallQueryInterface(mListControlFrame, &stateful);
02542   NS_ASSERTION(NS_SUCCEEDED(rv), "Must implement nsIStatefulFrame");
02543   rv = stateful->RestoreState(aPresContext, aState);
02544   return rv;
02545 }
02546 
02547 
02548 //
02549 // Some toolkits (just Cocoa at this point) use a native widget
02550 // for the combobox popup, which affects drawing and event
02551 // handling here and in nsListControlFrame.
02552 // 
02553 
02554 /* static */
02555 PRBool
02556 nsComboboxControlFrame::ToolkitHasNativePopup()
02557 {
02558 #ifdef MOZ_WIDGET_COCOA
02559   return PR_TRUE;
02560 #else
02561   return PR_FALSE;
02562 #endif
02563 }
02564