1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsComboboxControlFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1612 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "nsCOMPtr.h" 1.9 +#include "nsComboboxControlFrame.h" 1.10 +#include "nsFocusManager.h" 1.11 +#include "nsFormControlFrame.h" 1.12 +#include "nsGkAtoms.h" 1.13 +#include "nsCSSAnonBoxes.h" 1.14 +#include "nsHTMLParts.h" 1.15 +#include "nsIFormControl.h" 1.16 +#include "nsNameSpaceManager.h" 1.17 +#include "nsIListControlFrame.h" 1.18 +#include "nsPIDOMWindow.h" 1.19 +#include "nsIPresShell.h" 1.20 +#include "nsContentList.h" 1.21 +#include "nsView.h" 1.22 +#include "nsViewManager.h" 1.23 +#include "nsIDOMEventListener.h" 1.24 +#include "nsIDOMNode.h" 1.25 +#include "nsISelectControlFrame.h" 1.26 +#include "nsContentUtils.h" 1.27 +#include "nsIDocument.h" 1.28 +#include "nsINodeInfo.h" 1.29 +#include "nsIScrollableFrame.h" 1.30 +#include "nsListControlFrame.h" 1.31 +#include "nsAutoPtr.h" 1.32 +#include "nsStyleSet.h" 1.33 +#include "nsNodeInfoManager.h" 1.34 +#include "nsContentCreatorFunctions.h" 1.35 +#include "nsLayoutUtils.h" 1.36 +#include "nsDisplayList.h" 1.37 +#include "nsITheme.h" 1.38 +#include "nsRenderingContext.h" 1.39 +#include "mozilla/Likely.h" 1.40 +#include <algorithm> 1.41 +#include "nsTextNode.h" 1.42 +#include "mozilla/AsyncEventDispatcher.h" 1.43 +#include "mozilla/EventStates.h" 1.44 +#include "mozilla/LookAndFeel.h" 1.45 +#include "mozilla/MouseEvents.h" 1.46 +#include "mozilla/unused.h" 1.47 + 1.48 +using namespace mozilla; 1.49 + 1.50 +NS_IMETHODIMP 1.51 +nsComboboxControlFrame::RedisplayTextEvent::Run() 1.52 +{ 1.53 + if (mControlFrame) 1.54 + mControlFrame->HandleRedisplayTextEvent(); 1.55 + return NS_OK; 1.56 +} 1.57 + 1.58 +class nsPresState; 1.59 + 1.60 +#define FIX_FOR_BUG_53259 1.61 + 1.62 +// Drop down list event management. 1.63 +// The combo box uses the following strategy for managing the drop-down list. 1.64 +// If the combo box or its arrow button is clicked on the drop-down list is displayed 1.65 +// If mouse exits the combo box with the drop-down list displayed the drop-down list 1.66 +// is asked to capture events 1.67 +// The drop-down list will capture all events including mouse down and up and will always 1.68 +// return with ListWasSelected method call regardless of whether an item in the list was 1.69 +// actually selected. 1.70 +// The ListWasSelected code will turn off mouse-capture for the drop-down list. 1.71 +// The drop-down list does not explicitly set capture when it is in the drop-down mode. 1.72 + 1.73 + 1.74 +/** 1.75 + * Helper class that listens to the combo boxes button. If the button is pressed the 1.76 + * combo box is toggled to open or close. this is used by Accessibility which presses 1.77 + * that button Programmatically. 1.78 + */ 1.79 +class nsComboButtonListener : public nsIDOMEventListener 1.80 +{ 1.81 +public: 1.82 + NS_DECL_ISUPPORTS 1.83 + 1.84 + NS_IMETHOD HandleEvent(nsIDOMEvent*) MOZ_OVERRIDE 1.85 + { 1.86 + mComboBox->ShowDropDown(!mComboBox->IsDroppedDown()); 1.87 + return NS_OK; 1.88 + } 1.89 + 1.90 + nsComboButtonListener(nsComboboxControlFrame* aCombobox) 1.91 + { 1.92 + mComboBox = aCombobox; 1.93 + } 1.94 + 1.95 + virtual ~nsComboButtonListener() {} 1.96 + 1.97 + nsComboboxControlFrame* mComboBox; 1.98 +}; 1.99 + 1.100 +NS_IMPL_ISUPPORTS(nsComboButtonListener, 1.101 + nsIDOMEventListener) 1.102 + 1.103 +// static class data member for Bug 32920 1.104 +nsComboboxControlFrame* nsComboboxControlFrame::sFocused = nullptr; 1.105 + 1.106 +nsIFrame* 1.107 +NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags) 1.108 +{ 1.109 + nsComboboxControlFrame* it = new (aPresShell) nsComboboxControlFrame(aContext); 1.110 + 1.111 + if (it) { 1.112 + // set the state flags (if any are provided) 1.113 + it->AddStateBits(aStateFlags); 1.114 + } 1.115 + 1.116 + return it; 1.117 +} 1.118 + 1.119 +NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame) 1.120 + 1.121 +//----------------------------------------------------------- 1.122 +// Reflow Debugging Macros 1.123 +// These let us "see" how many reflow counts are happening 1.124 +//----------------------------------------------------------- 1.125 +#ifdef DO_REFLOW_COUNTER 1.126 + 1.127 +#define MAX_REFLOW_CNT 1024 1.128 +static int32_t gTotalReqs = 0;; 1.129 +static int32_t gTotalReflows = 0;; 1.130 +static int32_t gReflowControlCntRQ[MAX_REFLOW_CNT]; 1.131 +static int32_t gReflowControlCnt[MAX_REFLOW_CNT]; 1.132 +static int32_t gReflowInx = -1; 1.133 + 1.134 +#define REFLOW_COUNTER() \ 1.135 + if (mReflowId > -1) \ 1.136 + gReflowControlCnt[mReflowId]++; 1.137 + 1.138 +#define REFLOW_COUNTER_REQUEST() \ 1.139 + if (mReflowId > -1) \ 1.140 + gReflowControlCntRQ[mReflowId]++; 1.141 + 1.142 +#define REFLOW_COUNTER_DUMP(__desc) \ 1.143 + if (mReflowId > -1) {\ 1.144 + gTotalReqs += gReflowControlCntRQ[mReflowId];\ 1.145 + gTotalReflows += gReflowControlCnt[mReflowId];\ 1.146 + printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \ 1.147 + mReflowId, (__desc), \ 1.148 + gReflowControlCnt[mReflowId], \ 1.149 + gReflowControlCntRQ[mReflowId],\ 1.150 + gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\ 1.151 + } 1.152 + 1.153 +#define REFLOW_COUNTER_INIT() \ 1.154 + if (gReflowInx < MAX_REFLOW_CNT) { \ 1.155 + gReflowInx++; \ 1.156 + mReflowId = gReflowInx; \ 1.157 + gReflowControlCnt[mReflowId] = 0; \ 1.158 + gReflowControlCntRQ[mReflowId] = 0; \ 1.159 + } else { \ 1.160 + mReflowId = -1; \ 1.161 + } 1.162 + 1.163 +// reflow messages 1.164 +#define REFLOW_DEBUG_MSG(_msg1) printf((_msg1)) 1.165 +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) 1.166 +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) 1.167 +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) 1.168 + 1.169 +#else //------------- 1.170 + 1.171 +#define REFLOW_COUNTER_REQUEST() 1.172 +#define REFLOW_COUNTER() 1.173 +#define REFLOW_COUNTER_DUMP(__desc) 1.174 +#define REFLOW_COUNTER_INIT() 1.175 + 1.176 +#define REFLOW_DEBUG_MSG(_msg) 1.177 +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) 1.178 +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) 1.179 +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) 1.180 + 1.181 + 1.182 +#endif 1.183 + 1.184 +//------------------------------------------ 1.185 +// This is for being VERY noisy 1.186 +//------------------------------------------ 1.187 +#ifdef DO_VERY_NOISY 1.188 +#define REFLOW_NOISY_MSG(_msg1) printf((_msg1)) 1.189 +#define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) 1.190 +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) 1.191 +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) 1.192 +#else 1.193 +#define REFLOW_NOISY_MSG(_msg) 1.194 +#define REFLOW_NOISY_MSG2(_msg1, _msg2) 1.195 +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) 1.196 +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) 1.197 +#endif 1.198 + 1.199 +//------------------------------------------ 1.200 +// Displays value in pixels or twips 1.201 +//------------------------------------------ 1.202 +#ifdef DO_PIXELS 1.203 +#define PX(__v) __v / 15 1.204 +#else 1.205 +#define PX(__v) __v 1.206 +#endif 1.207 + 1.208 +//------------------------------------------------------ 1.209 +//-- Done with macros 1.210 +//------------------------------------------------------ 1.211 + 1.212 +nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext* aContext) 1.213 + : nsBlockFrame(aContext) 1.214 + , mDisplayFrame(nullptr) 1.215 + , mButtonFrame(nullptr) 1.216 + , mDropdownFrame(nullptr) 1.217 + , mListControlFrame(nullptr) 1.218 + , mDisplayWidth(0) 1.219 + , mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX) 1.220 + , mDisplayedIndex(-1) 1.221 + , mLastDropDownAboveScreenY(nscoord_MIN) 1.222 + , mLastDropDownBelowScreenY(nscoord_MIN) 1.223 + , mDroppedDown(false) 1.224 + , mInRedisplayText(false) 1.225 + , mDelayedShowDropDown(false) 1.226 +{ 1.227 + REFLOW_COUNTER_INIT() 1.228 +} 1.229 + 1.230 +//-------------------------------------------------------------- 1.231 +nsComboboxControlFrame::~nsComboboxControlFrame() 1.232 +{ 1.233 + REFLOW_COUNTER_DUMP("nsCCF"); 1.234 +} 1.235 + 1.236 +//-------------------------------------------------------------- 1.237 + 1.238 +NS_QUERYFRAME_HEAD(nsComboboxControlFrame) 1.239 + NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame) 1.240 + NS_QUERYFRAME_ENTRY(nsIFormControlFrame) 1.241 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.242 + NS_QUERYFRAME_ENTRY(nsISelectControlFrame) 1.243 + NS_QUERYFRAME_ENTRY(nsIStatefulFrame) 1.244 +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) 1.245 + 1.246 +#ifdef ACCESSIBILITY 1.247 +a11y::AccType 1.248 +nsComboboxControlFrame::AccessibleType() 1.249 +{ 1.250 + return a11y::eHTMLComboboxType; 1.251 +} 1.252 +#endif 1.253 + 1.254 +void 1.255 +nsComboboxControlFrame::SetFocus(bool aOn, bool aRepaint) 1.256 +{ 1.257 + nsWeakFrame weakFrame(this); 1.258 + if (aOn) { 1.259 + nsListControlFrame::ComboboxFocusSet(); 1.260 + sFocused = this; 1.261 + if (mDelayedShowDropDown) { 1.262 + ShowDropDown(true); // might destroy us 1.263 + if (!weakFrame.IsAlive()) { 1.264 + return; 1.265 + } 1.266 + } 1.267 + } else { 1.268 + sFocused = nullptr; 1.269 + mDelayedShowDropDown = false; 1.270 + if (mDroppedDown) { 1.271 + mListControlFrame->ComboboxFinish(mDisplayedIndex); // might destroy us 1.272 + if (!weakFrame.IsAlive()) { 1.273 + return; 1.274 + } 1.275 + } 1.276 + // May delete |this|. 1.277 + mListControlFrame->FireOnChange(); 1.278 + } 1.279 + 1.280 + if (!weakFrame.IsAlive()) { 1.281 + return; 1.282 + } 1.283 + 1.284 + // This is needed on a temporary basis. It causes the focus 1.285 + // rect to be drawn. This is much faster than ReResolvingStyle 1.286 + // Bug 32920 1.287 + InvalidateFrame(); 1.288 +} 1.289 + 1.290 +void 1.291 +nsComboboxControlFrame::ShowPopup(bool aShowPopup) 1.292 +{ 1.293 + nsView* view = mDropdownFrame->GetView(); 1.294 + nsViewManager* viewManager = view->GetViewManager(); 1.295 + 1.296 + if (aShowPopup) { 1.297 + nsRect rect = mDropdownFrame->GetRect(); 1.298 + rect.x = rect.y = 0; 1.299 + viewManager->ResizeView(view, rect); 1.300 + viewManager->SetViewVisibility(view, nsViewVisibility_kShow); 1.301 + } else { 1.302 + viewManager->SetViewVisibility(view, nsViewVisibility_kHide); 1.303 + nsRect emptyRect(0, 0, 0, 0); 1.304 + viewManager->ResizeView(view, emptyRect); 1.305 + } 1.306 + 1.307 + // fire a popup dom event 1.308 + nsEventStatus status = nsEventStatus_eIgnore; 1.309 + WidgetMouseEvent event(true, aShowPopup ? 1.310 + NS_XUL_POPUP_SHOWING : NS_XUL_POPUP_HIDING, nullptr, 1.311 + WidgetMouseEvent::eReal); 1.312 + 1.313 + nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell(); 1.314 + if (shell) 1.315 + shell->HandleDOMEventWithTarget(mContent, &event, &status); 1.316 +} 1.317 + 1.318 +bool 1.319 +nsComboboxControlFrame::ShowList(bool aShowList) 1.320 +{ 1.321 + nsView* view = mDropdownFrame->GetView(); 1.322 + if (aShowList) { 1.323 + NS_ASSERTION(!view->HasWidget(), 1.324 + "We shouldn't have a widget before we need to display the popup"); 1.325 + 1.326 + // Create the widget for the drop-down list 1.327 + view->GetViewManager()->SetViewFloating(view, true); 1.328 + 1.329 + nsWidgetInitData widgetData; 1.330 + widgetData.mWindowType = eWindowType_popup; 1.331 + widgetData.mBorderStyle = eBorderStyle_default; 1.332 + view->CreateWidgetForPopup(&widgetData); 1.333 + } else { 1.334 + nsIWidget* widget = view->GetWidget(); 1.335 + if (widget) { 1.336 + // We must do this before ShowPopup in case it destroys us (bug 813442). 1.337 + widget->CaptureRollupEvents(this, false); 1.338 + } 1.339 + } 1.340 + 1.341 + nsWeakFrame weakFrame(this); 1.342 + ShowPopup(aShowList); // might destroy us 1.343 + if (!weakFrame.IsAlive()) { 1.344 + return false; 1.345 + } 1.346 + 1.347 + mDroppedDown = aShowList; 1.348 + nsIWidget* widget = view->GetWidget(); 1.349 + if (mDroppedDown) { 1.350 + // The listcontrol frame will call back to the nsComboboxControlFrame's 1.351 + // ListWasSelected which will stop the capture. 1.352 + mListControlFrame->AboutToDropDown(); 1.353 + mListControlFrame->CaptureMouseEvents(true); 1.354 + if (widget) { 1.355 + widget->CaptureRollupEvents(this, true); 1.356 + } 1.357 + } else { 1.358 + if (widget) { 1.359 + view->DestroyWidget(); 1.360 + } 1.361 + } 1.362 + 1.363 + return weakFrame.IsAlive(); 1.364 +} 1.365 + 1.366 +class nsResizeDropdownAtFinalPosition 1.367 + : public nsIReflowCallback, public nsRunnable 1.368 +{ 1.369 +public: 1.370 + nsResizeDropdownAtFinalPosition(nsComboboxControlFrame* aFrame) 1.371 + : mFrame(aFrame) 1.372 + { 1.373 + MOZ_COUNT_CTOR(nsResizeDropdownAtFinalPosition); 1.374 + } 1.375 + ~nsResizeDropdownAtFinalPosition() 1.376 + { 1.377 + MOZ_COUNT_DTOR(nsResizeDropdownAtFinalPosition); 1.378 + } 1.379 + 1.380 + virtual bool ReflowFinished() MOZ_OVERRIDE 1.381 + { 1.382 + Run(); 1.383 + NS_RELEASE_THIS(); 1.384 + return false; 1.385 + } 1.386 + 1.387 + virtual void ReflowCallbackCanceled() MOZ_OVERRIDE 1.388 + { 1.389 + NS_RELEASE_THIS(); 1.390 + } 1.391 + 1.392 + NS_IMETHODIMP Run() 1.393 + { 1.394 + if (mFrame.IsAlive()) { 1.395 + static_cast<nsComboboxControlFrame*>(mFrame.GetFrame())-> 1.396 + AbsolutelyPositionDropDown(); 1.397 + } 1.398 + return NS_OK; 1.399 + } 1.400 + 1.401 + nsWeakFrame mFrame; 1.402 +}; 1.403 + 1.404 +nsresult 1.405 +nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext, 1.406 + const nsHTMLReflowState& aReflowState) 1.407 +{ 1.408 + // All we want out of it later on, really, is the height of a row, so we 1.409 + // don't even need to cache mDropdownFrame's ascent or anything. If we don't 1.410 + // need to reflow it, just bail out here. 1.411 + if (!aReflowState.ShouldReflowAllKids() && 1.412 + !NS_SUBTREE_DIRTY(mDropdownFrame)) { 1.413 + return NS_OK; 1.414 + } 1.415 + 1.416 + // XXXbz this will, for small-height dropdowns, have extra space on the right 1.417 + // edge for the scrollbar we don't show... but that's the best we can do here 1.418 + // for now. 1.419 + nsSize availSize(aReflowState.AvailableWidth(), NS_UNCONSTRAINEDSIZE); 1.420 + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mDropdownFrame, 1.421 + availSize); 1.422 + 1.423 + // If the dropdown's intrinsic width is narrower than our specified width, 1.424 + // then expand it out. We want our border-box width to end up the same as 1.425 + // the dropdown's so account for both sets of mComputedBorderPadding. 1.426 + nscoord forcedWidth = aReflowState.ComputedWidth() + 1.427 + aReflowState.ComputedPhysicalBorderPadding().LeftRight() - 1.428 + kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.429 + kidReflowState.SetComputedWidth(std::max(kidReflowState.ComputedWidth(), 1.430 + forcedWidth)); 1.431 + 1.432 + // ensure we start off hidden 1.433 + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { 1.434 + nsView* view = mDropdownFrame->GetView(); 1.435 + nsViewManager* viewManager = view->GetViewManager(); 1.436 + viewManager->SetViewVisibility(view, nsViewVisibility_kHide); 1.437 + nsRect emptyRect(0, 0, 0, 0); 1.438 + viewManager->ResizeView(view, emptyRect); 1.439 + } 1.440 + 1.441 + // Allow the child to move/size/change-visibility its view if it's currently 1.442 + // dropped down 1.443 + int32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW; 1.444 + if (mDroppedDown) { 1.445 + flags = 0; 1.446 + } 1.447 + nsRect rect = mDropdownFrame->GetRect(); 1.448 + nsHTMLReflowMetrics desiredSize(aReflowState); 1.449 + nsReflowStatus ignoredStatus; 1.450 + nsresult rv = ReflowChild(mDropdownFrame, aPresContext, desiredSize, 1.451 + kidReflowState, rect.x, rect.y, flags, 1.452 + ignoredStatus); 1.453 + 1.454 + // Set the child's width and height to its desired size 1.455 + FinishReflowChild(mDropdownFrame, aPresContext, desiredSize, 1.456 + &kidReflowState, rect.x, rect.y, flags); 1.457 + return rv; 1.458 +} 1.459 + 1.460 +nsPoint 1.461 +nsComboboxControlFrame::GetCSSTransformTranslation() 1.462 +{ 1.463 + nsIFrame* frame = this; 1.464 + bool is3DTransform = false; 1.465 + gfxMatrix transform; 1.466 + while (frame) { 1.467 + nsIFrame* parent; 1.468 + gfx3DMatrix ctm = frame->GetTransformMatrix(nullptr, &parent); 1.469 + gfxMatrix matrix; 1.470 + if (ctm.Is2D(&matrix)) { 1.471 + transform = transform * matrix; 1.472 + } else { 1.473 + is3DTransform = true; 1.474 + break; 1.475 + } 1.476 + frame = parent; 1.477 + } 1.478 + nsPoint translation; 1.479 + if (!is3DTransform && !transform.HasNonTranslation()) { 1.480 + nsPresContext* pc = PresContext(); 1.481 + gfxPoint pixelTranslation = transform.GetTranslation(); 1.482 + int32_t apd = pc->AppUnitsPerDevPixel(); 1.483 + translation.x = NSFloatPixelsToAppUnits(float(pixelTranslation.x), apd); 1.484 + translation.y = NSFloatPixelsToAppUnits(float(pixelTranslation.y), apd); 1.485 + // To get the translation introduced only by transforms we subtract the 1.486 + // regular non-transform translation. 1.487 + nsRootPresContext* rootPC = pc->GetRootPresContext(); 1.488 + if (rootPC) { 1.489 + translation -= GetOffsetToCrossDoc(rootPC->PresShell()->GetRootFrame()); 1.490 + } else { 1.491 + translation.x = translation.y = 0; 1.492 + } 1.493 + } 1.494 + return translation; 1.495 +} 1.496 + 1.497 +class nsAsyncRollup : public nsRunnable 1.498 +{ 1.499 +public: 1.500 + nsAsyncRollup(nsComboboxControlFrame* aFrame) : mFrame(aFrame) {} 1.501 + NS_IMETHODIMP Run() 1.502 + { 1.503 + if (mFrame.IsAlive()) { 1.504 + static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()) 1.505 + ->RollupFromList(); 1.506 + } 1.507 + return NS_OK; 1.508 + } 1.509 + nsWeakFrame mFrame; 1.510 +}; 1.511 + 1.512 +class nsAsyncResize : public nsRunnable 1.513 +{ 1.514 +public: 1.515 + nsAsyncResize(nsComboboxControlFrame* aFrame) : mFrame(aFrame) {} 1.516 + NS_IMETHODIMP Run() 1.517 + { 1.518 + if (mFrame.IsAlive()) { 1.519 + nsComboboxControlFrame* combo = 1.520 + static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()); 1.521 + static_cast<nsListControlFrame*>(combo->mDropdownFrame)-> 1.522 + SetSuppressScrollbarUpdate(true); 1.523 + nsCOMPtr<nsIPresShell> shell = mFrame->PresContext()->PresShell(); 1.524 + shell->FrameNeedsReflow(combo->mDropdownFrame, nsIPresShell::eResize, 1.525 + NS_FRAME_IS_DIRTY); 1.526 + shell->FlushPendingNotifications(Flush_Layout); 1.527 + if (mFrame.IsAlive()) { 1.528 + combo = static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()); 1.529 + static_cast<nsListControlFrame*>(combo->mDropdownFrame)-> 1.530 + SetSuppressScrollbarUpdate(false); 1.531 + if (combo->mDelayedShowDropDown) { 1.532 + combo->ShowDropDown(true); 1.533 + } 1.534 + } 1.535 + } 1.536 + return NS_OK; 1.537 + } 1.538 + nsWeakFrame mFrame; 1.539 +}; 1.540 + 1.541 +void 1.542 +nsComboboxControlFrame::GetAvailableDropdownSpace(nscoord* aAbove, 1.543 + nscoord* aBelow, 1.544 + nsPoint* aTranslation) 1.545 +{ 1.546 + // Note: At first glance, it appears that you could simply get the absolute 1.547 + // bounding box for the dropdown list by first getting its view, then getting 1.548 + // the view's nsIWidget, then asking the nsIWidget for its AbsoluteBounds. 1.549 + // The problem with this approach, is that the dropdown lists y location can 1.550 + // change based on whether the dropdown is placed below or above the display 1.551 + // frame. The approach, taken here is to get the absolute position of the 1.552 + // display frame and use its location to determine if the dropdown will go 1.553 + // offscreen. 1.554 + 1.555 + // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms. 1.556 + // In the special case that our transform is only a 2D translation we 1.557 + // introduce this hack so that the dropdown will show up in the right place. 1.558 + *aTranslation = GetCSSTransformTranslation(); 1.559 + *aAbove = 0; 1.560 + *aBelow = 0; 1.561 + 1.562 + nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext()); 1.563 + if (mLastDropDownBelowScreenY == nscoord_MIN) { 1.564 + nsRect thisScreenRect = GetScreenRectInAppUnits(); 1.565 + mLastDropDownBelowScreenY = thisScreenRect.YMost() + aTranslation->y; 1.566 + mLastDropDownAboveScreenY = thisScreenRect.y + aTranslation->y; 1.567 + } 1.568 + 1.569 + nscoord minY; 1.570 + nsPresContext* pc = PresContext()->GetToplevelContentDocumentPresContext(); 1.571 + nsIFrame* root = pc ? pc->PresShell()->GetRootFrame() : nullptr; 1.572 + if (root) { 1.573 + minY = root->GetScreenRectInAppUnits().y; 1.574 + if (mLastDropDownBelowScreenY < minY) { 1.575 + // Don't allow the drop-down to be placed above the content area. 1.576 + return; 1.577 + } 1.578 + } else { 1.579 + minY = screen.y; 1.580 + } 1.581 + 1.582 + nscoord below = screen.YMost() - mLastDropDownBelowScreenY; 1.583 + nscoord above = mLastDropDownAboveScreenY - minY; 1.584 + 1.585 + // If the difference between the space above and below is less 1.586 + // than a row-height, then we favor the space below. 1.587 + if (above >= below) { 1.588 + nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); 1.589 + nscoord rowHeight = lcf->GetHeightOfARow(); 1.590 + if (above < below + rowHeight) { 1.591 + above -= rowHeight; 1.592 + } 1.593 + } 1.594 + 1.595 + *aBelow = below; 1.596 + *aAbove = above; 1.597 +} 1.598 + 1.599 +nsComboboxControlFrame::DropDownPositionState 1.600 +nsComboboxControlFrame::AbsolutelyPositionDropDown() 1.601 +{ 1.602 + nsPoint translation; 1.603 + nscoord above, below; 1.604 + mLastDropDownBelowScreenY = nscoord_MIN; 1.605 + GetAvailableDropdownSpace(&above, &below, &translation); 1.606 + if (above <= 0 && below <= 0) { 1.607 + if (IsDroppedDown()) { 1.608 + // Hide the view immediately to minimize flicker. 1.609 + nsView* view = mDropdownFrame->GetView(); 1.610 + view->GetViewManager()->SetViewVisibility(view, nsViewVisibility_kHide); 1.611 + NS_DispatchToCurrentThread(new nsAsyncRollup(this)); 1.612 + } 1.613 + return eDropDownPositionSuppressed; 1.614 + } 1.615 + 1.616 + nsSize dropdownSize = mDropdownFrame->GetSize(); 1.617 + nscoord height = std::max(above, below); 1.618 + nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); 1.619 + if (height < dropdownSize.height) { 1.620 + if (lcf->GetNumDisplayRows() > 1) { 1.621 + // The drop-down doesn't fit and currently shows more than 1 row - 1.622 + // schedule a resize to show fewer rows. 1.623 + NS_DispatchToCurrentThread(new nsAsyncResize(this)); 1.624 + return eDropDownPositionPendingResize; 1.625 + } 1.626 + } else if (height > (dropdownSize.height + lcf->GetHeightOfARow() * 1.5) && 1.627 + lcf->GetDropdownCanGrow()) { 1.628 + // The drop-down fits but there is room for at least 1.5 more rows - 1.629 + // schedule a resize to show more rows if it has more rows to show. 1.630 + // (1.5 rows for good measure to avoid any rounding issues that would 1.631 + // lead to a loop of reflow requests) 1.632 + NS_DispatchToCurrentThread(new nsAsyncResize(this)); 1.633 + return eDropDownPositionPendingResize; 1.634 + } 1.635 + 1.636 + // Position the drop-down below if there is room, otherwise place it above 1.637 + // if there is room. If there is no room for it on either side then place 1.638 + // it below (to avoid overlapping UI like the URL bar). 1.639 + bool b = dropdownSize.height <= below || dropdownSize.height > above; 1.640 + nsPoint dropdownPosition(0, b ? GetRect().height : -dropdownSize.height); 1.641 + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.642 + // Align the right edge of the drop-down with the right edge of the control. 1.643 + dropdownPosition.x = GetRect().width - dropdownSize.width; 1.644 + } 1.645 + 1.646 + // Don't position the view unless the position changed since it might cause 1.647 + // a call to NotifyGeometryChange() and an infinite loop here. 1.648 + const nsPoint currentPos = mDropdownFrame->GetPosition(); 1.649 + const nsPoint newPos = dropdownPosition + translation; 1.650 + if (currentPos != newPos) { 1.651 + mDropdownFrame->SetPosition(newPos); 1.652 + nsContainerFrame::PositionFrameView(mDropdownFrame); 1.653 + } 1.654 + return eDropDownPositionFinal; 1.655 +} 1.656 + 1.657 +void 1.658 +nsComboboxControlFrame::NotifyGeometryChange() 1.659 +{ 1.660 + // We don't need to resize if we're not dropped down since ShowDropDown 1.661 + // does that, or if we're dirty then the reflow callback does it, 1.662 + // or if we have a delayed ShowDropDown pending. 1.663 + if (IsDroppedDown() && 1.664 + !(GetStateBits() & NS_FRAME_IS_DIRTY) && 1.665 + !mDelayedShowDropDown) { 1.666 + // Async because we're likely in a middle of a scroll here so 1.667 + // frame/view positions are in flux. 1.668 + nsRefPtr<nsResizeDropdownAtFinalPosition> resize = 1.669 + new nsResizeDropdownAtFinalPosition(this); 1.670 + NS_DispatchToCurrentThread(resize); 1.671 + } 1.672 +} 1.673 + 1.674 +//---------------------------------------------------------- 1.675 +// 1.676 +//---------------------------------------------------------- 1.677 +#ifdef DO_REFLOW_DEBUG 1.678 +static int myCounter = 0; 1.679 + 1.680 +static void printSize(char * aDesc, nscoord aSize) 1.681 +{ 1.682 + printf(" %s: ", aDesc); 1.683 + if (aSize == NS_UNCONSTRAINEDSIZE) { 1.684 + printf("UC"); 1.685 + } else { 1.686 + printf("%d", PX(aSize)); 1.687 + } 1.688 +} 1.689 +#endif 1.690 + 1.691 +//------------------------------------------------------------------- 1.692 +//-- Main Reflow for the Combobox 1.693 +//------------------------------------------------------------------- 1.694 + 1.695 +nscoord 1.696 +nsComboboxControlFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext, 1.697 + nsLayoutUtils::IntrinsicWidthType aType) 1.698 +{ 1.699 + // get the scrollbar width, we'll use this later 1.700 + nscoord scrollbarWidth = 0; 1.701 + nsPresContext* presContext = PresContext(); 1.702 + if (mListControlFrame) { 1.703 + nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame); 1.704 + NS_ASSERTION(scrollable, "List must be a scrollable frame"); 1.705 + scrollbarWidth = scrollable->GetNondisappearingScrollbarWidth( 1.706 + presContext, aRenderingContext); 1.707 + } 1.708 + 1.709 + nscoord displayWidth = 0; 1.710 + if (MOZ_LIKELY(mDisplayFrame)) { 1.711 + displayWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.712 + mDisplayFrame, 1.713 + aType); 1.714 + } 1.715 + 1.716 + if (mDropdownFrame) { 1.717 + nscoord dropdownContentWidth; 1.718 + bool isUsingOverlayScrollbars = 1.719 + LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0; 1.720 + if (aType == nsLayoutUtils::MIN_WIDTH) { 1.721 + dropdownContentWidth = mDropdownFrame->GetMinWidth(aRenderingContext); 1.722 + if (isUsingOverlayScrollbars) { 1.723 + dropdownContentWidth += scrollbarWidth; 1.724 + } 1.725 + } else { 1.726 + NS_ASSERTION(aType == nsLayoutUtils::PREF_WIDTH, "Unexpected type"); 1.727 + dropdownContentWidth = mDropdownFrame->GetPrefWidth(aRenderingContext); 1.728 + if (isUsingOverlayScrollbars) { 1.729 + dropdownContentWidth += scrollbarWidth; 1.730 + } 1.731 + } 1.732 + dropdownContentWidth = NSCoordSaturatingSubtract(dropdownContentWidth, 1.733 + scrollbarWidth, 1.734 + nscoord_MAX); 1.735 + 1.736 + displayWidth = std::max(dropdownContentWidth, displayWidth); 1.737 + } 1.738 + 1.739 + // add room for the dropmarker button if there is one 1.740 + if (!IsThemed() || presContext->GetTheme()->ThemeNeedsComboboxDropmarker()) 1.741 + displayWidth += scrollbarWidth; 1.742 + 1.743 + return displayWidth; 1.744 + 1.745 +} 1.746 + 1.747 +nscoord 1.748 +nsComboboxControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.749 +{ 1.750 + nscoord minWidth; 1.751 + DISPLAY_MIN_WIDTH(this, minWidth); 1.752 + minWidth = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::MIN_WIDTH); 1.753 + return minWidth; 1.754 +} 1.755 + 1.756 +nscoord 1.757 +nsComboboxControlFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.758 +{ 1.759 + nscoord prefWidth; 1.760 + DISPLAY_PREF_WIDTH(this, prefWidth); 1.761 + prefWidth = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::PREF_WIDTH); 1.762 + return prefWidth; 1.763 +} 1.764 + 1.765 +nsresult 1.766 +nsComboboxControlFrame::Reflow(nsPresContext* aPresContext, 1.767 + nsHTMLReflowMetrics& aDesiredSize, 1.768 + const nsHTMLReflowState& aReflowState, 1.769 + nsReflowStatus& aStatus) 1.770 +{ 1.771 + // Constraints we try to satisfy: 1.772 + 1.773 + // 1) Default width of button is the vertical scrollbar size 1.774 + // 2) If the width of button is bigger than our width, set width of 1.775 + // button to 0. 1.776 + // 3) Default height of button is height of display area 1.777 + // 4) Width of display area is whatever is left over from our width after 1.778 + // allocating width for the button. 1.779 + // 5) Height of display area is GetHeightOfARow() on the 1.780 + // mListControlFrame. 1.781 + 1.782 + if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) { 1.783 + NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!"); 1.784 + return NS_ERROR_UNEXPECTED; 1.785 + } 1.786 + 1.787 + // Make sure the displayed text is the same as the selected option, bug 297389. 1.788 + int32_t selectedIndex; 1.789 + nsAutoString selectedOptionText; 1.790 + if (!mDroppedDown) { 1.791 + selectedIndex = mListControlFrame->GetSelectedIndex(); 1.792 + } 1.793 + else { 1.794 + // In dropped down mode the "selected index" is the hovered menu item, 1.795 + // we want the last selected item which is |mDisplayedIndex| in this case. 1.796 + selectedIndex = mDisplayedIndex; 1.797 + } 1.798 + if (selectedIndex != -1) { 1.799 + mListControlFrame->GetOptionText(selectedIndex, selectedOptionText); 1.800 + } 1.801 + if (mDisplayedOptionText != selectedOptionText) { 1.802 + RedisplayText(selectedIndex); 1.803 + } 1.804 + 1.805 + // First reflow our dropdown so that we know how tall we should be. 1.806 + ReflowDropdown(aPresContext, aReflowState); 1.807 + nsRefPtr<nsResizeDropdownAtFinalPosition> resize = 1.808 + new nsResizeDropdownAtFinalPosition(this); 1.809 + if (NS_SUCCEEDED(aPresContext->PresShell()->PostReflowCallback(resize))) { 1.810 + // The reflow callback queue doesn't AddRef so we keep it alive until 1.811 + // it's released in its ReflowFinished / ReflowCallbackCanceled. 1.812 + unused << resize.forget(); 1.813 + } 1.814 + 1.815 + // Get the width of the vertical scrollbar. That will be the width of the 1.816 + // dropdown button. 1.817 + nscoord buttonWidth; 1.818 + const nsStyleDisplay *disp = StyleDisplay(); 1.819 + if (IsThemed(disp) && !aPresContext->GetTheme()->ThemeNeedsComboboxDropmarker()) { 1.820 + buttonWidth = 0; 1.821 + } 1.822 + else { 1.823 + nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame); 1.824 + NS_ASSERTION(scrollable, "List must be a scrollable frame"); 1.825 + buttonWidth = scrollable->GetNondisappearingScrollbarWidth( 1.826 + PresContext(), aReflowState.rendContext); 1.827 + if (buttonWidth > aReflowState.ComputedWidth()) { 1.828 + buttonWidth = 0; 1.829 + } 1.830 + } 1.831 + 1.832 + mDisplayWidth = aReflowState.ComputedWidth() - buttonWidth; 1.833 + 1.834 + nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, 1.835 + aStatus); 1.836 + NS_ENSURE_SUCCESS(rv, rv); 1.837 + 1.838 + // The button should occupy the same space as a scrollbar 1.839 + nsRect buttonRect = mButtonFrame->GetRect(); 1.840 + 1.841 + if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.842 + buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().left - 1.843 + aReflowState.ComputedPhysicalPadding().left; 1.844 + } 1.845 + else { 1.846 + buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().LeftRight() + 1.847 + mDisplayWidth - 1.848 + (aReflowState.ComputedPhysicalBorderPadding().right - 1.849 + aReflowState.ComputedPhysicalPadding().right); 1.850 + } 1.851 + buttonRect.width = buttonWidth; 1.852 + 1.853 + buttonRect.y = this->GetUsedBorder().top; 1.854 + buttonRect.height = mDisplayFrame->GetRect().height + 1.855 + this->GetUsedPadding().TopBottom(); 1.856 + 1.857 + mButtonFrame->SetRect(buttonRect); 1.858 + 1.859 + if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && 1.860 + !NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { 1.861 + // This frame didn't fit inside a fragmentation container. Splitting 1.862 + // a nsComboboxControlFrame makes no sense, so we override the status here. 1.863 + aStatus = NS_FRAME_COMPLETE; 1.864 + } 1.865 + return rv; 1.866 +} 1.867 + 1.868 +//-------------------------------------------------------------- 1.869 + 1.870 +nsIAtom* 1.871 +nsComboboxControlFrame::GetType() const 1.872 +{ 1.873 + return nsGkAtoms::comboboxControlFrame; 1.874 +} 1.875 + 1.876 +#ifdef DEBUG_FRAME_DUMP 1.877 +nsresult 1.878 +nsComboboxControlFrame::GetFrameName(nsAString& aResult) const 1.879 +{ 1.880 + return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult); 1.881 +} 1.882 +#endif 1.883 + 1.884 + 1.885 +//---------------------------------------------------------------------- 1.886 +// nsIComboboxControlFrame 1.887 +//---------------------------------------------------------------------- 1.888 +void 1.889 +nsComboboxControlFrame::ShowDropDown(bool aDoDropDown) 1.890 +{ 1.891 + mDelayedShowDropDown = false; 1.892 + EventStates eventStates = mContent->AsElement()->State(); 1.893 + if (aDoDropDown && eventStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.894 + return; 1.895 + } 1.896 + 1.897 + if (!mDroppedDown && aDoDropDown) { 1.898 + nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1.899 + if (!fm || fm->GetFocusedContent() == GetContent()) { 1.900 + DropDownPositionState state = AbsolutelyPositionDropDown(); 1.901 + if (state == eDropDownPositionFinal) { 1.902 + ShowList(aDoDropDown); // might destroy us 1.903 + } else if (state == eDropDownPositionPendingResize) { 1.904 + // Delay until after the resize reflow, see nsAsyncResize. 1.905 + mDelayedShowDropDown = true; 1.906 + } 1.907 + } else { 1.908 + // Delay until we get focus, see SetFocus(). 1.909 + mDelayedShowDropDown = true; 1.910 + } 1.911 + } else if (mDroppedDown && !aDoDropDown) { 1.912 + ShowList(aDoDropDown); // might destroy us 1.913 + } 1.914 +} 1.915 + 1.916 +void 1.917 +nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame) 1.918 +{ 1.919 + mDropdownFrame = aDropDownFrame; 1.920 + mListControlFrame = do_QueryFrame(mDropdownFrame); 1.921 +} 1.922 + 1.923 +nsIFrame* 1.924 +nsComboboxControlFrame::GetDropDown() 1.925 +{ 1.926 + return mDropdownFrame; 1.927 +} 1.928 + 1.929 +/////////////////////////////////////////////////////////////// 1.930 + 1.931 +NS_IMETHODIMP 1.932 +nsComboboxControlFrame::RedisplaySelectedText() 1.933 +{ 1.934 + nsAutoScriptBlocker scriptBlocker; 1.935 + return RedisplayText(mListControlFrame->GetSelectedIndex()); 1.936 +} 1.937 + 1.938 +nsresult 1.939 +nsComboboxControlFrame::RedisplayText(int32_t aIndex) 1.940 +{ 1.941 + // Get the text to display 1.942 + if (aIndex != -1) { 1.943 + mListControlFrame->GetOptionText(aIndex, mDisplayedOptionText); 1.944 + } else { 1.945 + mDisplayedOptionText.Truncate(); 1.946 + } 1.947 + mDisplayedIndex = aIndex; 1.948 + 1.949 + REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n", 1.950 + NS_LossyConvertUTF16toASCII(mDisplayedOptionText).get()); 1.951 + 1.952 + // Send reflow command because the new text maybe larger 1.953 + nsresult rv = NS_OK; 1.954 + if (mDisplayContent) { 1.955 + // Don't call ActuallyDisplayText(true) directly here since that 1.956 + // could cause recursive frame construction. See bug 283117 and the comment in 1.957 + // HandleRedisplayTextEvent() below. 1.958 + 1.959 + // Revoke outstanding events to avoid out-of-order events which could mean 1.960 + // displaying the wrong text. 1.961 + mRedisplayTextEvent.Revoke(); 1.962 + 1.963 + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), 1.964 + "If we happen to run our redisplay event now, we might kill " 1.965 + "ourselves!"); 1.966 + 1.967 + nsRefPtr<RedisplayTextEvent> event = new RedisplayTextEvent(this); 1.968 + mRedisplayTextEvent = event; 1.969 + if (!nsContentUtils::AddScriptRunner(event)) 1.970 + mRedisplayTextEvent.Forget(); 1.971 + } 1.972 + return rv; 1.973 +} 1.974 + 1.975 +void 1.976 +nsComboboxControlFrame::HandleRedisplayTextEvent() 1.977 +{ 1.978 + // First, make sure that the content model is up to date and we've 1.979 + // constructed the frames for all our content in the right places. 1.980 + // Otherwise they'll end up under the wrong insertion frame when we 1.981 + // ActuallyDisplayText, since that flushes out the content sink by 1.982 + // calling SetText on a DOM node with aNotify set to true. See bug 1.983 + // 289730. 1.984 + nsWeakFrame weakThis(this); 1.985 + PresContext()->Document()-> 1.986 + FlushPendingNotifications(Flush_ContentAndNotify); 1.987 + if (!weakThis.IsAlive()) 1.988 + return; 1.989 + 1.990 + // Redirect frame insertions during this method (see GetContentInsertionFrame()) 1.991 + // so that any reframing that the frame constructor forces upon us is inserted 1.992 + // into the correct parent (mDisplayFrame). See bug 282607. 1.993 + NS_PRECONDITION(!mInRedisplayText, "Nested RedisplayText"); 1.994 + mInRedisplayText = true; 1.995 + mRedisplayTextEvent.Forget(); 1.996 + 1.997 + ActuallyDisplayText(true); 1.998 + // XXXbz This should perhaps be eResize. Check. 1.999 + PresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame, 1.1000 + nsIPresShell::eStyleChange, 1.1001 + NS_FRAME_IS_DIRTY); 1.1002 + 1.1003 + mInRedisplayText = false; 1.1004 +} 1.1005 + 1.1006 +void 1.1007 +nsComboboxControlFrame::ActuallyDisplayText(bool aNotify) 1.1008 +{ 1.1009 + if (mDisplayedOptionText.IsEmpty()) { 1.1010 + // Have to use a non-breaking space for line-height calculations 1.1011 + // to be right 1.1012 + static const char16_t space = 0xA0; 1.1013 + mDisplayContent->SetText(&space, 1, aNotify); 1.1014 + } else { 1.1015 + mDisplayContent->SetText(mDisplayedOptionText, aNotify); 1.1016 + } 1.1017 +} 1.1018 + 1.1019 +int32_t 1.1020 +nsComboboxControlFrame::GetIndexOfDisplayArea() 1.1021 +{ 1.1022 + return mDisplayedIndex; 1.1023 +} 1.1024 + 1.1025 +//---------------------------------------------------------------------- 1.1026 +// nsISelectControlFrame 1.1027 +//---------------------------------------------------------------------- 1.1028 +NS_IMETHODIMP 1.1029 +nsComboboxControlFrame::DoneAddingChildren(bool aIsDone) 1.1030 +{ 1.1031 + nsISelectControlFrame* listFrame = do_QueryFrame(mDropdownFrame); 1.1032 + if (!listFrame) 1.1033 + return NS_ERROR_FAILURE; 1.1034 + 1.1035 + return listFrame->DoneAddingChildren(aIsDone); 1.1036 +} 1.1037 + 1.1038 +NS_IMETHODIMP 1.1039 +nsComboboxControlFrame::AddOption(int32_t aIndex) 1.1040 +{ 1.1041 + if (aIndex <= mDisplayedIndex) { 1.1042 + ++mDisplayedIndex; 1.1043 + } 1.1044 + 1.1045 + nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); 1.1046 + return lcf->AddOption(aIndex); 1.1047 +} 1.1048 + 1.1049 + 1.1050 +NS_IMETHODIMP 1.1051 +nsComboboxControlFrame::RemoveOption(int32_t aIndex) 1.1052 +{ 1.1053 + nsWeakFrame weakThis(this); 1.1054 + if (mListControlFrame->GetNumberOfOptions() > 0) { 1.1055 + if (aIndex < mDisplayedIndex) { 1.1056 + --mDisplayedIndex; 1.1057 + } else if (aIndex == mDisplayedIndex) { 1.1058 + mDisplayedIndex = 0; // IE6 compat 1.1059 + RedisplayText(mDisplayedIndex); 1.1060 + } 1.1061 + } 1.1062 + else { 1.1063 + // If we removed the last option, we need to blank things out 1.1064 + RedisplayText(-1); 1.1065 + } 1.1066 + 1.1067 + if (!weakThis.IsAlive()) 1.1068 + return NS_OK; 1.1069 + 1.1070 + nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); 1.1071 + return lcf->RemoveOption(aIndex); 1.1072 +} 1.1073 + 1.1074 +NS_IMETHODIMP 1.1075 +nsComboboxControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) 1.1076 +{ 1.1077 + nsAutoScriptBlocker scriptBlocker; 1.1078 + RedisplayText(aNewIndex); 1.1079 + NS_ASSERTION(mDropdownFrame, "No dropdown frame!"); 1.1080 + 1.1081 + nsISelectControlFrame* listFrame = do_QueryFrame(mDropdownFrame); 1.1082 + NS_ASSERTION(listFrame, "No list frame!"); 1.1083 + 1.1084 + return listFrame->OnSetSelectedIndex(aOldIndex, aNewIndex); 1.1085 +} 1.1086 + 1.1087 +// End nsISelectControlFrame 1.1088 +//---------------------------------------------------------------------- 1.1089 + 1.1090 +nsresult 1.1091 +nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext, 1.1092 + WidgetGUIEvent* aEvent, 1.1093 + nsEventStatus* aEventStatus) 1.1094 +{ 1.1095 + NS_ENSURE_ARG_POINTER(aEventStatus); 1.1096 + 1.1097 + if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { 1.1098 + return NS_OK; 1.1099 + } 1.1100 + 1.1101 + EventStates eventStates = mContent->AsElement()->State(); 1.1102 + if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) { 1.1103 + return NS_OK; 1.1104 + } 1.1105 + 1.1106 + // If we have style that affects how we are selected, feed event down to 1.1107 + // nsFrame::HandleEvent so that selection takes place when appropriate. 1.1108 + const nsStyleUserInterface* uiStyle = StyleUserInterface(); 1.1109 + if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE || uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED) 1.1110 + return nsBlockFrame::HandleEvent(aPresContext, aEvent, aEventStatus); 1.1111 + 1.1112 + return NS_OK; 1.1113 +} 1.1114 + 1.1115 + 1.1116 +nsresult 1.1117 +nsComboboxControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue) 1.1118 +{ 1.1119 + nsIFormControlFrame* fcFrame = do_QueryFrame(mDropdownFrame); 1.1120 + if (!fcFrame) { 1.1121 + return NS_NOINTERFACE; 1.1122 + } 1.1123 + 1.1124 + return fcFrame->SetFormProperty(aName, aValue); 1.1125 +} 1.1126 + 1.1127 +nsIFrame* 1.1128 +nsComboboxControlFrame::GetContentInsertionFrame() { 1.1129 + return mInRedisplayText ? mDisplayFrame : mDropdownFrame->GetContentInsertionFrame(); 1.1130 +} 1.1131 + 1.1132 +nsresult 1.1133 +nsComboboxControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.1134 +{ 1.1135 + // The frames used to display the combo box and the button used to popup the dropdown list 1.1136 + // are created through anonymous content. The dropdown list is not created through anonymous 1.1137 + // content because its frame is initialized specifically for the drop-down case and it is placed 1.1138 + // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the 1.1139 + // layout of the display and button. 1.1140 + // 1.1141 + // Note: The value attribute of the display content is set when an item is selected in the dropdown list. 1.1142 + // If the content specified below does not honor the value attribute than nothing will be displayed. 1.1143 + 1.1144 + // For now the content that is created corresponds to two input buttons. It would be better to create the 1.1145 + // tag as something other than input, but then there isn't any way to create a button frame since it 1.1146 + // isn't possible to set the display type in CSS2 to create a button frame. 1.1147 + 1.1148 + // create content used for display 1.1149 + //nsIAtom* tag = NS_NewAtom("mozcombodisplay"); 1.1150 + 1.1151 + // Add a child text content node for the label 1.1152 + 1.1153 + nsNodeInfoManager *nimgr = mContent->NodeInfo()->NodeInfoManager(); 1.1154 + 1.1155 + mDisplayContent = new nsTextNode(nimgr); 1.1156 + 1.1157 + // set the value of the text node 1.1158 + mDisplayedIndex = mListControlFrame->GetSelectedIndex(); 1.1159 + if (mDisplayedIndex != -1) { 1.1160 + mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionText); 1.1161 + } 1.1162 + ActuallyDisplayText(false); 1.1163 + 1.1164 + if (!aElements.AppendElement(mDisplayContent)) 1.1165 + return NS_ERROR_OUT_OF_MEMORY; 1.1166 + 1.1167 + mButtonContent = mContent->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button); 1.1168 + if (!mButtonContent) 1.1169 + return NS_ERROR_OUT_OF_MEMORY; 1.1170 + 1.1171 + // make someone to listen to the button. If its pressed by someone like Accessibility 1.1172 + // then open or close the combo box. 1.1173 + mButtonListener = new nsComboButtonListener(this); 1.1174 + mButtonContent->AddEventListener(NS_LITERAL_STRING("click"), mButtonListener, 1.1175 + false, false); 1.1176 + 1.1177 + mButtonContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type, 1.1178 + NS_LITERAL_STRING("button"), false); 1.1179 + // Set tabindex="-1" so that the button is not tabbable 1.1180 + mButtonContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, 1.1181 + NS_LITERAL_STRING("-1"), false); 1.1182 + 1.1183 + if (!aElements.AppendElement(mButtonContent)) 1.1184 + return NS_ERROR_OUT_OF_MEMORY; 1.1185 + 1.1186 + return NS_OK; 1.1187 +} 1.1188 + 1.1189 +void 1.1190 +nsComboboxControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.1191 + uint32_t aFilter) 1.1192 +{ 1.1193 + aElements.MaybeAppendElement(mDisplayContent); 1.1194 + aElements.MaybeAppendElement(mButtonContent); 1.1195 +} 1.1196 + 1.1197 +// XXXbz this is a for-now hack. Now that display:inline-block works, 1.1198 +// need to revisit this. 1.1199 +class nsComboboxDisplayFrame : public nsBlockFrame { 1.1200 +public: 1.1201 + NS_DECL_FRAMEARENA_HELPERS 1.1202 + 1.1203 + nsComboboxDisplayFrame (nsStyleContext* aContext, 1.1204 + nsComboboxControlFrame* aComboBox) 1.1205 + : nsBlockFrame(aContext), 1.1206 + mComboBox(aComboBox) 1.1207 + {} 1.1208 + 1.1209 + // Need this so that line layout knows that this block's width 1.1210 + // depends on the available width. 1.1211 + virtual nsIAtom* GetType() const MOZ_OVERRIDE; 1.1212 + 1.1213 + virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE 1.1214 + { 1.1215 + return nsBlockFrame::IsFrameOfType(aFlags & 1.1216 + ~(nsIFrame::eReplacedContainsBlock)); 1.1217 + } 1.1218 + 1.1219 + virtual nsresult Reflow(nsPresContext* aPresContext, 1.1220 + nsHTMLReflowMetrics& aDesiredSize, 1.1221 + const nsHTMLReflowState& aReflowState, 1.1222 + nsReflowStatus& aStatus) MOZ_OVERRIDE; 1.1223 + 1.1224 + virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1225 + const nsRect& aDirtyRect, 1.1226 + const nsDisplayListSet& aLists) MOZ_OVERRIDE; 1.1227 + 1.1228 +protected: 1.1229 + nsComboboxControlFrame* mComboBox; 1.1230 +}; 1.1231 + 1.1232 +NS_IMPL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame) 1.1233 + 1.1234 +nsIAtom* 1.1235 +nsComboboxDisplayFrame::GetType() const 1.1236 +{ 1.1237 + return nsGkAtoms::comboboxDisplayFrame; 1.1238 +} 1.1239 + 1.1240 +nsresult 1.1241 +nsComboboxDisplayFrame::Reflow(nsPresContext* aPresContext, 1.1242 + nsHTMLReflowMetrics& aDesiredSize, 1.1243 + const nsHTMLReflowState& aReflowState, 1.1244 + nsReflowStatus& aStatus) 1.1245 +{ 1.1246 + nsHTMLReflowState state(aReflowState); 1.1247 + if (state.ComputedHeight() == NS_INTRINSICSIZE) { 1.1248 + // Note that the only way we can have a computed height here is if the 1.1249 + // combobox had a specified height. If it didn't, size based on what our 1.1250 + // rows look like, for lack of anything better. 1.1251 + state.SetComputedHeight(mComboBox->mListControlFrame->GetHeightOfARow()); 1.1252 + } 1.1253 + nscoord computedWidth = mComboBox->mDisplayWidth - 1.1254 + state.ComputedPhysicalBorderPadding().LeftRight(); 1.1255 + if (computedWidth < 0) { 1.1256 + computedWidth = 0; 1.1257 + } 1.1258 + state.SetComputedWidth(computedWidth); 1.1259 + 1.1260 + return nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); 1.1261 +} 1.1262 + 1.1263 +void 1.1264 +nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1265 + const nsRect& aDirtyRect, 1.1266 + const nsDisplayListSet& aLists) 1.1267 +{ 1.1268 + nsDisplayListCollection set; 1.1269 + nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, set); 1.1270 + 1.1271 + // remove background items if parent frame is themed 1.1272 + if (mComboBox->IsThemed()) { 1.1273 + set.BorderBackground()->DeleteAll(); 1.1274 + } 1.1275 + 1.1276 + set.MoveTo(aLists); 1.1277 +} 1.1278 + 1.1279 +nsIFrame* 1.1280 +nsComboboxControlFrame::CreateFrameFor(nsIContent* aContent) 1.1281 +{ 1.1282 + NS_PRECONDITION(nullptr != aContent, "null ptr"); 1.1283 + 1.1284 + NS_ASSERTION(mDisplayContent, "mDisplayContent can't be null!"); 1.1285 + 1.1286 + if (mDisplayContent != aContent) { 1.1287 + // We only handle the frames for mDisplayContent here 1.1288 + return nullptr; 1.1289 + } 1.1290 + 1.1291 + // Get PresShell 1.1292 + nsIPresShell *shell = PresContext()->PresShell(); 1.1293 + nsStyleSet *styleSet = shell->StyleSet(); 1.1294 + 1.1295 + // create the style contexts for the anonymous block frame and text frame 1.1296 + nsRefPtr<nsStyleContext> styleContext; 1.1297 + styleContext = styleSet-> 1.1298 + ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozDisplayComboboxControlFrame, 1.1299 + mStyleContext); 1.1300 + 1.1301 + nsRefPtr<nsStyleContext> textStyleContext; 1.1302 + textStyleContext = styleSet->ResolveStyleForNonElement(mStyleContext); 1.1303 + 1.1304 + // Start by creating our anonymous block frame 1.1305 + mDisplayFrame = new (shell) nsComboboxDisplayFrame(styleContext, this); 1.1306 + mDisplayFrame->Init(mContent, this, nullptr); 1.1307 + 1.1308 + // Create a text frame and put it inside the block frame 1.1309 + nsIFrame* textFrame = NS_NewTextFrame(shell, textStyleContext); 1.1310 + 1.1311 + // initialize the text frame 1.1312 + textFrame->Init(aContent, mDisplayFrame, nullptr); 1.1313 + mDisplayContent->SetPrimaryFrame(textFrame); 1.1314 + 1.1315 + nsFrameList textList(textFrame, textFrame); 1.1316 + mDisplayFrame->SetInitialChildList(kPrincipalList, textList); 1.1317 + return mDisplayFrame; 1.1318 +} 1.1319 + 1.1320 +void 1.1321 +nsComboboxControlFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.1322 +{ 1.1323 + // Revoke any pending RedisplayTextEvent 1.1324 + mRedisplayTextEvent.Revoke(); 1.1325 + 1.1326 + nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); 1.1327 + 1.1328 + if (mDroppedDown) { 1.1329 + MOZ_ASSERT(mDropdownFrame, "mDroppedDown without frame"); 1.1330 + nsView* view = mDropdownFrame->GetView(); 1.1331 + MOZ_ASSERT(view); 1.1332 + nsIWidget* widget = view->GetWidget(); 1.1333 + if (widget) { 1.1334 + widget->CaptureRollupEvents(this, false); 1.1335 + } 1.1336 + } 1.1337 + 1.1338 + // Cleanup frames in popup child list 1.1339 + mPopupFrames.DestroyFramesFrom(aDestructRoot); 1.1340 + nsContentUtils::DestroyAnonymousContent(&mDisplayContent); 1.1341 + nsContentUtils::DestroyAnonymousContent(&mButtonContent); 1.1342 + nsBlockFrame::DestroyFrom(aDestructRoot); 1.1343 +} 1.1344 + 1.1345 +const nsFrameList& 1.1346 +nsComboboxControlFrame::GetChildList(ChildListID aListID) const 1.1347 +{ 1.1348 + if (kSelectPopupList == aListID) { 1.1349 + return mPopupFrames; 1.1350 + } 1.1351 + return nsBlockFrame::GetChildList(aListID); 1.1352 +} 1.1353 + 1.1354 +void 1.1355 +nsComboboxControlFrame::GetChildLists(nsTArray<ChildList>* aLists) const 1.1356 +{ 1.1357 + nsBlockFrame::GetChildLists(aLists); 1.1358 + mPopupFrames.AppendIfNonempty(aLists, kSelectPopupList); 1.1359 +} 1.1360 + 1.1361 +nsresult 1.1362 +nsComboboxControlFrame::SetInitialChildList(ChildListID aListID, 1.1363 + nsFrameList& aChildList) 1.1364 +{ 1.1365 + nsresult rv = NS_OK; 1.1366 + if (kSelectPopupList == aListID) { 1.1367 + mPopupFrames.SetFrames(aChildList); 1.1368 + } else { 1.1369 + for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) { 1.1370 + nsCOMPtr<nsIFormControl> formControl = 1.1371 + do_QueryInterface(e.get()->GetContent()); 1.1372 + if (formControl && formControl->GetType() == NS_FORM_BUTTON_BUTTON) { 1.1373 + mButtonFrame = e.get(); 1.1374 + break; 1.1375 + } 1.1376 + } 1.1377 + NS_ASSERTION(mButtonFrame, "missing button frame in initial child list"); 1.1378 + rv = nsBlockFrame::SetInitialChildList(aListID, aChildList); 1.1379 + } 1.1380 + return rv; 1.1381 +} 1.1382 + 1.1383 +//---------------------------------------------------------------------- 1.1384 + //nsIRollupListener 1.1385 +//---------------------------------------------------------------------- 1.1386 +bool 1.1387 +nsComboboxControlFrame::Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) 1.1388 +{ 1.1389 + if (!mDroppedDown) 1.1390 + return false; 1.1391 + 1.1392 + nsWeakFrame weakFrame(this); 1.1393 + mListControlFrame->AboutToRollup(); // might destroy us 1.1394 + if (!weakFrame.IsAlive()) 1.1395 + return true; 1.1396 + ShowDropDown(false); // might destroy us 1.1397 + if (weakFrame.IsAlive()) { 1.1398 + mListControlFrame->CaptureMouseEvents(false); 1.1399 + } 1.1400 + 1.1401 + return true; 1.1402 +} 1.1403 + 1.1404 +nsIWidget* 1.1405 +nsComboboxControlFrame::GetRollupWidget() 1.1406 +{ 1.1407 + nsView* view = mDropdownFrame->GetView(); 1.1408 + MOZ_ASSERT(view); 1.1409 + return view->GetWidget(); 1.1410 +} 1.1411 + 1.1412 +void 1.1413 +nsComboboxControlFrame::RollupFromList() 1.1414 +{ 1.1415 + if (ShowList(false)) 1.1416 + mListControlFrame->CaptureMouseEvents(false); 1.1417 +} 1.1418 + 1.1419 +int32_t 1.1420 +nsComboboxControlFrame::UpdateRecentIndex(int32_t aIndex) 1.1421 +{ 1.1422 + int32_t index = mRecentSelectedIndex; 1.1423 + if (mRecentSelectedIndex == NS_SKIP_NOTIFY_INDEX || aIndex == NS_SKIP_NOTIFY_INDEX) 1.1424 + mRecentSelectedIndex = aIndex; 1.1425 + return index; 1.1426 +} 1.1427 + 1.1428 +class nsDisplayComboboxFocus : public nsDisplayItem { 1.1429 +public: 1.1430 + nsDisplayComboboxFocus(nsDisplayListBuilder* aBuilder, 1.1431 + nsComboboxControlFrame* aFrame) 1.1432 + : nsDisplayItem(aBuilder, aFrame) { 1.1433 + MOZ_COUNT_CTOR(nsDisplayComboboxFocus); 1.1434 + } 1.1435 +#ifdef NS_BUILD_REFCNT_LOGGING 1.1436 + virtual ~nsDisplayComboboxFocus() { 1.1437 + MOZ_COUNT_DTOR(nsDisplayComboboxFocus); 1.1438 + } 1.1439 +#endif 1.1440 + 1.1441 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.1442 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.1443 + NS_DISPLAY_DECL_NAME("ComboboxFocus", TYPE_COMBOBOX_FOCUS) 1.1444 +}; 1.1445 + 1.1446 +void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder* aBuilder, 1.1447 + nsRenderingContext* aCtx) 1.1448 +{ 1.1449 + static_cast<nsComboboxControlFrame*>(mFrame) 1.1450 + ->PaintFocus(*aCtx, ToReferenceFrame()); 1.1451 +} 1.1452 + 1.1453 +void 1.1454 +nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1455 + const nsRect& aDirtyRect, 1.1456 + const nsDisplayListSet& aLists) 1.1457 +{ 1.1458 +#ifdef NOISY 1.1459 + printf("%p paint at (%d, %d, %d, %d)\n", this, 1.1460 + aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); 1.1461 +#endif 1.1462 + 1.1463 + if (aBuilder->IsForEventDelivery()) { 1.1464 + // Don't allow children to receive events. 1.1465 + // REVIEW: following old GetFrameForPoint 1.1466 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.1467 + } else { 1.1468 + // REVIEW: Our in-flow child frames are inline-level so they will paint in our 1.1469 + // content list, so we don't need to mess with layers. 1.1470 + nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); 1.1471 + } 1.1472 + 1.1473 + // draw a focus indicator only when focus rings should be drawn 1.1474 + nsIDocument* doc = mContent->GetCurrentDoc(); 1.1475 + if (doc) { 1.1476 + nsPIDOMWindow* window = doc->GetWindow(); 1.1477 + if (window && window->ShouldShowFocusRing()) { 1.1478 + nsPresContext *presContext = PresContext(); 1.1479 + const nsStyleDisplay *disp = StyleDisplay(); 1.1480 + if ((!IsThemed(disp) || 1.1481 + !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) && 1.1482 + mDisplayFrame && IsVisibleForPainting(aBuilder)) { 1.1483 + aLists.Content()->AppendNewToTop( 1.1484 + new (aBuilder) nsDisplayComboboxFocus(aBuilder, this)); 1.1485 + } 1.1486 + } 1.1487 + } 1.1488 + 1.1489 + DisplaySelectionOverlay(aBuilder, aLists.Content()); 1.1490 +} 1.1491 + 1.1492 +void nsComboboxControlFrame::PaintFocus(nsRenderingContext& aRenderingContext, 1.1493 + nsPoint aPt) 1.1494 +{ 1.1495 + /* Do we need to do anything? */ 1.1496 + EventStates eventStates = mContent->AsElement()->State(); 1.1497 + if (eventStates.HasState(NS_EVENT_STATE_DISABLED) || sFocused != this) 1.1498 + return; 1.1499 + 1.1500 + aRenderingContext.PushState(); 1.1501 + nsRect clipRect = mDisplayFrame->GetRect() + aPt; 1.1502 + aRenderingContext.IntersectClip(clipRect); 1.1503 + 1.1504 + // REVIEW: Why does the old code paint mDisplayFrame again? We've 1.1505 + // already painted it in the children above. So clipping it here won't do 1.1506 + // us much good. 1.1507 + 1.1508 + ///////////////////// 1.1509 + // draw focus 1.1510 + 1.1511 + aRenderingContext.SetLineStyle(nsLineStyle_kDotted); 1.1512 + aRenderingContext.SetColor(StyleColor()->mColor); 1.1513 + 1.1514 + //aRenderingContext.DrawRect(clipRect); 1.1515 + 1.1516 + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); 1.1517 + clipRect.width -= onePixel; 1.1518 + clipRect.height -= onePixel; 1.1519 + aRenderingContext.DrawLine(clipRect.TopLeft(), clipRect.TopRight()); 1.1520 + aRenderingContext.DrawLine(clipRect.TopRight(), clipRect.BottomRight()); 1.1521 + aRenderingContext.DrawLine(clipRect.BottomRight(), clipRect.BottomLeft()); 1.1522 + aRenderingContext.DrawLine(clipRect.BottomLeft(), clipRect.TopLeft()); 1.1523 + 1.1524 + aRenderingContext.PopState(); 1.1525 +} 1.1526 + 1.1527 +//--------------------------------------------------------- 1.1528 +// gets the content (an option) by index and then set it as 1.1529 +// being selected or not selected 1.1530 +//--------------------------------------------------------- 1.1531 +NS_IMETHODIMP 1.1532 +nsComboboxControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected) 1.1533 +{ 1.1534 + if (mDroppedDown) { 1.1535 + nsISelectControlFrame *selectFrame = do_QueryFrame(mListControlFrame); 1.1536 + if (selectFrame) { 1.1537 + selectFrame->OnOptionSelected(aIndex, aSelected); 1.1538 + } 1.1539 + } else { 1.1540 + if (aSelected) { 1.1541 + nsAutoScriptBlocker blocker; 1.1542 + RedisplayText(aIndex); 1.1543 + } else { 1.1544 + nsWeakFrame weakFrame(this); 1.1545 + RedisplaySelectedText(); 1.1546 + if (weakFrame.IsAlive()) { 1.1547 + FireValueChangeEvent(); // Fire after old option is unselected 1.1548 + } 1.1549 + } 1.1550 + } 1.1551 + 1.1552 + return NS_OK; 1.1553 +} 1.1554 + 1.1555 +void nsComboboxControlFrame::FireValueChangeEvent() 1.1556 +{ 1.1557 + // Fire ValueChange event to indicate data value of combo box has changed 1.1558 + nsContentUtils::AddScriptRunner( 1.1559 + new AsyncEventDispatcher(mContent, NS_LITERAL_STRING("ValueChange"), true, 1.1560 + false)); 1.1561 +} 1.1562 + 1.1563 +void 1.1564 +nsComboboxControlFrame::OnContentReset() 1.1565 +{ 1.1566 + if (mListControlFrame) { 1.1567 + mListControlFrame->OnContentReset(); 1.1568 + } 1.1569 +} 1.1570 + 1.1571 + 1.1572 +//-------------------------------------------------------- 1.1573 +// nsIStatefulFrame 1.1574 +//-------------------------------------------------------- 1.1575 +NS_IMETHODIMP 1.1576 +nsComboboxControlFrame::SaveState(nsPresState** aState) 1.1577 +{ 1.1578 + if (!mListControlFrame) 1.1579 + return NS_ERROR_FAILURE; 1.1580 + 1.1581 + nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame); 1.1582 + return stateful->SaveState(aState); 1.1583 +} 1.1584 + 1.1585 +NS_IMETHODIMP 1.1586 +nsComboboxControlFrame::RestoreState(nsPresState* aState) 1.1587 +{ 1.1588 + if (!mListControlFrame) 1.1589 + return NS_ERROR_FAILURE; 1.1590 + 1.1591 + nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame); 1.1592 + NS_ASSERTION(stateful, "Must implement nsIStatefulFrame"); 1.1593 + return stateful->RestoreState(aState); 1.1594 +} 1.1595 + 1.1596 + 1.1597 +// 1.1598 +// Camino uses a native widget for the combobox 1.1599 +// popup, which affects drawing and event 1.1600 +// handling here and in nsListControlFrame. 1.1601 +// 1.1602 +// Also, Fennec use a custom combobox built-in widget 1.1603 +// 1.1604 + 1.1605 +/* static */ 1.1606 +bool 1.1607 +nsComboboxControlFrame::ToolkitHasNativePopup() 1.1608 +{ 1.1609 +#ifdef MOZ_USE_NATIVE_POPUP_WINDOWS 1.1610 + return true; 1.1611 +#else 1.1612 + return false; 1.1613 +#endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ 1.1614 +} 1.1615 +