Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | #include "nsCOMPtr.h" |
michael@0 | 6 | #include "nsComboboxControlFrame.h" |
michael@0 | 7 | #include "nsFocusManager.h" |
michael@0 | 8 | #include "nsFormControlFrame.h" |
michael@0 | 9 | #include "nsGkAtoms.h" |
michael@0 | 10 | #include "nsCSSAnonBoxes.h" |
michael@0 | 11 | #include "nsHTMLParts.h" |
michael@0 | 12 | #include "nsIFormControl.h" |
michael@0 | 13 | #include "nsNameSpaceManager.h" |
michael@0 | 14 | #include "nsIListControlFrame.h" |
michael@0 | 15 | #include "nsPIDOMWindow.h" |
michael@0 | 16 | #include "nsIPresShell.h" |
michael@0 | 17 | #include "nsContentList.h" |
michael@0 | 18 | #include "nsView.h" |
michael@0 | 19 | #include "nsViewManager.h" |
michael@0 | 20 | #include "nsIDOMEventListener.h" |
michael@0 | 21 | #include "nsIDOMNode.h" |
michael@0 | 22 | #include "nsISelectControlFrame.h" |
michael@0 | 23 | #include "nsContentUtils.h" |
michael@0 | 24 | #include "nsIDocument.h" |
michael@0 | 25 | #include "nsINodeInfo.h" |
michael@0 | 26 | #include "nsIScrollableFrame.h" |
michael@0 | 27 | #include "nsListControlFrame.h" |
michael@0 | 28 | #include "nsAutoPtr.h" |
michael@0 | 29 | #include "nsStyleSet.h" |
michael@0 | 30 | #include "nsNodeInfoManager.h" |
michael@0 | 31 | #include "nsContentCreatorFunctions.h" |
michael@0 | 32 | #include "nsLayoutUtils.h" |
michael@0 | 33 | #include "nsDisplayList.h" |
michael@0 | 34 | #include "nsITheme.h" |
michael@0 | 35 | #include "nsRenderingContext.h" |
michael@0 | 36 | #include "mozilla/Likely.h" |
michael@0 | 37 | #include <algorithm> |
michael@0 | 38 | #include "nsTextNode.h" |
michael@0 | 39 | #include "mozilla/AsyncEventDispatcher.h" |
michael@0 | 40 | #include "mozilla/EventStates.h" |
michael@0 | 41 | #include "mozilla/LookAndFeel.h" |
michael@0 | 42 | #include "mozilla/MouseEvents.h" |
michael@0 | 43 | #include "mozilla/unused.h" |
michael@0 | 44 | |
michael@0 | 45 | using namespace mozilla; |
michael@0 | 46 | |
michael@0 | 47 | NS_IMETHODIMP |
michael@0 | 48 | nsComboboxControlFrame::RedisplayTextEvent::Run() |
michael@0 | 49 | { |
michael@0 | 50 | if (mControlFrame) |
michael@0 | 51 | mControlFrame->HandleRedisplayTextEvent(); |
michael@0 | 52 | return NS_OK; |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | class nsPresState; |
michael@0 | 56 | |
michael@0 | 57 | #define FIX_FOR_BUG_53259 |
michael@0 | 58 | |
michael@0 | 59 | // Drop down list event management. |
michael@0 | 60 | // The combo box uses the following strategy for managing the drop-down list. |
michael@0 | 61 | // If the combo box or its arrow button is clicked on the drop-down list is displayed |
michael@0 | 62 | // If mouse exits the combo box with the drop-down list displayed the drop-down list |
michael@0 | 63 | // is asked to capture events |
michael@0 | 64 | // The drop-down list will capture all events including mouse down and up and will always |
michael@0 | 65 | // return with ListWasSelected method call regardless of whether an item in the list was |
michael@0 | 66 | // actually selected. |
michael@0 | 67 | // The ListWasSelected code will turn off mouse-capture for the drop-down list. |
michael@0 | 68 | // The drop-down list does not explicitly set capture when it is in the drop-down mode. |
michael@0 | 69 | |
michael@0 | 70 | |
michael@0 | 71 | /** |
michael@0 | 72 | * Helper class that listens to the combo boxes button. If the button is pressed the |
michael@0 | 73 | * combo box is toggled to open or close. this is used by Accessibility which presses |
michael@0 | 74 | * that button Programmatically. |
michael@0 | 75 | */ |
michael@0 | 76 | class nsComboButtonListener : public nsIDOMEventListener |
michael@0 | 77 | { |
michael@0 | 78 | public: |
michael@0 | 79 | NS_DECL_ISUPPORTS |
michael@0 | 80 | |
michael@0 | 81 | NS_IMETHOD HandleEvent(nsIDOMEvent*) MOZ_OVERRIDE |
michael@0 | 82 | { |
michael@0 | 83 | mComboBox->ShowDropDown(!mComboBox->IsDroppedDown()); |
michael@0 | 84 | return NS_OK; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | nsComboButtonListener(nsComboboxControlFrame* aCombobox) |
michael@0 | 88 | { |
michael@0 | 89 | mComboBox = aCombobox; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | virtual ~nsComboButtonListener() {} |
michael@0 | 93 | |
michael@0 | 94 | nsComboboxControlFrame* mComboBox; |
michael@0 | 95 | }; |
michael@0 | 96 | |
michael@0 | 97 | NS_IMPL_ISUPPORTS(nsComboButtonListener, |
michael@0 | 98 | nsIDOMEventListener) |
michael@0 | 99 | |
michael@0 | 100 | // static class data member for Bug 32920 |
michael@0 | 101 | nsComboboxControlFrame* nsComboboxControlFrame::sFocused = nullptr; |
michael@0 | 102 | |
michael@0 | 103 | nsIFrame* |
michael@0 | 104 | NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aStateFlags) |
michael@0 | 105 | { |
michael@0 | 106 | nsComboboxControlFrame* it = new (aPresShell) nsComboboxControlFrame(aContext); |
michael@0 | 107 | |
michael@0 | 108 | if (it) { |
michael@0 | 109 | // set the state flags (if any are provided) |
michael@0 | 110 | it->AddStateBits(aStateFlags); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | return it; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | NS_IMPL_FRAMEARENA_HELPERS(nsComboboxControlFrame) |
michael@0 | 117 | |
michael@0 | 118 | //----------------------------------------------------------- |
michael@0 | 119 | // Reflow Debugging Macros |
michael@0 | 120 | // These let us "see" how many reflow counts are happening |
michael@0 | 121 | //----------------------------------------------------------- |
michael@0 | 122 | #ifdef DO_REFLOW_COUNTER |
michael@0 | 123 | |
michael@0 | 124 | #define MAX_REFLOW_CNT 1024 |
michael@0 | 125 | static int32_t gTotalReqs = 0;; |
michael@0 | 126 | static int32_t gTotalReflows = 0;; |
michael@0 | 127 | static int32_t gReflowControlCntRQ[MAX_REFLOW_CNT]; |
michael@0 | 128 | static int32_t gReflowControlCnt[MAX_REFLOW_CNT]; |
michael@0 | 129 | static int32_t gReflowInx = -1; |
michael@0 | 130 | |
michael@0 | 131 | #define REFLOW_COUNTER() \ |
michael@0 | 132 | if (mReflowId > -1) \ |
michael@0 | 133 | gReflowControlCnt[mReflowId]++; |
michael@0 | 134 | |
michael@0 | 135 | #define REFLOW_COUNTER_REQUEST() \ |
michael@0 | 136 | if (mReflowId > -1) \ |
michael@0 | 137 | gReflowControlCntRQ[mReflowId]++; |
michael@0 | 138 | |
michael@0 | 139 | #define REFLOW_COUNTER_DUMP(__desc) \ |
michael@0 | 140 | if (mReflowId > -1) {\ |
michael@0 | 141 | gTotalReqs += gReflowControlCntRQ[mReflowId];\ |
michael@0 | 142 | gTotalReflows += gReflowControlCnt[mReflowId];\ |
michael@0 | 143 | printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \ |
michael@0 | 144 | mReflowId, (__desc), \ |
michael@0 | 145 | gReflowControlCnt[mReflowId], \ |
michael@0 | 146 | gReflowControlCntRQ[mReflowId],\ |
michael@0 | 147 | gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\ |
michael@0 | 148 | } |
michael@0 | 149 | |
michael@0 | 150 | #define REFLOW_COUNTER_INIT() \ |
michael@0 | 151 | if (gReflowInx < MAX_REFLOW_CNT) { \ |
michael@0 | 152 | gReflowInx++; \ |
michael@0 | 153 | mReflowId = gReflowInx; \ |
michael@0 | 154 | gReflowControlCnt[mReflowId] = 0; \ |
michael@0 | 155 | gReflowControlCntRQ[mReflowId] = 0; \ |
michael@0 | 156 | } else { \ |
michael@0 | 157 | mReflowId = -1; \ |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | // reflow messages |
michael@0 | 161 | #define REFLOW_DEBUG_MSG(_msg1) printf((_msg1)) |
michael@0 | 162 | #define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) |
michael@0 | 163 | #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) |
michael@0 | 164 | #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) |
michael@0 | 165 | |
michael@0 | 166 | #else //------------- |
michael@0 | 167 | |
michael@0 | 168 | #define REFLOW_COUNTER_REQUEST() |
michael@0 | 169 | #define REFLOW_COUNTER() |
michael@0 | 170 | #define REFLOW_COUNTER_DUMP(__desc) |
michael@0 | 171 | #define REFLOW_COUNTER_INIT() |
michael@0 | 172 | |
michael@0 | 173 | #define REFLOW_DEBUG_MSG(_msg) |
michael@0 | 174 | #define REFLOW_DEBUG_MSG2(_msg1, _msg2) |
michael@0 | 175 | #define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) |
michael@0 | 176 | #define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) |
michael@0 | 177 | |
michael@0 | 178 | |
michael@0 | 179 | #endif |
michael@0 | 180 | |
michael@0 | 181 | //------------------------------------------ |
michael@0 | 182 | // This is for being VERY noisy |
michael@0 | 183 | //------------------------------------------ |
michael@0 | 184 | #ifdef DO_VERY_NOISY |
michael@0 | 185 | #define REFLOW_NOISY_MSG(_msg1) printf((_msg1)) |
michael@0 | 186 | #define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) |
michael@0 | 187 | #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) |
michael@0 | 188 | #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) |
michael@0 | 189 | #else |
michael@0 | 190 | #define REFLOW_NOISY_MSG(_msg) |
michael@0 | 191 | #define REFLOW_NOISY_MSG2(_msg1, _msg2) |
michael@0 | 192 | #define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) |
michael@0 | 193 | #define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) |
michael@0 | 194 | #endif |
michael@0 | 195 | |
michael@0 | 196 | //------------------------------------------ |
michael@0 | 197 | // Displays value in pixels or twips |
michael@0 | 198 | //------------------------------------------ |
michael@0 | 199 | #ifdef DO_PIXELS |
michael@0 | 200 | #define PX(__v) __v / 15 |
michael@0 | 201 | #else |
michael@0 | 202 | #define PX(__v) __v |
michael@0 | 203 | #endif |
michael@0 | 204 | |
michael@0 | 205 | //------------------------------------------------------ |
michael@0 | 206 | //-- Done with macros |
michael@0 | 207 | //------------------------------------------------------ |
michael@0 | 208 | |
michael@0 | 209 | nsComboboxControlFrame::nsComboboxControlFrame(nsStyleContext* aContext) |
michael@0 | 210 | : nsBlockFrame(aContext) |
michael@0 | 211 | , mDisplayFrame(nullptr) |
michael@0 | 212 | , mButtonFrame(nullptr) |
michael@0 | 213 | , mDropdownFrame(nullptr) |
michael@0 | 214 | , mListControlFrame(nullptr) |
michael@0 | 215 | , mDisplayWidth(0) |
michael@0 | 216 | , mRecentSelectedIndex(NS_SKIP_NOTIFY_INDEX) |
michael@0 | 217 | , mDisplayedIndex(-1) |
michael@0 | 218 | , mLastDropDownAboveScreenY(nscoord_MIN) |
michael@0 | 219 | , mLastDropDownBelowScreenY(nscoord_MIN) |
michael@0 | 220 | , mDroppedDown(false) |
michael@0 | 221 | , mInRedisplayText(false) |
michael@0 | 222 | , mDelayedShowDropDown(false) |
michael@0 | 223 | { |
michael@0 | 224 | REFLOW_COUNTER_INIT() |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | //-------------------------------------------------------------- |
michael@0 | 228 | nsComboboxControlFrame::~nsComboboxControlFrame() |
michael@0 | 229 | { |
michael@0 | 230 | REFLOW_COUNTER_DUMP("nsCCF"); |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | //-------------------------------------------------------------- |
michael@0 | 234 | |
michael@0 | 235 | NS_QUERYFRAME_HEAD(nsComboboxControlFrame) |
michael@0 | 236 | NS_QUERYFRAME_ENTRY(nsIComboboxControlFrame) |
michael@0 | 237 | NS_QUERYFRAME_ENTRY(nsIFormControlFrame) |
michael@0 | 238 | NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) |
michael@0 | 239 | NS_QUERYFRAME_ENTRY(nsISelectControlFrame) |
michael@0 | 240 | NS_QUERYFRAME_ENTRY(nsIStatefulFrame) |
michael@0 | 241 | NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame) |
michael@0 | 242 | |
michael@0 | 243 | #ifdef ACCESSIBILITY |
michael@0 | 244 | a11y::AccType |
michael@0 | 245 | nsComboboxControlFrame::AccessibleType() |
michael@0 | 246 | { |
michael@0 | 247 | return a11y::eHTMLComboboxType; |
michael@0 | 248 | } |
michael@0 | 249 | #endif |
michael@0 | 250 | |
michael@0 | 251 | void |
michael@0 | 252 | nsComboboxControlFrame::SetFocus(bool aOn, bool aRepaint) |
michael@0 | 253 | { |
michael@0 | 254 | nsWeakFrame weakFrame(this); |
michael@0 | 255 | if (aOn) { |
michael@0 | 256 | nsListControlFrame::ComboboxFocusSet(); |
michael@0 | 257 | sFocused = this; |
michael@0 | 258 | if (mDelayedShowDropDown) { |
michael@0 | 259 | ShowDropDown(true); // might destroy us |
michael@0 | 260 | if (!weakFrame.IsAlive()) { |
michael@0 | 261 | return; |
michael@0 | 262 | } |
michael@0 | 263 | } |
michael@0 | 264 | } else { |
michael@0 | 265 | sFocused = nullptr; |
michael@0 | 266 | mDelayedShowDropDown = false; |
michael@0 | 267 | if (mDroppedDown) { |
michael@0 | 268 | mListControlFrame->ComboboxFinish(mDisplayedIndex); // might destroy us |
michael@0 | 269 | if (!weakFrame.IsAlive()) { |
michael@0 | 270 | return; |
michael@0 | 271 | } |
michael@0 | 272 | } |
michael@0 | 273 | // May delete |this|. |
michael@0 | 274 | mListControlFrame->FireOnChange(); |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | if (!weakFrame.IsAlive()) { |
michael@0 | 278 | return; |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | // This is needed on a temporary basis. It causes the focus |
michael@0 | 282 | // rect to be drawn. This is much faster than ReResolvingStyle |
michael@0 | 283 | // Bug 32920 |
michael@0 | 284 | InvalidateFrame(); |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | void |
michael@0 | 288 | nsComboboxControlFrame::ShowPopup(bool aShowPopup) |
michael@0 | 289 | { |
michael@0 | 290 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 291 | nsViewManager* viewManager = view->GetViewManager(); |
michael@0 | 292 | |
michael@0 | 293 | if (aShowPopup) { |
michael@0 | 294 | nsRect rect = mDropdownFrame->GetRect(); |
michael@0 | 295 | rect.x = rect.y = 0; |
michael@0 | 296 | viewManager->ResizeView(view, rect); |
michael@0 | 297 | viewManager->SetViewVisibility(view, nsViewVisibility_kShow); |
michael@0 | 298 | } else { |
michael@0 | 299 | viewManager->SetViewVisibility(view, nsViewVisibility_kHide); |
michael@0 | 300 | nsRect emptyRect(0, 0, 0, 0); |
michael@0 | 301 | viewManager->ResizeView(view, emptyRect); |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | // fire a popup dom event |
michael@0 | 305 | nsEventStatus status = nsEventStatus_eIgnore; |
michael@0 | 306 | WidgetMouseEvent event(true, aShowPopup ? |
michael@0 | 307 | NS_XUL_POPUP_SHOWING : NS_XUL_POPUP_HIDING, nullptr, |
michael@0 | 308 | WidgetMouseEvent::eReal); |
michael@0 | 309 | |
michael@0 | 310 | nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell(); |
michael@0 | 311 | if (shell) |
michael@0 | 312 | shell->HandleDOMEventWithTarget(mContent, &event, &status); |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | bool |
michael@0 | 316 | nsComboboxControlFrame::ShowList(bool aShowList) |
michael@0 | 317 | { |
michael@0 | 318 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 319 | if (aShowList) { |
michael@0 | 320 | NS_ASSERTION(!view->HasWidget(), |
michael@0 | 321 | "We shouldn't have a widget before we need to display the popup"); |
michael@0 | 322 | |
michael@0 | 323 | // Create the widget for the drop-down list |
michael@0 | 324 | view->GetViewManager()->SetViewFloating(view, true); |
michael@0 | 325 | |
michael@0 | 326 | nsWidgetInitData widgetData; |
michael@0 | 327 | widgetData.mWindowType = eWindowType_popup; |
michael@0 | 328 | widgetData.mBorderStyle = eBorderStyle_default; |
michael@0 | 329 | view->CreateWidgetForPopup(&widgetData); |
michael@0 | 330 | } else { |
michael@0 | 331 | nsIWidget* widget = view->GetWidget(); |
michael@0 | 332 | if (widget) { |
michael@0 | 333 | // We must do this before ShowPopup in case it destroys us (bug 813442). |
michael@0 | 334 | widget->CaptureRollupEvents(this, false); |
michael@0 | 335 | } |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | nsWeakFrame weakFrame(this); |
michael@0 | 339 | ShowPopup(aShowList); // might destroy us |
michael@0 | 340 | if (!weakFrame.IsAlive()) { |
michael@0 | 341 | return false; |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | mDroppedDown = aShowList; |
michael@0 | 345 | nsIWidget* widget = view->GetWidget(); |
michael@0 | 346 | if (mDroppedDown) { |
michael@0 | 347 | // The listcontrol frame will call back to the nsComboboxControlFrame's |
michael@0 | 348 | // ListWasSelected which will stop the capture. |
michael@0 | 349 | mListControlFrame->AboutToDropDown(); |
michael@0 | 350 | mListControlFrame->CaptureMouseEvents(true); |
michael@0 | 351 | if (widget) { |
michael@0 | 352 | widget->CaptureRollupEvents(this, true); |
michael@0 | 353 | } |
michael@0 | 354 | } else { |
michael@0 | 355 | if (widget) { |
michael@0 | 356 | view->DestroyWidget(); |
michael@0 | 357 | } |
michael@0 | 358 | } |
michael@0 | 359 | |
michael@0 | 360 | return weakFrame.IsAlive(); |
michael@0 | 361 | } |
michael@0 | 362 | |
michael@0 | 363 | class nsResizeDropdownAtFinalPosition |
michael@0 | 364 | : public nsIReflowCallback, public nsRunnable |
michael@0 | 365 | { |
michael@0 | 366 | public: |
michael@0 | 367 | nsResizeDropdownAtFinalPosition(nsComboboxControlFrame* aFrame) |
michael@0 | 368 | : mFrame(aFrame) |
michael@0 | 369 | { |
michael@0 | 370 | MOZ_COUNT_CTOR(nsResizeDropdownAtFinalPosition); |
michael@0 | 371 | } |
michael@0 | 372 | ~nsResizeDropdownAtFinalPosition() |
michael@0 | 373 | { |
michael@0 | 374 | MOZ_COUNT_DTOR(nsResizeDropdownAtFinalPosition); |
michael@0 | 375 | } |
michael@0 | 376 | |
michael@0 | 377 | virtual bool ReflowFinished() MOZ_OVERRIDE |
michael@0 | 378 | { |
michael@0 | 379 | Run(); |
michael@0 | 380 | NS_RELEASE_THIS(); |
michael@0 | 381 | return false; |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | virtual void ReflowCallbackCanceled() MOZ_OVERRIDE |
michael@0 | 385 | { |
michael@0 | 386 | NS_RELEASE_THIS(); |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | NS_IMETHODIMP Run() |
michael@0 | 390 | { |
michael@0 | 391 | if (mFrame.IsAlive()) { |
michael@0 | 392 | static_cast<nsComboboxControlFrame*>(mFrame.GetFrame())-> |
michael@0 | 393 | AbsolutelyPositionDropDown(); |
michael@0 | 394 | } |
michael@0 | 395 | return NS_OK; |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | nsWeakFrame mFrame; |
michael@0 | 399 | }; |
michael@0 | 400 | |
michael@0 | 401 | nsresult |
michael@0 | 402 | nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext, |
michael@0 | 403 | const nsHTMLReflowState& aReflowState) |
michael@0 | 404 | { |
michael@0 | 405 | // All we want out of it later on, really, is the height of a row, so we |
michael@0 | 406 | // don't even need to cache mDropdownFrame's ascent or anything. If we don't |
michael@0 | 407 | // need to reflow it, just bail out here. |
michael@0 | 408 | if (!aReflowState.ShouldReflowAllKids() && |
michael@0 | 409 | !NS_SUBTREE_DIRTY(mDropdownFrame)) { |
michael@0 | 410 | return NS_OK; |
michael@0 | 411 | } |
michael@0 | 412 | |
michael@0 | 413 | // XXXbz this will, for small-height dropdowns, have extra space on the right |
michael@0 | 414 | // edge for the scrollbar we don't show... but that's the best we can do here |
michael@0 | 415 | // for now. |
michael@0 | 416 | nsSize availSize(aReflowState.AvailableWidth(), NS_UNCONSTRAINEDSIZE); |
michael@0 | 417 | nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mDropdownFrame, |
michael@0 | 418 | availSize); |
michael@0 | 419 | |
michael@0 | 420 | // If the dropdown's intrinsic width is narrower than our specified width, |
michael@0 | 421 | // then expand it out. We want our border-box width to end up the same as |
michael@0 | 422 | // the dropdown's so account for both sets of mComputedBorderPadding. |
michael@0 | 423 | nscoord forcedWidth = aReflowState.ComputedWidth() + |
michael@0 | 424 | aReflowState.ComputedPhysicalBorderPadding().LeftRight() - |
michael@0 | 425 | kidReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 426 | kidReflowState.SetComputedWidth(std::max(kidReflowState.ComputedWidth(), |
michael@0 | 427 | forcedWidth)); |
michael@0 | 428 | |
michael@0 | 429 | // ensure we start off hidden |
michael@0 | 430 | if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
michael@0 | 431 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 432 | nsViewManager* viewManager = view->GetViewManager(); |
michael@0 | 433 | viewManager->SetViewVisibility(view, nsViewVisibility_kHide); |
michael@0 | 434 | nsRect emptyRect(0, 0, 0, 0); |
michael@0 | 435 | viewManager->ResizeView(view, emptyRect); |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | // Allow the child to move/size/change-visibility its view if it's currently |
michael@0 | 439 | // dropped down |
michael@0 | 440 | int32_t flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW; |
michael@0 | 441 | if (mDroppedDown) { |
michael@0 | 442 | flags = 0; |
michael@0 | 443 | } |
michael@0 | 444 | nsRect rect = mDropdownFrame->GetRect(); |
michael@0 | 445 | nsHTMLReflowMetrics desiredSize(aReflowState); |
michael@0 | 446 | nsReflowStatus ignoredStatus; |
michael@0 | 447 | nsresult rv = ReflowChild(mDropdownFrame, aPresContext, desiredSize, |
michael@0 | 448 | kidReflowState, rect.x, rect.y, flags, |
michael@0 | 449 | ignoredStatus); |
michael@0 | 450 | |
michael@0 | 451 | // Set the child's width and height to its desired size |
michael@0 | 452 | FinishReflowChild(mDropdownFrame, aPresContext, desiredSize, |
michael@0 | 453 | &kidReflowState, rect.x, rect.y, flags); |
michael@0 | 454 | return rv; |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | nsPoint |
michael@0 | 458 | nsComboboxControlFrame::GetCSSTransformTranslation() |
michael@0 | 459 | { |
michael@0 | 460 | nsIFrame* frame = this; |
michael@0 | 461 | bool is3DTransform = false; |
michael@0 | 462 | gfxMatrix transform; |
michael@0 | 463 | while (frame) { |
michael@0 | 464 | nsIFrame* parent; |
michael@0 | 465 | gfx3DMatrix ctm = frame->GetTransformMatrix(nullptr, &parent); |
michael@0 | 466 | gfxMatrix matrix; |
michael@0 | 467 | if (ctm.Is2D(&matrix)) { |
michael@0 | 468 | transform = transform * matrix; |
michael@0 | 469 | } else { |
michael@0 | 470 | is3DTransform = true; |
michael@0 | 471 | break; |
michael@0 | 472 | } |
michael@0 | 473 | frame = parent; |
michael@0 | 474 | } |
michael@0 | 475 | nsPoint translation; |
michael@0 | 476 | if (!is3DTransform && !transform.HasNonTranslation()) { |
michael@0 | 477 | nsPresContext* pc = PresContext(); |
michael@0 | 478 | gfxPoint pixelTranslation = transform.GetTranslation(); |
michael@0 | 479 | int32_t apd = pc->AppUnitsPerDevPixel(); |
michael@0 | 480 | translation.x = NSFloatPixelsToAppUnits(float(pixelTranslation.x), apd); |
michael@0 | 481 | translation.y = NSFloatPixelsToAppUnits(float(pixelTranslation.y), apd); |
michael@0 | 482 | // To get the translation introduced only by transforms we subtract the |
michael@0 | 483 | // regular non-transform translation. |
michael@0 | 484 | nsRootPresContext* rootPC = pc->GetRootPresContext(); |
michael@0 | 485 | if (rootPC) { |
michael@0 | 486 | translation -= GetOffsetToCrossDoc(rootPC->PresShell()->GetRootFrame()); |
michael@0 | 487 | } else { |
michael@0 | 488 | translation.x = translation.y = 0; |
michael@0 | 489 | } |
michael@0 | 490 | } |
michael@0 | 491 | return translation; |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | class nsAsyncRollup : public nsRunnable |
michael@0 | 495 | { |
michael@0 | 496 | public: |
michael@0 | 497 | nsAsyncRollup(nsComboboxControlFrame* aFrame) : mFrame(aFrame) {} |
michael@0 | 498 | NS_IMETHODIMP Run() |
michael@0 | 499 | { |
michael@0 | 500 | if (mFrame.IsAlive()) { |
michael@0 | 501 | static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()) |
michael@0 | 502 | ->RollupFromList(); |
michael@0 | 503 | } |
michael@0 | 504 | return NS_OK; |
michael@0 | 505 | } |
michael@0 | 506 | nsWeakFrame mFrame; |
michael@0 | 507 | }; |
michael@0 | 508 | |
michael@0 | 509 | class nsAsyncResize : public nsRunnable |
michael@0 | 510 | { |
michael@0 | 511 | public: |
michael@0 | 512 | nsAsyncResize(nsComboboxControlFrame* aFrame) : mFrame(aFrame) {} |
michael@0 | 513 | NS_IMETHODIMP Run() |
michael@0 | 514 | { |
michael@0 | 515 | if (mFrame.IsAlive()) { |
michael@0 | 516 | nsComboboxControlFrame* combo = |
michael@0 | 517 | static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()); |
michael@0 | 518 | static_cast<nsListControlFrame*>(combo->mDropdownFrame)-> |
michael@0 | 519 | SetSuppressScrollbarUpdate(true); |
michael@0 | 520 | nsCOMPtr<nsIPresShell> shell = mFrame->PresContext()->PresShell(); |
michael@0 | 521 | shell->FrameNeedsReflow(combo->mDropdownFrame, nsIPresShell::eResize, |
michael@0 | 522 | NS_FRAME_IS_DIRTY); |
michael@0 | 523 | shell->FlushPendingNotifications(Flush_Layout); |
michael@0 | 524 | if (mFrame.IsAlive()) { |
michael@0 | 525 | combo = static_cast<nsComboboxControlFrame*>(mFrame.GetFrame()); |
michael@0 | 526 | static_cast<nsListControlFrame*>(combo->mDropdownFrame)-> |
michael@0 | 527 | SetSuppressScrollbarUpdate(false); |
michael@0 | 528 | if (combo->mDelayedShowDropDown) { |
michael@0 | 529 | combo->ShowDropDown(true); |
michael@0 | 530 | } |
michael@0 | 531 | } |
michael@0 | 532 | } |
michael@0 | 533 | return NS_OK; |
michael@0 | 534 | } |
michael@0 | 535 | nsWeakFrame mFrame; |
michael@0 | 536 | }; |
michael@0 | 537 | |
michael@0 | 538 | void |
michael@0 | 539 | nsComboboxControlFrame::GetAvailableDropdownSpace(nscoord* aAbove, |
michael@0 | 540 | nscoord* aBelow, |
michael@0 | 541 | nsPoint* aTranslation) |
michael@0 | 542 | { |
michael@0 | 543 | // Note: At first glance, it appears that you could simply get the absolute |
michael@0 | 544 | // bounding box for the dropdown list by first getting its view, then getting |
michael@0 | 545 | // the view's nsIWidget, then asking the nsIWidget for its AbsoluteBounds. |
michael@0 | 546 | // The problem with this approach, is that the dropdown lists y location can |
michael@0 | 547 | // change based on whether the dropdown is placed below or above the display |
michael@0 | 548 | // frame. The approach, taken here is to get the absolute position of the |
michael@0 | 549 | // display frame and use its location to determine if the dropdown will go |
michael@0 | 550 | // offscreen. |
michael@0 | 551 | |
michael@0 | 552 | // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms. |
michael@0 | 553 | // In the special case that our transform is only a 2D translation we |
michael@0 | 554 | // introduce this hack so that the dropdown will show up in the right place. |
michael@0 | 555 | *aTranslation = GetCSSTransformTranslation(); |
michael@0 | 556 | *aAbove = 0; |
michael@0 | 557 | *aBelow = 0; |
michael@0 | 558 | |
michael@0 | 559 | nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext()); |
michael@0 | 560 | if (mLastDropDownBelowScreenY == nscoord_MIN) { |
michael@0 | 561 | nsRect thisScreenRect = GetScreenRectInAppUnits(); |
michael@0 | 562 | mLastDropDownBelowScreenY = thisScreenRect.YMost() + aTranslation->y; |
michael@0 | 563 | mLastDropDownAboveScreenY = thisScreenRect.y + aTranslation->y; |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | nscoord minY; |
michael@0 | 567 | nsPresContext* pc = PresContext()->GetToplevelContentDocumentPresContext(); |
michael@0 | 568 | nsIFrame* root = pc ? pc->PresShell()->GetRootFrame() : nullptr; |
michael@0 | 569 | if (root) { |
michael@0 | 570 | minY = root->GetScreenRectInAppUnits().y; |
michael@0 | 571 | if (mLastDropDownBelowScreenY < minY) { |
michael@0 | 572 | // Don't allow the drop-down to be placed above the content area. |
michael@0 | 573 | return; |
michael@0 | 574 | } |
michael@0 | 575 | } else { |
michael@0 | 576 | minY = screen.y; |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | nscoord below = screen.YMost() - mLastDropDownBelowScreenY; |
michael@0 | 580 | nscoord above = mLastDropDownAboveScreenY - minY; |
michael@0 | 581 | |
michael@0 | 582 | // If the difference between the space above and below is less |
michael@0 | 583 | // than a row-height, then we favor the space below. |
michael@0 | 584 | if (above >= below) { |
michael@0 | 585 | nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); |
michael@0 | 586 | nscoord rowHeight = lcf->GetHeightOfARow(); |
michael@0 | 587 | if (above < below + rowHeight) { |
michael@0 | 588 | above -= rowHeight; |
michael@0 | 589 | } |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | *aBelow = below; |
michael@0 | 593 | *aAbove = above; |
michael@0 | 594 | } |
michael@0 | 595 | |
michael@0 | 596 | nsComboboxControlFrame::DropDownPositionState |
michael@0 | 597 | nsComboboxControlFrame::AbsolutelyPositionDropDown() |
michael@0 | 598 | { |
michael@0 | 599 | nsPoint translation; |
michael@0 | 600 | nscoord above, below; |
michael@0 | 601 | mLastDropDownBelowScreenY = nscoord_MIN; |
michael@0 | 602 | GetAvailableDropdownSpace(&above, &below, &translation); |
michael@0 | 603 | if (above <= 0 && below <= 0) { |
michael@0 | 604 | if (IsDroppedDown()) { |
michael@0 | 605 | // Hide the view immediately to minimize flicker. |
michael@0 | 606 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 607 | view->GetViewManager()->SetViewVisibility(view, nsViewVisibility_kHide); |
michael@0 | 608 | NS_DispatchToCurrentThread(new nsAsyncRollup(this)); |
michael@0 | 609 | } |
michael@0 | 610 | return eDropDownPositionSuppressed; |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | nsSize dropdownSize = mDropdownFrame->GetSize(); |
michael@0 | 614 | nscoord height = std::max(above, below); |
michael@0 | 615 | nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); |
michael@0 | 616 | if (height < dropdownSize.height) { |
michael@0 | 617 | if (lcf->GetNumDisplayRows() > 1) { |
michael@0 | 618 | // The drop-down doesn't fit and currently shows more than 1 row - |
michael@0 | 619 | // schedule a resize to show fewer rows. |
michael@0 | 620 | NS_DispatchToCurrentThread(new nsAsyncResize(this)); |
michael@0 | 621 | return eDropDownPositionPendingResize; |
michael@0 | 622 | } |
michael@0 | 623 | } else if (height > (dropdownSize.height + lcf->GetHeightOfARow() * 1.5) && |
michael@0 | 624 | lcf->GetDropdownCanGrow()) { |
michael@0 | 625 | // The drop-down fits but there is room for at least 1.5 more rows - |
michael@0 | 626 | // schedule a resize to show more rows if it has more rows to show. |
michael@0 | 627 | // (1.5 rows for good measure to avoid any rounding issues that would |
michael@0 | 628 | // lead to a loop of reflow requests) |
michael@0 | 629 | NS_DispatchToCurrentThread(new nsAsyncResize(this)); |
michael@0 | 630 | return eDropDownPositionPendingResize; |
michael@0 | 631 | } |
michael@0 | 632 | |
michael@0 | 633 | // Position the drop-down below if there is room, otherwise place it above |
michael@0 | 634 | // if there is room. If there is no room for it on either side then place |
michael@0 | 635 | // it below (to avoid overlapping UI like the URL bar). |
michael@0 | 636 | bool b = dropdownSize.height <= below || dropdownSize.height > above; |
michael@0 | 637 | nsPoint dropdownPosition(0, b ? GetRect().height : -dropdownSize.height); |
michael@0 | 638 | if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
michael@0 | 639 | // Align the right edge of the drop-down with the right edge of the control. |
michael@0 | 640 | dropdownPosition.x = GetRect().width - dropdownSize.width; |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | // Don't position the view unless the position changed since it might cause |
michael@0 | 644 | // a call to NotifyGeometryChange() and an infinite loop here. |
michael@0 | 645 | const nsPoint currentPos = mDropdownFrame->GetPosition(); |
michael@0 | 646 | const nsPoint newPos = dropdownPosition + translation; |
michael@0 | 647 | if (currentPos != newPos) { |
michael@0 | 648 | mDropdownFrame->SetPosition(newPos); |
michael@0 | 649 | nsContainerFrame::PositionFrameView(mDropdownFrame); |
michael@0 | 650 | } |
michael@0 | 651 | return eDropDownPositionFinal; |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | void |
michael@0 | 655 | nsComboboxControlFrame::NotifyGeometryChange() |
michael@0 | 656 | { |
michael@0 | 657 | // We don't need to resize if we're not dropped down since ShowDropDown |
michael@0 | 658 | // does that, or if we're dirty then the reflow callback does it, |
michael@0 | 659 | // or if we have a delayed ShowDropDown pending. |
michael@0 | 660 | if (IsDroppedDown() && |
michael@0 | 661 | !(GetStateBits() & NS_FRAME_IS_DIRTY) && |
michael@0 | 662 | !mDelayedShowDropDown) { |
michael@0 | 663 | // Async because we're likely in a middle of a scroll here so |
michael@0 | 664 | // frame/view positions are in flux. |
michael@0 | 665 | nsRefPtr<nsResizeDropdownAtFinalPosition> resize = |
michael@0 | 666 | new nsResizeDropdownAtFinalPosition(this); |
michael@0 | 667 | NS_DispatchToCurrentThread(resize); |
michael@0 | 668 | } |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | //---------------------------------------------------------- |
michael@0 | 672 | // |
michael@0 | 673 | //---------------------------------------------------------- |
michael@0 | 674 | #ifdef DO_REFLOW_DEBUG |
michael@0 | 675 | static int myCounter = 0; |
michael@0 | 676 | |
michael@0 | 677 | static void printSize(char * aDesc, nscoord aSize) |
michael@0 | 678 | { |
michael@0 | 679 | printf(" %s: ", aDesc); |
michael@0 | 680 | if (aSize == NS_UNCONSTRAINEDSIZE) { |
michael@0 | 681 | printf("UC"); |
michael@0 | 682 | } else { |
michael@0 | 683 | printf("%d", PX(aSize)); |
michael@0 | 684 | } |
michael@0 | 685 | } |
michael@0 | 686 | #endif |
michael@0 | 687 | |
michael@0 | 688 | //------------------------------------------------------------------- |
michael@0 | 689 | //-- Main Reflow for the Combobox |
michael@0 | 690 | //------------------------------------------------------------------- |
michael@0 | 691 | |
michael@0 | 692 | nscoord |
michael@0 | 693 | nsComboboxControlFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext, |
michael@0 | 694 | nsLayoutUtils::IntrinsicWidthType aType) |
michael@0 | 695 | { |
michael@0 | 696 | // get the scrollbar width, we'll use this later |
michael@0 | 697 | nscoord scrollbarWidth = 0; |
michael@0 | 698 | nsPresContext* presContext = PresContext(); |
michael@0 | 699 | if (mListControlFrame) { |
michael@0 | 700 | nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame); |
michael@0 | 701 | NS_ASSERTION(scrollable, "List must be a scrollable frame"); |
michael@0 | 702 | scrollbarWidth = scrollable->GetNondisappearingScrollbarWidth( |
michael@0 | 703 | presContext, aRenderingContext); |
michael@0 | 704 | } |
michael@0 | 705 | |
michael@0 | 706 | nscoord displayWidth = 0; |
michael@0 | 707 | if (MOZ_LIKELY(mDisplayFrame)) { |
michael@0 | 708 | displayWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
michael@0 | 709 | mDisplayFrame, |
michael@0 | 710 | aType); |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | if (mDropdownFrame) { |
michael@0 | 714 | nscoord dropdownContentWidth; |
michael@0 | 715 | bool isUsingOverlayScrollbars = |
michael@0 | 716 | LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0; |
michael@0 | 717 | if (aType == nsLayoutUtils::MIN_WIDTH) { |
michael@0 | 718 | dropdownContentWidth = mDropdownFrame->GetMinWidth(aRenderingContext); |
michael@0 | 719 | if (isUsingOverlayScrollbars) { |
michael@0 | 720 | dropdownContentWidth += scrollbarWidth; |
michael@0 | 721 | } |
michael@0 | 722 | } else { |
michael@0 | 723 | NS_ASSERTION(aType == nsLayoutUtils::PREF_WIDTH, "Unexpected type"); |
michael@0 | 724 | dropdownContentWidth = mDropdownFrame->GetPrefWidth(aRenderingContext); |
michael@0 | 725 | if (isUsingOverlayScrollbars) { |
michael@0 | 726 | dropdownContentWidth += scrollbarWidth; |
michael@0 | 727 | } |
michael@0 | 728 | } |
michael@0 | 729 | dropdownContentWidth = NSCoordSaturatingSubtract(dropdownContentWidth, |
michael@0 | 730 | scrollbarWidth, |
michael@0 | 731 | nscoord_MAX); |
michael@0 | 732 | |
michael@0 | 733 | displayWidth = std::max(dropdownContentWidth, displayWidth); |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | // add room for the dropmarker button if there is one |
michael@0 | 737 | if (!IsThemed() || presContext->GetTheme()->ThemeNeedsComboboxDropmarker()) |
michael@0 | 738 | displayWidth += scrollbarWidth; |
michael@0 | 739 | |
michael@0 | 740 | return displayWidth; |
michael@0 | 741 | |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | nscoord |
michael@0 | 745 | nsComboboxControlFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 746 | { |
michael@0 | 747 | nscoord minWidth; |
michael@0 | 748 | DISPLAY_MIN_WIDTH(this, minWidth); |
michael@0 | 749 | minWidth = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::MIN_WIDTH); |
michael@0 | 750 | return minWidth; |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | nscoord |
michael@0 | 754 | nsComboboxControlFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
michael@0 | 755 | { |
michael@0 | 756 | nscoord prefWidth; |
michael@0 | 757 | DISPLAY_PREF_WIDTH(this, prefWidth); |
michael@0 | 758 | prefWidth = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::PREF_WIDTH); |
michael@0 | 759 | return prefWidth; |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | nsresult |
michael@0 | 763 | nsComboboxControlFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 764 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 765 | const nsHTMLReflowState& aReflowState, |
michael@0 | 766 | nsReflowStatus& aStatus) |
michael@0 | 767 | { |
michael@0 | 768 | // Constraints we try to satisfy: |
michael@0 | 769 | |
michael@0 | 770 | // 1) Default width of button is the vertical scrollbar size |
michael@0 | 771 | // 2) If the width of button is bigger than our width, set width of |
michael@0 | 772 | // button to 0. |
michael@0 | 773 | // 3) Default height of button is height of display area |
michael@0 | 774 | // 4) Width of display area is whatever is left over from our width after |
michael@0 | 775 | // allocating width for the button. |
michael@0 | 776 | // 5) Height of display area is GetHeightOfARow() on the |
michael@0 | 777 | // mListControlFrame. |
michael@0 | 778 | |
michael@0 | 779 | if (!mDisplayFrame || !mButtonFrame || !mDropdownFrame) { |
michael@0 | 780 | NS_ERROR("Why did the frame constructor allow this to happen? Fix it!!"); |
michael@0 | 781 | return NS_ERROR_UNEXPECTED; |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | // Make sure the displayed text is the same as the selected option, bug 297389. |
michael@0 | 785 | int32_t selectedIndex; |
michael@0 | 786 | nsAutoString selectedOptionText; |
michael@0 | 787 | if (!mDroppedDown) { |
michael@0 | 788 | selectedIndex = mListControlFrame->GetSelectedIndex(); |
michael@0 | 789 | } |
michael@0 | 790 | else { |
michael@0 | 791 | // In dropped down mode the "selected index" is the hovered menu item, |
michael@0 | 792 | // we want the last selected item which is |mDisplayedIndex| in this case. |
michael@0 | 793 | selectedIndex = mDisplayedIndex; |
michael@0 | 794 | } |
michael@0 | 795 | if (selectedIndex != -1) { |
michael@0 | 796 | mListControlFrame->GetOptionText(selectedIndex, selectedOptionText); |
michael@0 | 797 | } |
michael@0 | 798 | if (mDisplayedOptionText != selectedOptionText) { |
michael@0 | 799 | RedisplayText(selectedIndex); |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | // First reflow our dropdown so that we know how tall we should be. |
michael@0 | 803 | ReflowDropdown(aPresContext, aReflowState); |
michael@0 | 804 | nsRefPtr<nsResizeDropdownAtFinalPosition> resize = |
michael@0 | 805 | new nsResizeDropdownAtFinalPosition(this); |
michael@0 | 806 | if (NS_SUCCEEDED(aPresContext->PresShell()->PostReflowCallback(resize))) { |
michael@0 | 807 | // The reflow callback queue doesn't AddRef so we keep it alive until |
michael@0 | 808 | // it's released in its ReflowFinished / ReflowCallbackCanceled. |
michael@0 | 809 | unused << resize.forget(); |
michael@0 | 810 | } |
michael@0 | 811 | |
michael@0 | 812 | // Get the width of the vertical scrollbar. That will be the width of the |
michael@0 | 813 | // dropdown button. |
michael@0 | 814 | nscoord buttonWidth; |
michael@0 | 815 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 816 | if (IsThemed(disp) && !aPresContext->GetTheme()->ThemeNeedsComboboxDropmarker()) { |
michael@0 | 817 | buttonWidth = 0; |
michael@0 | 818 | } |
michael@0 | 819 | else { |
michael@0 | 820 | nsIScrollableFrame* scrollable = do_QueryFrame(mListControlFrame); |
michael@0 | 821 | NS_ASSERTION(scrollable, "List must be a scrollable frame"); |
michael@0 | 822 | buttonWidth = scrollable->GetNondisappearingScrollbarWidth( |
michael@0 | 823 | PresContext(), aReflowState.rendContext); |
michael@0 | 824 | if (buttonWidth > aReflowState.ComputedWidth()) { |
michael@0 | 825 | buttonWidth = 0; |
michael@0 | 826 | } |
michael@0 | 827 | } |
michael@0 | 828 | |
michael@0 | 829 | mDisplayWidth = aReflowState.ComputedWidth() - buttonWidth; |
michael@0 | 830 | |
michael@0 | 831 | nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, |
michael@0 | 832 | aStatus); |
michael@0 | 833 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 834 | |
michael@0 | 835 | // The button should occupy the same space as a scrollbar |
michael@0 | 836 | nsRect buttonRect = mButtonFrame->GetRect(); |
michael@0 | 837 | |
michael@0 | 838 | if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { |
michael@0 | 839 | buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().left - |
michael@0 | 840 | aReflowState.ComputedPhysicalPadding().left; |
michael@0 | 841 | } |
michael@0 | 842 | else { |
michael@0 | 843 | buttonRect.x = aReflowState.ComputedPhysicalBorderPadding().LeftRight() + |
michael@0 | 844 | mDisplayWidth - |
michael@0 | 845 | (aReflowState.ComputedPhysicalBorderPadding().right - |
michael@0 | 846 | aReflowState.ComputedPhysicalPadding().right); |
michael@0 | 847 | } |
michael@0 | 848 | buttonRect.width = buttonWidth; |
michael@0 | 849 | |
michael@0 | 850 | buttonRect.y = this->GetUsedBorder().top; |
michael@0 | 851 | buttonRect.height = mDisplayFrame->GetRect().height + |
michael@0 | 852 | this->GetUsedPadding().TopBottom(); |
michael@0 | 853 | |
michael@0 | 854 | mButtonFrame->SetRect(buttonRect); |
michael@0 | 855 | |
michael@0 | 856 | if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && |
michael@0 | 857 | !NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { |
michael@0 | 858 | // This frame didn't fit inside a fragmentation container. Splitting |
michael@0 | 859 | // a nsComboboxControlFrame makes no sense, so we override the status here. |
michael@0 | 860 | aStatus = NS_FRAME_COMPLETE; |
michael@0 | 861 | } |
michael@0 | 862 | return rv; |
michael@0 | 863 | } |
michael@0 | 864 | |
michael@0 | 865 | //-------------------------------------------------------------- |
michael@0 | 866 | |
michael@0 | 867 | nsIAtom* |
michael@0 | 868 | nsComboboxControlFrame::GetType() const |
michael@0 | 869 | { |
michael@0 | 870 | return nsGkAtoms::comboboxControlFrame; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | #ifdef DEBUG_FRAME_DUMP |
michael@0 | 874 | nsresult |
michael@0 | 875 | nsComboboxControlFrame::GetFrameName(nsAString& aResult) const |
michael@0 | 876 | { |
michael@0 | 877 | return MakeFrameName(NS_LITERAL_STRING("ComboboxControl"), aResult); |
michael@0 | 878 | } |
michael@0 | 879 | #endif |
michael@0 | 880 | |
michael@0 | 881 | |
michael@0 | 882 | //---------------------------------------------------------------------- |
michael@0 | 883 | // nsIComboboxControlFrame |
michael@0 | 884 | //---------------------------------------------------------------------- |
michael@0 | 885 | void |
michael@0 | 886 | nsComboboxControlFrame::ShowDropDown(bool aDoDropDown) |
michael@0 | 887 | { |
michael@0 | 888 | mDelayedShowDropDown = false; |
michael@0 | 889 | EventStates eventStates = mContent->AsElement()->State(); |
michael@0 | 890 | if (aDoDropDown && eventStates.HasState(NS_EVENT_STATE_DISABLED)) { |
michael@0 | 891 | return; |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | if (!mDroppedDown && aDoDropDown) { |
michael@0 | 895 | nsFocusManager* fm = nsFocusManager::GetFocusManager(); |
michael@0 | 896 | if (!fm || fm->GetFocusedContent() == GetContent()) { |
michael@0 | 897 | DropDownPositionState state = AbsolutelyPositionDropDown(); |
michael@0 | 898 | if (state == eDropDownPositionFinal) { |
michael@0 | 899 | ShowList(aDoDropDown); // might destroy us |
michael@0 | 900 | } else if (state == eDropDownPositionPendingResize) { |
michael@0 | 901 | // Delay until after the resize reflow, see nsAsyncResize. |
michael@0 | 902 | mDelayedShowDropDown = true; |
michael@0 | 903 | } |
michael@0 | 904 | } else { |
michael@0 | 905 | // Delay until we get focus, see SetFocus(). |
michael@0 | 906 | mDelayedShowDropDown = true; |
michael@0 | 907 | } |
michael@0 | 908 | } else if (mDroppedDown && !aDoDropDown) { |
michael@0 | 909 | ShowList(aDoDropDown); // might destroy us |
michael@0 | 910 | } |
michael@0 | 911 | } |
michael@0 | 912 | |
michael@0 | 913 | void |
michael@0 | 914 | nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame) |
michael@0 | 915 | { |
michael@0 | 916 | mDropdownFrame = aDropDownFrame; |
michael@0 | 917 | mListControlFrame = do_QueryFrame(mDropdownFrame); |
michael@0 | 918 | } |
michael@0 | 919 | |
michael@0 | 920 | nsIFrame* |
michael@0 | 921 | nsComboboxControlFrame::GetDropDown() |
michael@0 | 922 | { |
michael@0 | 923 | return mDropdownFrame; |
michael@0 | 924 | } |
michael@0 | 925 | |
michael@0 | 926 | /////////////////////////////////////////////////////////////// |
michael@0 | 927 | |
michael@0 | 928 | NS_IMETHODIMP |
michael@0 | 929 | nsComboboxControlFrame::RedisplaySelectedText() |
michael@0 | 930 | { |
michael@0 | 931 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 932 | return RedisplayText(mListControlFrame->GetSelectedIndex()); |
michael@0 | 933 | } |
michael@0 | 934 | |
michael@0 | 935 | nsresult |
michael@0 | 936 | nsComboboxControlFrame::RedisplayText(int32_t aIndex) |
michael@0 | 937 | { |
michael@0 | 938 | // Get the text to display |
michael@0 | 939 | if (aIndex != -1) { |
michael@0 | 940 | mListControlFrame->GetOptionText(aIndex, mDisplayedOptionText); |
michael@0 | 941 | } else { |
michael@0 | 942 | mDisplayedOptionText.Truncate(); |
michael@0 | 943 | } |
michael@0 | 944 | mDisplayedIndex = aIndex; |
michael@0 | 945 | |
michael@0 | 946 | REFLOW_DEBUG_MSG2("RedisplayText \"%s\"\n", |
michael@0 | 947 | NS_LossyConvertUTF16toASCII(mDisplayedOptionText).get()); |
michael@0 | 948 | |
michael@0 | 949 | // Send reflow command because the new text maybe larger |
michael@0 | 950 | nsresult rv = NS_OK; |
michael@0 | 951 | if (mDisplayContent) { |
michael@0 | 952 | // Don't call ActuallyDisplayText(true) directly here since that |
michael@0 | 953 | // could cause recursive frame construction. See bug 283117 and the comment in |
michael@0 | 954 | // HandleRedisplayTextEvent() below. |
michael@0 | 955 | |
michael@0 | 956 | // Revoke outstanding events to avoid out-of-order events which could mean |
michael@0 | 957 | // displaying the wrong text. |
michael@0 | 958 | mRedisplayTextEvent.Revoke(); |
michael@0 | 959 | |
michael@0 | 960 | NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), |
michael@0 | 961 | "If we happen to run our redisplay event now, we might kill " |
michael@0 | 962 | "ourselves!"); |
michael@0 | 963 | |
michael@0 | 964 | nsRefPtr<RedisplayTextEvent> event = new RedisplayTextEvent(this); |
michael@0 | 965 | mRedisplayTextEvent = event; |
michael@0 | 966 | if (!nsContentUtils::AddScriptRunner(event)) |
michael@0 | 967 | mRedisplayTextEvent.Forget(); |
michael@0 | 968 | } |
michael@0 | 969 | return rv; |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | void |
michael@0 | 973 | nsComboboxControlFrame::HandleRedisplayTextEvent() |
michael@0 | 974 | { |
michael@0 | 975 | // First, make sure that the content model is up to date and we've |
michael@0 | 976 | // constructed the frames for all our content in the right places. |
michael@0 | 977 | // Otherwise they'll end up under the wrong insertion frame when we |
michael@0 | 978 | // ActuallyDisplayText, since that flushes out the content sink by |
michael@0 | 979 | // calling SetText on a DOM node with aNotify set to true. See bug |
michael@0 | 980 | // 289730. |
michael@0 | 981 | nsWeakFrame weakThis(this); |
michael@0 | 982 | PresContext()->Document()-> |
michael@0 | 983 | FlushPendingNotifications(Flush_ContentAndNotify); |
michael@0 | 984 | if (!weakThis.IsAlive()) |
michael@0 | 985 | return; |
michael@0 | 986 | |
michael@0 | 987 | // Redirect frame insertions during this method (see GetContentInsertionFrame()) |
michael@0 | 988 | // so that any reframing that the frame constructor forces upon us is inserted |
michael@0 | 989 | // into the correct parent (mDisplayFrame). See bug 282607. |
michael@0 | 990 | NS_PRECONDITION(!mInRedisplayText, "Nested RedisplayText"); |
michael@0 | 991 | mInRedisplayText = true; |
michael@0 | 992 | mRedisplayTextEvent.Forget(); |
michael@0 | 993 | |
michael@0 | 994 | ActuallyDisplayText(true); |
michael@0 | 995 | // XXXbz This should perhaps be eResize. Check. |
michael@0 | 996 | PresContext()->PresShell()->FrameNeedsReflow(mDisplayFrame, |
michael@0 | 997 | nsIPresShell::eStyleChange, |
michael@0 | 998 | NS_FRAME_IS_DIRTY); |
michael@0 | 999 | |
michael@0 | 1000 | mInRedisplayText = false; |
michael@0 | 1001 | } |
michael@0 | 1002 | |
michael@0 | 1003 | void |
michael@0 | 1004 | nsComboboxControlFrame::ActuallyDisplayText(bool aNotify) |
michael@0 | 1005 | { |
michael@0 | 1006 | if (mDisplayedOptionText.IsEmpty()) { |
michael@0 | 1007 | // Have to use a non-breaking space for line-height calculations |
michael@0 | 1008 | // to be right |
michael@0 | 1009 | static const char16_t space = 0xA0; |
michael@0 | 1010 | mDisplayContent->SetText(&space, 1, aNotify); |
michael@0 | 1011 | } else { |
michael@0 | 1012 | mDisplayContent->SetText(mDisplayedOptionText, aNotify); |
michael@0 | 1013 | } |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | int32_t |
michael@0 | 1017 | nsComboboxControlFrame::GetIndexOfDisplayArea() |
michael@0 | 1018 | { |
michael@0 | 1019 | return mDisplayedIndex; |
michael@0 | 1020 | } |
michael@0 | 1021 | |
michael@0 | 1022 | //---------------------------------------------------------------------- |
michael@0 | 1023 | // nsISelectControlFrame |
michael@0 | 1024 | //---------------------------------------------------------------------- |
michael@0 | 1025 | NS_IMETHODIMP |
michael@0 | 1026 | nsComboboxControlFrame::DoneAddingChildren(bool aIsDone) |
michael@0 | 1027 | { |
michael@0 | 1028 | nsISelectControlFrame* listFrame = do_QueryFrame(mDropdownFrame); |
michael@0 | 1029 | if (!listFrame) |
michael@0 | 1030 | return NS_ERROR_FAILURE; |
michael@0 | 1031 | |
michael@0 | 1032 | return listFrame->DoneAddingChildren(aIsDone); |
michael@0 | 1033 | } |
michael@0 | 1034 | |
michael@0 | 1035 | NS_IMETHODIMP |
michael@0 | 1036 | nsComboboxControlFrame::AddOption(int32_t aIndex) |
michael@0 | 1037 | { |
michael@0 | 1038 | if (aIndex <= mDisplayedIndex) { |
michael@0 | 1039 | ++mDisplayedIndex; |
michael@0 | 1040 | } |
michael@0 | 1041 | |
michael@0 | 1042 | nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); |
michael@0 | 1043 | return lcf->AddOption(aIndex); |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | |
michael@0 | 1047 | NS_IMETHODIMP |
michael@0 | 1048 | nsComboboxControlFrame::RemoveOption(int32_t aIndex) |
michael@0 | 1049 | { |
michael@0 | 1050 | nsWeakFrame weakThis(this); |
michael@0 | 1051 | if (mListControlFrame->GetNumberOfOptions() > 0) { |
michael@0 | 1052 | if (aIndex < mDisplayedIndex) { |
michael@0 | 1053 | --mDisplayedIndex; |
michael@0 | 1054 | } else if (aIndex == mDisplayedIndex) { |
michael@0 | 1055 | mDisplayedIndex = 0; // IE6 compat |
michael@0 | 1056 | RedisplayText(mDisplayedIndex); |
michael@0 | 1057 | } |
michael@0 | 1058 | } |
michael@0 | 1059 | else { |
michael@0 | 1060 | // If we removed the last option, we need to blank things out |
michael@0 | 1061 | RedisplayText(-1); |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | if (!weakThis.IsAlive()) |
michael@0 | 1065 | return NS_OK; |
michael@0 | 1066 | |
michael@0 | 1067 | nsListControlFrame* lcf = static_cast<nsListControlFrame*>(mDropdownFrame); |
michael@0 | 1068 | return lcf->RemoveOption(aIndex); |
michael@0 | 1069 | } |
michael@0 | 1070 | |
michael@0 | 1071 | NS_IMETHODIMP |
michael@0 | 1072 | nsComboboxControlFrame::OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) |
michael@0 | 1073 | { |
michael@0 | 1074 | nsAutoScriptBlocker scriptBlocker; |
michael@0 | 1075 | RedisplayText(aNewIndex); |
michael@0 | 1076 | NS_ASSERTION(mDropdownFrame, "No dropdown frame!"); |
michael@0 | 1077 | |
michael@0 | 1078 | nsISelectControlFrame* listFrame = do_QueryFrame(mDropdownFrame); |
michael@0 | 1079 | NS_ASSERTION(listFrame, "No list frame!"); |
michael@0 | 1080 | |
michael@0 | 1081 | return listFrame->OnSetSelectedIndex(aOldIndex, aNewIndex); |
michael@0 | 1082 | } |
michael@0 | 1083 | |
michael@0 | 1084 | // End nsISelectControlFrame |
michael@0 | 1085 | //---------------------------------------------------------------------- |
michael@0 | 1086 | |
michael@0 | 1087 | nsresult |
michael@0 | 1088 | nsComboboxControlFrame::HandleEvent(nsPresContext* aPresContext, |
michael@0 | 1089 | WidgetGUIEvent* aEvent, |
michael@0 | 1090 | nsEventStatus* aEventStatus) |
michael@0 | 1091 | { |
michael@0 | 1092 | NS_ENSURE_ARG_POINTER(aEventStatus); |
michael@0 | 1093 | |
michael@0 | 1094 | if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
michael@0 | 1095 | return NS_OK; |
michael@0 | 1096 | } |
michael@0 | 1097 | |
michael@0 | 1098 | EventStates eventStates = mContent->AsElement()->State(); |
michael@0 | 1099 | if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) { |
michael@0 | 1100 | return NS_OK; |
michael@0 | 1101 | } |
michael@0 | 1102 | |
michael@0 | 1103 | // If we have style that affects how we are selected, feed event down to |
michael@0 | 1104 | // nsFrame::HandleEvent so that selection takes place when appropriate. |
michael@0 | 1105 | const nsStyleUserInterface* uiStyle = StyleUserInterface(); |
michael@0 | 1106 | if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE || uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED) |
michael@0 | 1107 | return nsBlockFrame::HandleEvent(aPresContext, aEvent, aEventStatus); |
michael@0 | 1108 | |
michael@0 | 1109 | return NS_OK; |
michael@0 | 1110 | } |
michael@0 | 1111 | |
michael@0 | 1112 | |
michael@0 | 1113 | nsresult |
michael@0 | 1114 | nsComboboxControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue) |
michael@0 | 1115 | { |
michael@0 | 1116 | nsIFormControlFrame* fcFrame = do_QueryFrame(mDropdownFrame); |
michael@0 | 1117 | if (!fcFrame) { |
michael@0 | 1118 | return NS_NOINTERFACE; |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | return fcFrame->SetFormProperty(aName, aValue); |
michael@0 | 1122 | } |
michael@0 | 1123 | |
michael@0 | 1124 | nsIFrame* |
michael@0 | 1125 | nsComboboxControlFrame::GetContentInsertionFrame() { |
michael@0 | 1126 | return mInRedisplayText ? mDisplayFrame : mDropdownFrame->GetContentInsertionFrame(); |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | nsresult |
michael@0 | 1130 | nsComboboxControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) |
michael@0 | 1131 | { |
michael@0 | 1132 | // The frames used to display the combo box and the button used to popup the dropdown list |
michael@0 | 1133 | // are created through anonymous content. The dropdown list is not created through anonymous |
michael@0 | 1134 | // content because its frame is initialized specifically for the drop-down case and it is placed |
michael@0 | 1135 | // a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the |
michael@0 | 1136 | // layout of the display and button. |
michael@0 | 1137 | // |
michael@0 | 1138 | // Note: The value attribute of the display content is set when an item is selected in the dropdown list. |
michael@0 | 1139 | // If the content specified below does not honor the value attribute than nothing will be displayed. |
michael@0 | 1140 | |
michael@0 | 1141 | // For now the content that is created corresponds to two input buttons. It would be better to create the |
michael@0 | 1142 | // tag as something other than input, but then there isn't any way to create a button frame since it |
michael@0 | 1143 | // isn't possible to set the display type in CSS2 to create a button frame. |
michael@0 | 1144 | |
michael@0 | 1145 | // create content used for display |
michael@0 | 1146 | //nsIAtom* tag = NS_NewAtom("mozcombodisplay"); |
michael@0 | 1147 | |
michael@0 | 1148 | // Add a child text content node for the label |
michael@0 | 1149 | |
michael@0 | 1150 | nsNodeInfoManager *nimgr = mContent->NodeInfo()->NodeInfoManager(); |
michael@0 | 1151 | |
michael@0 | 1152 | mDisplayContent = new nsTextNode(nimgr); |
michael@0 | 1153 | |
michael@0 | 1154 | // set the value of the text node |
michael@0 | 1155 | mDisplayedIndex = mListControlFrame->GetSelectedIndex(); |
michael@0 | 1156 | if (mDisplayedIndex != -1) { |
michael@0 | 1157 | mListControlFrame->GetOptionText(mDisplayedIndex, mDisplayedOptionText); |
michael@0 | 1158 | } |
michael@0 | 1159 | ActuallyDisplayText(false); |
michael@0 | 1160 | |
michael@0 | 1161 | if (!aElements.AppendElement(mDisplayContent)) |
michael@0 | 1162 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1163 | |
michael@0 | 1164 | mButtonContent = mContent->OwnerDoc()->CreateHTMLElement(nsGkAtoms::button); |
michael@0 | 1165 | if (!mButtonContent) |
michael@0 | 1166 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1167 | |
michael@0 | 1168 | // make someone to listen to the button. If its pressed by someone like Accessibility |
michael@0 | 1169 | // then open or close the combo box. |
michael@0 | 1170 | mButtonListener = new nsComboButtonListener(this); |
michael@0 | 1171 | mButtonContent->AddEventListener(NS_LITERAL_STRING("click"), mButtonListener, |
michael@0 | 1172 | false, false); |
michael@0 | 1173 | |
michael@0 | 1174 | mButtonContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type, |
michael@0 | 1175 | NS_LITERAL_STRING("button"), false); |
michael@0 | 1176 | // Set tabindex="-1" so that the button is not tabbable |
michael@0 | 1177 | mButtonContent->SetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, |
michael@0 | 1178 | NS_LITERAL_STRING("-1"), false); |
michael@0 | 1179 | |
michael@0 | 1180 | if (!aElements.AppendElement(mButtonContent)) |
michael@0 | 1181 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 1182 | |
michael@0 | 1183 | return NS_OK; |
michael@0 | 1184 | } |
michael@0 | 1185 | |
michael@0 | 1186 | void |
michael@0 | 1187 | nsComboboxControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, |
michael@0 | 1188 | uint32_t aFilter) |
michael@0 | 1189 | { |
michael@0 | 1190 | aElements.MaybeAppendElement(mDisplayContent); |
michael@0 | 1191 | aElements.MaybeAppendElement(mButtonContent); |
michael@0 | 1192 | } |
michael@0 | 1193 | |
michael@0 | 1194 | // XXXbz this is a for-now hack. Now that display:inline-block works, |
michael@0 | 1195 | // need to revisit this. |
michael@0 | 1196 | class nsComboboxDisplayFrame : public nsBlockFrame { |
michael@0 | 1197 | public: |
michael@0 | 1198 | NS_DECL_FRAMEARENA_HELPERS |
michael@0 | 1199 | |
michael@0 | 1200 | nsComboboxDisplayFrame (nsStyleContext* aContext, |
michael@0 | 1201 | nsComboboxControlFrame* aComboBox) |
michael@0 | 1202 | : nsBlockFrame(aContext), |
michael@0 | 1203 | mComboBox(aComboBox) |
michael@0 | 1204 | {} |
michael@0 | 1205 | |
michael@0 | 1206 | // Need this so that line layout knows that this block's width |
michael@0 | 1207 | // depends on the available width. |
michael@0 | 1208 | virtual nsIAtom* GetType() const MOZ_OVERRIDE; |
michael@0 | 1209 | |
michael@0 | 1210 | virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE |
michael@0 | 1211 | { |
michael@0 | 1212 | return nsBlockFrame::IsFrameOfType(aFlags & |
michael@0 | 1213 | ~(nsIFrame::eReplacedContainsBlock)); |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | virtual nsresult Reflow(nsPresContext* aPresContext, |
michael@0 | 1217 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1218 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1219 | nsReflowStatus& aStatus) MOZ_OVERRIDE; |
michael@0 | 1220 | |
michael@0 | 1221 | virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 1222 | const nsRect& aDirtyRect, |
michael@0 | 1223 | const nsDisplayListSet& aLists) MOZ_OVERRIDE; |
michael@0 | 1224 | |
michael@0 | 1225 | protected: |
michael@0 | 1226 | nsComboboxControlFrame* mComboBox; |
michael@0 | 1227 | }; |
michael@0 | 1228 | |
michael@0 | 1229 | NS_IMPL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame) |
michael@0 | 1230 | |
michael@0 | 1231 | nsIAtom* |
michael@0 | 1232 | nsComboboxDisplayFrame::GetType() const |
michael@0 | 1233 | { |
michael@0 | 1234 | return nsGkAtoms::comboboxDisplayFrame; |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | nsresult |
michael@0 | 1238 | nsComboboxDisplayFrame::Reflow(nsPresContext* aPresContext, |
michael@0 | 1239 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 1240 | const nsHTMLReflowState& aReflowState, |
michael@0 | 1241 | nsReflowStatus& aStatus) |
michael@0 | 1242 | { |
michael@0 | 1243 | nsHTMLReflowState state(aReflowState); |
michael@0 | 1244 | if (state.ComputedHeight() == NS_INTRINSICSIZE) { |
michael@0 | 1245 | // Note that the only way we can have a computed height here is if the |
michael@0 | 1246 | // combobox had a specified height. If it didn't, size based on what our |
michael@0 | 1247 | // rows look like, for lack of anything better. |
michael@0 | 1248 | state.SetComputedHeight(mComboBox->mListControlFrame->GetHeightOfARow()); |
michael@0 | 1249 | } |
michael@0 | 1250 | nscoord computedWidth = mComboBox->mDisplayWidth - |
michael@0 | 1251 | state.ComputedPhysicalBorderPadding().LeftRight(); |
michael@0 | 1252 | if (computedWidth < 0) { |
michael@0 | 1253 | computedWidth = 0; |
michael@0 | 1254 | } |
michael@0 | 1255 | state.SetComputedWidth(computedWidth); |
michael@0 | 1256 | |
michael@0 | 1257 | return nsBlockFrame::Reflow(aPresContext, aDesiredSize, state, aStatus); |
michael@0 | 1258 | } |
michael@0 | 1259 | |
michael@0 | 1260 | void |
michael@0 | 1261 | nsComboboxDisplayFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 1262 | const nsRect& aDirtyRect, |
michael@0 | 1263 | const nsDisplayListSet& aLists) |
michael@0 | 1264 | { |
michael@0 | 1265 | nsDisplayListCollection set; |
michael@0 | 1266 | nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, set); |
michael@0 | 1267 | |
michael@0 | 1268 | // remove background items if parent frame is themed |
michael@0 | 1269 | if (mComboBox->IsThemed()) { |
michael@0 | 1270 | set.BorderBackground()->DeleteAll(); |
michael@0 | 1271 | } |
michael@0 | 1272 | |
michael@0 | 1273 | set.MoveTo(aLists); |
michael@0 | 1274 | } |
michael@0 | 1275 | |
michael@0 | 1276 | nsIFrame* |
michael@0 | 1277 | nsComboboxControlFrame::CreateFrameFor(nsIContent* aContent) |
michael@0 | 1278 | { |
michael@0 | 1279 | NS_PRECONDITION(nullptr != aContent, "null ptr"); |
michael@0 | 1280 | |
michael@0 | 1281 | NS_ASSERTION(mDisplayContent, "mDisplayContent can't be null!"); |
michael@0 | 1282 | |
michael@0 | 1283 | if (mDisplayContent != aContent) { |
michael@0 | 1284 | // We only handle the frames for mDisplayContent here |
michael@0 | 1285 | return nullptr; |
michael@0 | 1286 | } |
michael@0 | 1287 | |
michael@0 | 1288 | // Get PresShell |
michael@0 | 1289 | nsIPresShell *shell = PresContext()->PresShell(); |
michael@0 | 1290 | nsStyleSet *styleSet = shell->StyleSet(); |
michael@0 | 1291 | |
michael@0 | 1292 | // create the style contexts for the anonymous block frame and text frame |
michael@0 | 1293 | nsRefPtr<nsStyleContext> styleContext; |
michael@0 | 1294 | styleContext = styleSet-> |
michael@0 | 1295 | ResolveAnonymousBoxStyle(nsCSSAnonBoxes::mozDisplayComboboxControlFrame, |
michael@0 | 1296 | mStyleContext); |
michael@0 | 1297 | |
michael@0 | 1298 | nsRefPtr<nsStyleContext> textStyleContext; |
michael@0 | 1299 | textStyleContext = styleSet->ResolveStyleForNonElement(mStyleContext); |
michael@0 | 1300 | |
michael@0 | 1301 | // Start by creating our anonymous block frame |
michael@0 | 1302 | mDisplayFrame = new (shell) nsComboboxDisplayFrame(styleContext, this); |
michael@0 | 1303 | mDisplayFrame->Init(mContent, this, nullptr); |
michael@0 | 1304 | |
michael@0 | 1305 | // Create a text frame and put it inside the block frame |
michael@0 | 1306 | nsIFrame* textFrame = NS_NewTextFrame(shell, textStyleContext); |
michael@0 | 1307 | |
michael@0 | 1308 | // initialize the text frame |
michael@0 | 1309 | textFrame->Init(aContent, mDisplayFrame, nullptr); |
michael@0 | 1310 | mDisplayContent->SetPrimaryFrame(textFrame); |
michael@0 | 1311 | |
michael@0 | 1312 | nsFrameList textList(textFrame, textFrame); |
michael@0 | 1313 | mDisplayFrame->SetInitialChildList(kPrincipalList, textList); |
michael@0 | 1314 | return mDisplayFrame; |
michael@0 | 1315 | } |
michael@0 | 1316 | |
michael@0 | 1317 | void |
michael@0 | 1318 | nsComboboxControlFrame::DestroyFrom(nsIFrame* aDestructRoot) |
michael@0 | 1319 | { |
michael@0 | 1320 | // Revoke any pending RedisplayTextEvent |
michael@0 | 1321 | mRedisplayTextEvent.Revoke(); |
michael@0 | 1322 | |
michael@0 | 1323 | nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); |
michael@0 | 1324 | |
michael@0 | 1325 | if (mDroppedDown) { |
michael@0 | 1326 | MOZ_ASSERT(mDropdownFrame, "mDroppedDown without frame"); |
michael@0 | 1327 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 1328 | MOZ_ASSERT(view); |
michael@0 | 1329 | nsIWidget* widget = view->GetWidget(); |
michael@0 | 1330 | if (widget) { |
michael@0 | 1331 | widget->CaptureRollupEvents(this, false); |
michael@0 | 1332 | } |
michael@0 | 1333 | } |
michael@0 | 1334 | |
michael@0 | 1335 | // Cleanup frames in popup child list |
michael@0 | 1336 | mPopupFrames.DestroyFramesFrom(aDestructRoot); |
michael@0 | 1337 | nsContentUtils::DestroyAnonymousContent(&mDisplayContent); |
michael@0 | 1338 | nsContentUtils::DestroyAnonymousContent(&mButtonContent); |
michael@0 | 1339 | nsBlockFrame::DestroyFrom(aDestructRoot); |
michael@0 | 1340 | } |
michael@0 | 1341 | |
michael@0 | 1342 | const nsFrameList& |
michael@0 | 1343 | nsComboboxControlFrame::GetChildList(ChildListID aListID) const |
michael@0 | 1344 | { |
michael@0 | 1345 | if (kSelectPopupList == aListID) { |
michael@0 | 1346 | return mPopupFrames; |
michael@0 | 1347 | } |
michael@0 | 1348 | return nsBlockFrame::GetChildList(aListID); |
michael@0 | 1349 | } |
michael@0 | 1350 | |
michael@0 | 1351 | void |
michael@0 | 1352 | nsComboboxControlFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
michael@0 | 1353 | { |
michael@0 | 1354 | nsBlockFrame::GetChildLists(aLists); |
michael@0 | 1355 | mPopupFrames.AppendIfNonempty(aLists, kSelectPopupList); |
michael@0 | 1356 | } |
michael@0 | 1357 | |
michael@0 | 1358 | nsresult |
michael@0 | 1359 | nsComboboxControlFrame::SetInitialChildList(ChildListID aListID, |
michael@0 | 1360 | nsFrameList& aChildList) |
michael@0 | 1361 | { |
michael@0 | 1362 | nsresult rv = NS_OK; |
michael@0 | 1363 | if (kSelectPopupList == aListID) { |
michael@0 | 1364 | mPopupFrames.SetFrames(aChildList); |
michael@0 | 1365 | } else { |
michael@0 | 1366 | for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) { |
michael@0 | 1367 | nsCOMPtr<nsIFormControl> formControl = |
michael@0 | 1368 | do_QueryInterface(e.get()->GetContent()); |
michael@0 | 1369 | if (formControl && formControl->GetType() == NS_FORM_BUTTON_BUTTON) { |
michael@0 | 1370 | mButtonFrame = e.get(); |
michael@0 | 1371 | break; |
michael@0 | 1372 | } |
michael@0 | 1373 | } |
michael@0 | 1374 | NS_ASSERTION(mButtonFrame, "missing button frame in initial child list"); |
michael@0 | 1375 | rv = nsBlockFrame::SetInitialChildList(aListID, aChildList); |
michael@0 | 1376 | } |
michael@0 | 1377 | return rv; |
michael@0 | 1378 | } |
michael@0 | 1379 | |
michael@0 | 1380 | //---------------------------------------------------------------------- |
michael@0 | 1381 | //nsIRollupListener |
michael@0 | 1382 | //---------------------------------------------------------------------- |
michael@0 | 1383 | bool |
michael@0 | 1384 | nsComboboxControlFrame::Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) |
michael@0 | 1385 | { |
michael@0 | 1386 | if (!mDroppedDown) |
michael@0 | 1387 | return false; |
michael@0 | 1388 | |
michael@0 | 1389 | nsWeakFrame weakFrame(this); |
michael@0 | 1390 | mListControlFrame->AboutToRollup(); // might destroy us |
michael@0 | 1391 | if (!weakFrame.IsAlive()) |
michael@0 | 1392 | return true; |
michael@0 | 1393 | ShowDropDown(false); // might destroy us |
michael@0 | 1394 | if (weakFrame.IsAlive()) { |
michael@0 | 1395 | mListControlFrame->CaptureMouseEvents(false); |
michael@0 | 1396 | } |
michael@0 | 1397 | |
michael@0 | 1398 | return true; |
michael@0 | 1399 | } |
michael@0 | 1400 | |
michael@0 | 1401 | nsIWidget* |
michael@0 | 1402 | nsComboboxControlFrame::GetRollupWidget() |
michael@0 | 1403 | { |
michael@0 | 1404 | nsView* view = mDropdownFrame->GetView(); |
michael@0 | 1405 | MOZ_ASSERT(view); |
michael@0 | 1406 | return view->GetWidget(); |
michael@0 | 1407 | } |
michael@0 | 1408 | |
michael@0 | 1409 | void |
michael@0 | 1410 | nsComboboxControlFrame::RollupFromList() |
michael@0 | 1411 | { |
michael@0 | 1412 | if (ShowList(false)) |
michael@0 | 1413 | mListControlFrame->CaptureMouseEvents(false); |
michael@0 | 1414 | } |
michael@0 | 1415 | |
michael@0 | 1416 | int32_t |
michael@0 | 1417 | nsComboboxControlFrame::UpdateRecentIndex(int32_t aIndex) |
michael@0 | 1418 | { |
michael@0 | 1419 | int32_t index = mRecentSelectedIndex; |
michael@0 | 1420 | if (mRecentSelectedIndex == NS_SKIP_NOTIFY_INDEX || aIndex == NS_SKIP_NOTIFY_INDEX) |
michael@0 | 1421 | mRecentSelectedIndex = aIndex; |
michael@0 | 1422 | return index; |
michael@0 | 1423 | } |
michael@0 | 1424 | |
michael@0 | 1425 | class nsDisplayComboboxFocus : public nsDisplayItem { |
michael@0 | 1426 | public: |
michael@0 | 1427 | nsDisplayComboboxFocus(nsDisplayListBuilder* aBuilder, |
michael@0 | 1428 | nsComboboxControlFrame* aFrame) |
michael@0 | 1429 | : nsDisplayItem(aBuilder, aFrame) { |
michael@0 | 1430 | MOZ_COUNT_CTOR(nsDisplayComboboxFocus); |
michael@0 | 1431 | } |
michael@0 | 1432 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 1433 | virtual ~nsDisplayComboboxFocus() { |
michael@0 | 1434 | MOZ_COUNT_DTOR(nsDisplayComboboxFocus); |
michael@0 | 1435 | } |
michael@0 | 1436 | #endif |
michael@0 | 1437 | |
michael@0 | 1438 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1439 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 1440 | NS_DISPLAY_DECL_NAME("ComboboxFocus", TYPE_COMBOBOX_FOCUS) |
michael@0 | 1441 | }; |
michael@0 | 1442 | |
michael@0 | 1443 | void nsDisplayComboboxFocus::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 1444 | nsRenderingContext* aCtx) |
michael@0 | 1445 | { |
michael@0 | 1446 | static_cast<nsComboboxControlFrame*>(mFrame) |
michael@0 | 1447 | ->PaintFocus(*aCtx, ToReferenceFrame()); |
michael@0 | 1448 | } |
michael@0 | 1449 | |
michael@0 | 1450 | void |
michael@0 | 1451 | nsComboboxControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 1452 | const nsRect& aDirtyRect, |
michael@0 | 1453 | const nsDisplayListSet& aLists) |
michael@0 | 1454 | { |
michael@0 | 1455 | #ifdef NOISY |
michael@0 | 1456 | printf("%p paint at (%d, %d, %d, %d)\n", this, |
michael@0 | 1457 | aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); |
michael@0 | 1458 | #endif |
michael@0 | 1459 | |
michael@0 | 1460 | if (aBuilder->IsForEventDelivery()) { |
michael@0 | 1461 | // Don't allow children to receive events. |
michael@0 | 1462 | // REVIEW: following old GetFrameForPoint |
michael@0 | 1463 | DisplayBorderBackgroundOutline(aBuilder, aLists); |
michael@0 | 1464 | } else { |
michael@0 | 1465 | // REVIEW: Our in-flow child frames are inline-level so they will paint in our |
michael@0 | 1466 | // content list, so we don't need to mess with layers. |
michael@0 | 1467 | nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); |
michael@0 | 1468 | } |
michael@0 | 1469 | |
michael@0 | 1470 | // draw a focus indicator only when focus rings should be drawn |
michael@0 | 1471 | nsIDocument* doc = mContent->GetCurrentDoc(); |
michael@0 | 1472 | if (doc) { |
michael@0 | 1473 | nsPIDOMWindow* window = doc->GetWindow(); |
michael@0 | 1474 | if (window && window->ShouldShowFocusRing()) { |
michael@0 | 1475 | nsPresContext *presContext = PresContext(); |
michael@0 | 1476 | const nsStyleDisplay *disp = StyleDisplay(); |
michael@0 | 1477 | if ((!IsThemed(disp) || |
michael@0 | 1478 | !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) && |
michael@0 | 1479 | mDisplayFrame && IsVisibleForPainting(aBuilder)) { |
michael@0 | 1480 | aLists.Content()->AppendNewToTop( |
michael@0 | 1481 | new (aBuilder) nsDisplayComboboxFocus(aBuilder, this)); |
michael@0 | 1482 | } |
michael@0 | 1483 | } |
michael@0 | 1484 | } |
michael@0 | 1485 | |
michael@0 | 1486 | DisplaySelectionOverlay(aBuilder, aLists.Content()); |
michael@0 | 1487 | } |
michael@0 | 1488 | |
michael@0 | 1489 | void nsComboboxControlFrame::PaintFocus(nsRenderingContext& aRenderingContext, |
michael@0 | 1490 | nsPoint aPt) |
michael@0 | 1491 | { |
michael@0 | 1492 | /* Do we need to do anything? */ |
michael@0 | 1493 | EventStates eventStates = mContent->AsElement()->State(); |
michael@0 | 1494 | if (eventStates.HasState(NS_EVENT_STATE_DISABLED) || sFocused != this) |
michael@0 | 1495 | return; |
michael@0 | 1496 | |
michael@0 | 1497 | aRenderingContext.PushState(); |
michael@0 | 1498 | nsRect clipRect = mDisplayFrame->GetRect() + aPt; |
michael@0 | 1499 | aRenderingContext.IntersectClip(clipRect); |
michael@0 | 1500 | |
michael@0 | 1501 | // REVIEW: Why does the old code paint mDisplayFrame again? We've |
michael@0 | 1502 | // already painted it in the children above. So clipping it here won't do |
michael@0 | 1503 | // us much good. |
michael@0 | 1504 | |
michael@0 | 1505 | ///////////////////// |
michael@0 | 1506 | // draw focus |
michael@0 | 1507 | |
michael@0 | 1508 | aRenderingContext.SetLineStyle(nsLineStyle_kDotted); |
michael@0 | 1509 | aRenderingContext.SetColor(StyleColor()->mColor); |
michael@0 | 1510 | |
michael@0 | 1511 | //aRenderingContext.DrawRect(clipRect); |
michael@0 | 1512 | |
michael@0 | 1513 | nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
michael@0 | 1514 | clipRect.width -= onePixel; |
michael@0 | 1515 | clipRect.height -= onePixel; |
michael@0 | 1516 | aRenderingContext.DrawLine(clipRect.TopLeft(), clipRect.TopRight()); |
michael@0 | 1517 | aRenderingContext.DrawLine(clipRect.TopRight(), clipRect.BottomRight()); |
michael@0 | 1518 | aRenderingContext.DrawLine(clipRect.BottomRight(), clipRect.BottomLeft()); |
michael@0 | 1519 | aRenderingContext.DrawLine(clipRect.BottomLeft(), clipRect.TopLeft()); |
michael@0 | 1520 | |
michael@0 | 1521 | aRenderingContext.PopState(); |
michael@0 | 1522 | } |
michael@0 | 1523 | |
michael@0 | 1524 | //--------------------------------------------------------- |
michael@0 | 1525 | // gets the content (an option) by index and then set it as |
michael@0 | 1526 | // being selected or not selected |
michael@0 | 1527 | //--------------------------------------------------------- |
michael@0 | 1528 | NS_IMETHODIMP |
michael@0 | 1529 | nsComboboxControlFrame::OnOptionSelected(int32_t aIndex, bool aSelected) |
michael@0 | 1530 | { |
michael@0 | 1531 | if (mDroppedDown) { |
michael@0 | 1532 | nsISelectControlFrame *selectFrame = do_QueryFrame(mListControlFrame); |
michael@0 | 1533 | if (selectFrame) { |
michael@0 | 1534 | selectFrame->OnOptionSelected(aIndex, aSelected); |
michael@0 | 1535 | } |
michael@0 | 1536 | } else { |
michael@0 | 1537 | if (aSelected) { |
michael@0 | 1538 | nsAutoScriptBlocker blocker; |
michael@0 | 1539 | RedisplayText(aIndex); |
michael@0 | 1540 | } else { |
michael@0 | 1541 | nsWeakFrame weakFrame(this); |
michael@0 | 1542 | RedisplaySelectedText(); |
michael@0 | 1543 | if (weakFrame.IsAlive()) { |
michael@0 | 1544 | FireValueChangeEvent(); // Fire after old option is unselected |
michael@0 | 1545 | } |
michael@0 | 1546 | } |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | return NS_OK; |
michael@0 | 1550 | } |
michael@0 | 1551 | |
michael@0 | 1552 | void nsComboboxControlFrame::FireValueChangeEvent() |
michael@0 | 1553 | { |
michael@0 | 1554 | // Fire ValueChange event to indicate data value of combo box has changed |
michael@0 | 1555 | nsContentUtils::AddScriptRunner( |
michael@0 | 1556 | new AsyncEventDispatcher(mContent, NS_LITERAL_STRING("ValueChange"), true, |
michael@0 | 1557 | false)); |
michael@0 | 1558 | } |
michael@0 | 1559 | |
michael@0 | 1560 | void |
michael@0 | 1561 | nsComboboxControlFrame::OnContentReset() |
michael@0 | 1562 | { |
michael@0 | 1563 | if (mListControlFrame) { |
michael@0 | 1564 | mListControlFrame->OnContentReset(); |
michael@0 | 1565 | } |
michael@0 | 1566 | } |
michael@0 | 1567 | |
michael@0 | 1568 | |
michael@0 | 1569 | //-------------------------------------------------------- |
michael@0 | 1570 | // nsIStatefulFrame |
michael@0 | 1571 | //-------------------------------------------------------- |
michael@0 | 1572 | NS_IMETHODIMP |
michael@0 | 1573 | nsComboboxControlFrame::SaveState(nsPresState** aState) |
michael@0 | 1574 | { |
michael@0 | 1575 | if (!mListControlFrame) |
michael@0 | 1576 | return NS_ERROR_FAILURE; |
michael@0 | 1577 | |
michael@0 | 1578 | nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame); |
michael@0 | 1579 | return stateful->SaveState(aState); |
michael@0 | 1580 | } |
michael@0 | 1581 | |
michael@0 | 1582 | NS_IMETHODIMP |
michael@0 | 1583 | nsComboboxControlFrame::RestoreState(nsPresState* aState) |
michael@0 | 1584 | { |
michael@0 | 1585 | if (!mListControlFrame) |
michael@0 | 1586 | return NS_ERROR_FAILURE; |
michael@0 | 1587 | |
michael@0 | 1588 | nsIStatefulFrame* stateful = do_QueryFrame(mListControlFrame); |
michael@0 | 1589 | NS_ASSERTION(stateful, "Must implement nsIStatefulFrame"); |
michael@0 | 1590 | return stateful->RestoreState(aState); |
michael@0 | 1591 | } |
michael@0 | 1592 | |
michael@0 | 1593 | |
michael@0 | 1594 | // |
michael@0 | 1595 | // Camino uses a native widget for the combobox |
michael@0 | 1596 | // popup, which affects drawing and event |
michael@0 | 1597 | // handling here and in nsListControlFrame. |
michael@0 | 1598 | // |
michael@0 | 1599 | // Also, Fennec use a custom combobox built-in widget |
michael@0 | 1600 | // |
michael@0 | 1601 | |
michael@0 | 1602 | /* static */ |
michael@0 | 1603 | bool |
michael@0 | 1604 | nsComboboxControlFrame::ToolkitHasNativePopup() |
michael@0 | 1605 | { |
michael@0 | 1606 | #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS |
michael@0 | 1607 | return true; |
michael@0 | 1608 | #else |
michael@0 | 1609 | return false; |
michael@0 | 1610 | #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */ |
michael@0 | 1611 | } |
michael@0 | 1612 |