/src/mozilla-central/layout/forms/nsListControlFrame.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | #ifndef nsListControlFrame_h___ |
7 | | #define nsListControlFrame_h___ |
8 | | |
9 | | #ifdef DEBUG_evaughan |
10 | | //#define DEBUG_rods |
11 | | #endif |
12 | | |
13 | | #ifdef DEBUG_rods |
14 | | //#define DO_REFLOW_DEBUG |
15 | | //#define DO_REFLOW_COUNTER |
16 | | //#define DO_UNCONSTRAINED_CHECK |
17 | | //#define DO_PIXELS |
18 | | #endif |
19 | | |
20 | | #include "mozilla/Attributes.h" |
21 | | #include "nsGfxScrollFrame.h" |
22 | | #include "nsIFormControlFrame.h" |
23 | | #include "nsIListControlFrame.h" |
24 | | #include "nsISelectControlFrame.h" |
25 | | #include "nsSelectsAreaFrame.h" |
26 | | |
27 | | // X.h defines KeyPress |
28 | | #ifdef KeyPress |
29 | | #undef KeyPress |
30 | | #endif |
31 | | |
32 | | class nsIComboboxControlFrame; |
33 | | class nsPresContext; |
34 | | class nsListEventListener; |
35 | | |
36 | | namespace mozilla { |
37 | | namespace dom { |
38 | | class Event; |
39 | | class HTMLOptionElement; |
40 | | class HTMLOptionsCollection; |
41 | | } // namespace dom |
42 | | } // namespace mozilla |
43 | | |
44 | | /** |
45 | | * Frame-based listbox. |
46 | | */ |
47 | | |
48 | | class nsListControlFrame final : public nsHTMLScrollFrame, |
49 | | public nsIFormControlFrame, |
50 | | public nsIListControlFrame, |
51 | | public nsISelectControlFrame |
52 | | { |
53 | | public: |
54 | | typedef mozilla::dom::HTMLOptionElement HTMLOptionElement; |
55 | | |
56 | | friend nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell, |
57 | | ComputedStyle* aStyle); |
58 | | |
59 | | NS_DECL_QUERYFRAME |
60 | | NS_DECL_FRAMEARENA_HELPERS(nsListControlFrame) |
61 | | |
62 | | // nsIFrame |
63 | | virtual nsresult HandleEvent(nsPresContext* aPresContext, |
64 | | mozilla::WidgetGUIEvent* aEvent, |
65 | | nsEventStatus* aEventStatus) override; |
66 | | |
67 | | virtual void SetInitialChildList(ChildListID aListID, |
68 | | nsFrameList& aChildList) override; |
69 | | |
70 | | virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override; |
71 | | virtual nscoord GetMinISize(gfxContext *aRenderingContext) override; |
72 | | |
73 | | virtual void Reflow(nsPresContext* aCX, |
74 | | ReflowOutput& aDesiredSize, |
75 | | const ReflowInput& aReflowInput, |
76 | | nsReflowStatus& aStatus) override; |
77 | | |
78 | | virtual void Init(nsIContent* aContent, |
79 | | nsContainerFrame* aParent, |
80 | | nsIFrame* aPrevInFlow) override; |
81 | | |
82 | | virtual void DidReflow(nsPresContext* aPresContext, |
83 | | const ReflowInput* aReflowInput) override; |
84 | | virtual void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) override; |
85 | | |
86 | | virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
87 | | const nsDisplayListSet& aLists) override; |
88 | | |
89 | | virtual nsContainerFrame* GetContentInsertionFrame() override; |
90 | | |
91 | | virtual bool IsFrameOfType(uint32_t aFlags) const override |
92 | 0 | { |
93 | 0 | return nsHTMLScrollFrame::IsFrameOfType(aFlags & |
94 | 0 | ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); |
95 | 0 | } |
96 | | |
97 | | #ifdef DEBUG_FRAME_DUMP |
98 | | virtual nsresult GetFrameName(nsAString& aResult) const override; |
99 | | #endif |
100 | | |
101 | | // nsIFormControlFrame |
102 | | virtual nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override; |
103 | | virtual void SetFocus(bool aOn = true, bool aRepaint = false) override; |
104 | | |
105 | | virtual mozilla::ScrollStyles GetScrollStyles() const override; |
106 | | virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override; |
107 | | |
108 | | // for accessibility purposes |
109 | | #ifdef ACCESSIBILITY |
110 | | virtual mozilla::a11y::AccType AccessibleType() override; |
111 | | #endif |
112 | | |
113 | | // nsIListControlFrame |
114 | | virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) override; |
115 | | virtual int32_t GetSelectedIndex() override; |
116 | | virtual HTMLOptionElement* GetCurrentOption() override; |
117 | | |
118 | | /** |
119 | | * Gets the text of the currently selected item. |
120 | | * If the there are zero items then an empty string is returned |
121 | | * If there is nothing selected, then the 0th item's text is returned. |
122 | | */ |
123 | | virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) override; |
124 | | |
125 | | virtual void CaptureMouseEvents(bool aGrabMouseEvents) override; |
126 | | virtual nscoord GetBSizeOfARow() override; |
127 | | virtual uint32_t GetNumberOfOptions() override; |
128 | | virtual void AboutToDropDown() override; |
129 | | |
130 | | /** |
131 | | * @note This method might destroy the frame, pres shell and other objects. |
132 | | */ |
133 | | virtual void AboutToRollup() override; |
134 | | |
135 | | /** |
136 | | * Dispatch a DOM oninput and onchange event synchroniously. |
137 | | * @note This method might destroy the frame, pres shell and other objects. |
138 | | */ |
139 | | virtual void FireOnInputAndOnChange() override; |
140 | | |
141 | | /** |
142 | | * Makes aIndex the selected option of a combobox list. |
143 | | * @note This method might destroy the frame, pres shell and other objects. |
144 | | */ |
145 | | virtual void ComboboxFinish(int32_t aIndex) override; |
146 | | virtual void OnContentReset() override; |
147 | | |
148 | | // nsISelectControlFrame |
149 | | NS_IMETHOD AddOption(int32_t index) override; |
150 | | NS_IMETHOD RemoveOption(int32_t index) override; |
151 | | NS_IMETHOD DoneAddingChildren(bool aIsDone) override; |
152 | | |
153 | | /** |
154 | | * Gets the content (an option) by index and then set it as |
155 | | * being selected or not selected. |
156 | | */ |
157 | | NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override; |
158 | | NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override; |
159 | | |
160 | | /** |
161 | | * Mouse event listeners. |
162 | | * @note These methods might destroy the frame, pres shell and other objects. |
163 | | */ |
164 | | nsresult MouseDown(mozilla::dom::Event* aMouseEvent); |
165 | | nsresult MouseUp(mozilla::dom::Event* aMouseEvent); |
166 | | nsresult MouseMove(mozilla::dom::Event* aMouseEvent); |
167 | | nsresult DragMove(mozilla::dom::Event* aMouseEvent); |
168 | | nsresult KeyDown(mozilla::dom::Event* aKeyEvent); |
169 | | nsresult KeyPress(mozilla::dom::Event* aKeyEvent); |
170 | | |
171 | | /** |
172 | | * Returns the options collection for mContent, if any. |
173 | | */ |
174 | | mozilla::dom::HTMLOptionsCollection* GetOptions() const; |
175 | | /** |
176 | | * Returns the HTMLOptionElement for a given index in mContent's collection. |
177 | | */ |
178 | | HTMLOptionElement* GetOption(uint32_t aIndex) const; |
179 | | |
180 | | static void ComboboxFocusSet(); |
181 | | |
182 | | // Helper |
183 | 0 | bool IsFocused() { return this == mFocused; } |
184 | | |
185 | | /** |
186 | | * Function to paint the focus rect when our nsSelectsAreaFrame is painting. |
187 | | * @param aPt the offset of this frame, relative to the rendering reference |
188 | | * frame |
189 | | */ |
190 | | void PaintFocus(mozilla::gfx::DrawTarget* aDrawTarget, nsPoint aPt); |
191 | | |
192 | | /** |
193 | | * If this frame IsFocused(), invalidates an area that includes anything |
194 | | * that PaintFocus will or could have painted --- basically the whole |
195 | | * GetOptionsContainer, plus some extra stuff if there are no options. This |
196 | | * must be called every time mEndSelectionIndex changes. |
197 | | */ |
198 | | void InvalidateFocus(); |
199 | | |
200 | | /** |
201 | | * Function to calculate the block size of a row, for use with the |
202 | | * "size" attribute. |
203 | | * Can't be const because GetNumberOfOptions() isn't const. |
204 | | */ |
205 | | nscoord CalcBSizeOfARow(); |
206 | | |
207 | | /** |
208 | | * Function to ask whether we're currently in what might be the |
209 | | * first pass of a two-pass reflow. |
210 | | */ |
211 | 0 | bool MightNeedSecondPass() const { |
212 | 0 | return mMightNeedSecondPass; |
213 | 0 | } |
214 | | |
215 | 0 | void SetSuppressScrollbarUpdate(bool aSuppress) { |
216 | 0 | nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress); |
217 | 0 | } |
218 | | |
219 | | /** |
220 | | * Return whether the list is in dropdown mode. |
221 | | */ |
222 | | bool IsInDropDownMode() const; |
223 | | |
224 | | /** |
225 | | * Return the number of displayed rows in the list. |
226 | | */ |
227 | 0 | uint32_t GetNumDisplayRows() const { return mNumDisplayRows; } |
228 | | |
229 | | /** |
230 | | * Return true if the drop-down list can display more rows. |
231 | | * (always false if not in drop-down mode) |
232 | | */ |
233 | 0 | bool GetDropdownCanGrow() const { return mDropdownCanGrow; } |
234 | | |
235 | | /** |
236 | | * Frees statics owned by this class. |
237 | | */ |
238 | | static void Shutdown(); |
239 | | |
240 | | #ifdef ACCESSIBILITY |
241 | | /** |
242 | | * Post a custom DOM event for the change, so that accessibility can |
243 | | * fire a native focus event for accessibility |
244 | | * (Some 3rd party products need to track our focus) |
245 | | */ |
246 | | void FireMenuItemActiveEvent(); // Inform assistive tech what got focused |
247 | | #endif |
248 | | |
249 | | protected: |
250 | | /** |
251 | | * Return the first non-disabled option starting at aFromIndex (inclusive). |
252 | | * @param aFoundIndex if non-null, set to the index of the returned option |
253 | | */ |
254 | | HTMLOptionElement* GetNonDisabledOptionFrom(int32_t aFromIndex, |
255 | | int32_t* aFoundIndex = nullptr); |
256 | | |
257 | | /** |
258 | | * Updates the selected text in a combobox and then calls FireOnChange(). |
259 | | * @note This method might destroy the frame, pres shell and other objects. |
260 | | * Returns false if calling it destroyed |this|. |
261 | | */ |
262 | | bool UpdateSelection(); |
263 | | |
264 | | /** |
265 | | * Returns whether mContent supports multiple selection. |
266 | | */ |
267 | 0 | bool GetMultiple() const { |
268 | 0 | return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple); |
269 | 0 | } |
270 | | |
271 | | |
272 | | /** |
273 | | * Toggles (show/hide) the combobox dropdown menu. |
274 | | * @note This method might destroy the frame, pres shell and other objects. |
275 | | */ |
276 | | void DropDownToggleKey(mozilla::dom::Event* aKeyEvent); |
277 | | |
278 | | nsresult IsOptionDisabled(int32_t anIndex, bool &aIsDisabled); |
279 | | /** |
280 | | * @note This method might destroy the frame, pres shell and other objects. |
281 | | */ |
282 | | void ScrollToFrame(HTMLOptionElement& aOptElement); |
283 | | /** |
284 | | * @note This method might destroy the frame, pres shell and other objects. |
285 | | */ |
286 | | void ScrollToIndex(int32_t anIndex); |
287 | | |
288 | | /** |
289 | | * When the user clicks on the comboboxframe to show the dropdown |
290 | | * listbox, they then have to move the mouse into the list. We don't |
291 | | * want to process those mouse events as selection events (i.e., to |
292 | | * scroll list items into view). So we ignore the events until |
293 | | * the mouse moves below our border-inner-edge, when |
294 | | * mItemSelectionStarted is set. |
295 | | * |
296 | | * @param aPoint relative to this frame |
297 | | */ |
298 | | bool IgnoreMouseEventForSelection(mozilla::dom::Event* aEvent); |
299 | | |
300 | | /** |
301 | | * If the dropdown is showing and the mouse has moved below our |
302 | | * border-inner-edge, then set mItemSelectionStarted. |
303 | | */ |
304 | | void UpdateInListState(mozilla::dom::Event* aEvent); |
305 | | void AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex, |
306 | | int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext); |
307 | | |
308 | | /** |
309 | | * Resets the select back to it's original default values; |
310 | | * those values as determined by the original HTML |
311 | | */ |
312 | | virtual void ResetList(bool aAllowScrolling); |
313 | | |
314 | | explicit nsListControlFrame(ComputedStyle* aStyle); |
315 | | virtual ~nsListControlFrame(); |
316 | | |
317 | | /** |
318 | | * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what |
319 | | * item was selected using content |
320 | | * @param aPoint the event point, in listcontrolframe coordinates |
321 | | * @return NS_OK if it successfully found the selection |
322 | | */ |
323 | | nsresult GetIndexFromDOMEvent(mozilla::dom::Event* aMouseEvent, |
324 | | int32_t& aCurIndex); |
325 | | |
326 | | bool CheckIfAllFramesHere(); |
327 | | bool IsLeftButton(mozilla::dom::Event* aMouseEvent); |
328 | | |
329 | | // guess at a row block size based on our own style. |
330 | | nscoord CalcFallbackRowBSize(float aFontSizeInflation); |
331 | | |
332 | | // CalcIntrinsicBSize computes our intrinsic block size (taking the |
333 | | // "size" attribute into account). This should only be called in |
334 | | // non-dropdown mode. |
335 | | nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions); |
336 | | |
337 | | // Dropped down stuff |
338 | | void SetComboboxItem(int32_t aIndex); |
339 | | |
340 | | /** |
341 | | * Method to reflow ourselves as a dropdown list. This differs from |
342 | | * reflow as a listbox because the criteria for needing a second |
343 | | * pass are different. This will be called from Reflow() as needed. |
344 | | */ |
345 | | void ReflowAsDropdown(nsPresContext* aPresContext, |
346 | | ReflowOutput& aDesiredSize, |
347 | | const ReflowInput& aReflowInput, |
348 | | nsReflowStatus& aStatus); |
349 | | |
350 | | // Selection |
351 | | bool SetOptionsSelectedFromFrame(int32_t aStartIndex, |
352 | | int32_t aEndIndex, |
353 | | bool aValue, |
354 | | bool aClearAll); |
355 | | bool ToggleOptionSelectedFromFrame(int32_t aIndex); |
356 | | /** |
357 | | * @note This method might destroy the frame, pres shell and other objects. |
358 | | */ |
359 | | bool SingleSelection(int32_t aClickedIndex, bool aDoToggle); |
360 | | bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex, |
361 | | bool aClearAll); |
362 | | /** |
363 | | * @note This method might destroy the frame, pres shell and other objects. |
364 | | */ |
365 | | bool PerformSelection(int32_t aClickedIndex, bool aIsShift, |
366 | | bool aIsControl); |
367 | | /** |
368 | | * @note This method might destroy the frame, pres shell and other objects. |
369 | | */ |
370 | | bool HandleListSelection(mozilla::dom::Event * aDOMEvent, |
371 | | int32_t selectedIndex); |
372 | | void InitSelectionRange(int32_t aClickedIndex); |
373 | | void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode, |
374 | | bool aIsShift, bool aIsControlOrMeta); |
375 | | |
376 | | public: |
377 | 0 | nsSelectsAreaFrame* GetOptionsContainer() const { |
378 | 0 | return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame()); |
379 | 0 | } |
380 | | |
381 | | protected: |
382 | 0 | nscoord BSizeOfARow() { |
383 | 0 | return GetOptionsContainer()->BSizeOfARow(); |
384 | 0 | } |
385 | | |
386 | | /** |
387 | | * @return how many displayable options/optgroups this frame has. |
388 | | */ |
389 | | uint32_t GetNumberOfRows(); |
390 | | |
391 | 0 | nsView* GetViewInternal() const override { return mView; } |
392 | 0 | void SetViewInternal(nsView* aView) override { mView = aView; } |
393 | | |
394 | | // Data Members |
395 | | int32_t mStartSelectionIndex; |
396 | | int32_t mEndSelectionIndex; |
397 | | |
398 | | nsIComboboxControlFrame* mComboboxFrame; |
399 | | |
400 | | // The view is only created (& non-null) if IsInDropDownMode() is true. |
401 | | nsView* mView; |
402 | | |
403 | | uint32_t mNumDisplayRows; |
404 | | bool mChangesSinceDragStart:1; |
405 | | bool mButtonDown:1; |
406 | | |
407 | | // Has the user selected a visible item since we showed the dropdown? |
408 | | bool mItemSelectionStarted:1; |
409 | | |
410 | | bool mIsAllContentHere:1; |
411 | | bool mIsAllFramesHere:1; |
412 | | bool mHasBeenInitialized:1; |
413 | | bool mNeedToReset:1; |
414 | | bool mPostChildrenLoadedReset:1; |
415 | | |
416 | | //bool value for multiple discontiguous selection |
417 | | bool mControlSelectMode:1; |
418 | | |
419 | | // True if we're in the middle of a reflow and might need a second |
420 | | // pass. This only happens for auto heights. |
421 | | bool mMightNeedSecondPass:1; |
422 | | |
423 | | /** |
424 | | * Set to aPresContext->HasPendingInterrupt() at the start of Reflow. |
425 | | * Set to false at the end of DidReflow. |
426 | | */ |
427 | | bool mHasPendingInterruptAtStartOfReflow:1; |
428 | | |
429 | | // True if the drop-down can show more rows. Always false if this list |
430 | | // is not in drop-down mode. |
431 | | bool mDropdownCanGrow:1; |
432 | | |
433 | | // True if the selection can be set to nothing or disabled options. |
434 | | bool mForceSelection:1; |
435 | | |
436 | | // The last computed block size we reflowed at if we're a combobox |
437 | | // dropdown. |
438 | | // XXXbz should we be using a subclass here? Or just not worry |
439 | | // about the extra member on listboxes? |
440 | | nscoord mLastDropdownComputedBSize; |
441 | | |
442 | | // At the time of our last dropdown, the backstop color to draw in case we |
443 | | // are translucent. |
444 | | nscolor mLastDropdownBackstopColor; |
445 | | |
446 | | RefPtr<nsListEventListener> mEventListener; |
447 | | |
448 | | static nsListControlFrame * mFocused; |
449 | | static nsString * sIncrementalString; |
450 | | |
451 | | #ifdef DO_REFLOW_COUNTER |
452 | | int32_t mReflowId; |
453 | | #endif |
454 | | |
455 | | private: |
456 | | // for incremental typing navigation |
457 | | static nsAString& GetIncrementalString (); |
458 | | static DOMTimeStamp gLastKeyTime; |
459 | | |
460 | | class MOZ_RAII AutoIncrementalSearchResetter |
461 | | { |
462 | | public: |
463 | | AutoIncrementalSearchResetter() : |
464 | | mCancelled(false) |
465 | 0 | { |
466 | 0 | } |
467 | | ~AutoIncrementalSearchResetter() |
468 | 0 | { |
469 | 0 | if (!mCancelled) { |
470 | 0 | nsListControlFrame::GetIncrementalString().Truncate(); |
471 | 0 | } |
472 | 0 | } |
473 | | void Cancel() |
474 | 0 | { |
475 | 0 | mCancelled = true; |
476 | 0 | } |
477 | | private: |
478 | | bool mCancelled; |
479 | | }; |
480 | | }; |
481 | | |
482 | | #endif /* nsListControlFrame_h___ */ |
483 | | |