Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 : #ifndef nsListControlFrame_h___
6 : #define nsListControlFrame_h___
7 :
8 : #ifdef DEBUG_evaughan
9 : //#define DEBUG_rods
10 : #endif
11 :
12 : #ifdef DEBUG_rods
13 : //#define DO_REFLOW_DEBUG
14 : //#define DO_REFLOW_COUNTER
15 : //#define DO_UNCONSTRAINED_CHECK
16 : //#define DO_PIXELS
17 : #endif
18 :
19 : #include "mozilla/Attributes.h"
20 : #include "nsGfxScrollFrame.h"
21 : #include "nsIFormControlFrame.h"
22 : #include "nsIListControlFrame.h"
23 : #include "nsISelectControlFrame.h"
24 : #include "nsSelectsAreaFrame.h"
25 :
26 : // X.h defines KeyPress
27 : #ifdef KeyPress
28 : #undef KeyPress
29 : #endif
30 :
31 : class nsIComboboxControlFrame;
32 : class nsPresContext;
33 : class nsListEventListener;
34 :
35 : namespace mozilla {
36 : namespace dom {
37 : class HTMLOptionElement;
38 : class HTMLOptionsCollection;
39 : } // namespace dom
40 : } // namespace mozilla
41 :
42 : /**
43 : * Frame-based listbox.
44 : */
45 :
46 : class nsListControlFrame final : public nsHTMLScrollFrame,
47 : public nsIFormControlFrame,
48 : public nsIListControlFrame,
49 : public nsISelectControlFrame
50 : {
51 : public:
52 : typedef mozilla::dom::HTMLOptionElement HTMLOptionElement;
53 :
54 : friend nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell,
55 : nsStyleContext* aContext);
56 :
57 : NS_DECL_QUERYFRAME
58 0 : NS_DECL_FRAMEARENA_HELPERS(nsListControlFrame)
59 :
60 : // nsIFrame
61 : virtual nsresult HandleEvent(nsPresContext* aPresContext,
62 : mozilla::WidgetGUIEvent* aEvent,
63 : nsEventStatus* aEventStatus) override;
64 :
65 : virtual void SetInitialChildList(ChildListID aListID,
66 : nsFrameList& aChildList) override;
67 :
68 : virtual nscoord GetPrefISize(gfxContext *aRenderingContext) override;
69 : virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
70 :
71 : virtual void Reflow(nsPresContext* aCX,
72 : ReflowOutput& aDesiredSize,
73 : const ReflowInput& aReflowInput,
74 : nsReflowStatus& aStatus) override;
75 :
76 : virtual void Init(nsIContent* aContent,
77 : nsContainerFrame* aParent,
78 : nsIFrame* aPrevInFlow) override;
79 :
80 : virtual void DidReflow(nsPresContext* aPresContext,
81 : const ReflowInput* aReflowInput,
82 : nsDidReflowStatus aStatus) override;
83 : virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
84 :
85 : virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
86 : const nsRect& aDirtyRect,
87 : const nsDisplayListSet& aLists) override;
88 :
89 : virtual nsContainerFrame* GetContentInsertionFrame() override;
90 :
91 0 : virtual bool IsFrameOfType(uint32_t aFlags) const override
92 : {
93 0 : return nsHTMLScrollFrame::IsFrameOfType(aFlags &
94 0 : ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
95 : }
96 :
97 : #ifdef DEBUG_FRAME_DUMP
98 : virtual nsresult GetFrameName(nsAString& aResult) const override;
99 : #endif
100 :
101 : // nsIFormControlFrame
102 : virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
103 : virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
104 :
105 : virtual mozilla::ScrollbarStyles GetScrollbarStyles() 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(nsIDOMEvent* aMouseEvent);
165 : nsresult MouseUp(nsIDOMEvent* aMouseEvent);
166 : nsresult MouseMove(nsIDOMEvent* aMouseEvent);
167 : nsresult DragMove(nsIDOMEvent* aMouseEvent);
168 : nsresult KeyDown(nsIDOMEvent* aKeyEvent);
169 : nsresult KeyPress(nsIDOMEvent* 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 : }
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->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
269 : }
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(nsIDOMEvent* 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(nsIDOMEvent* 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(nsIDOMEvent* 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(nsStyleContext* aContext);
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(nsIDOMEvent* aMouseEvent, int32_t& aCurIndex);
324 :
325 : bool CheckIfAllFramesHere();
326 : bool IsLeftButton(nsIDOMEvent* aMouseEvent);
327 :
328 : // guess at a row block size based on our own style.
329 : nscoord CalcFallbackRowBSize(float aFontSizeInflation);
330 :
331 : // CalcIntrinsicBSize computes our intrinsic block size (taking the
332 : // "size" attribute into account). This should only be called in
333 : // non-dropdown mode.
334 : nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions);
335 :
336 : // Dropped down stuff
337 : void SetComboboxItem(int32_t aIndex);
338 :
339 : /**
340 : * Method to reflow ourselves as a dropdown list. This differs from
341 : * reflow as a listbox because the criteria for needing a second
342 : * pass are different. This will be called from Reflow() as needed.
343 : */
344 : void ReflowAsDropdown(nsPresContext* aPresContext,
345 : ReflowOutput& aDesiredSize,
346 : const ReflowInput& aReflowInput,
347 : nsReflowStatus& aStatus);
348 :
349 : // Selection
350 : bool SetOptionsSelectedFromFrame(int32_t aStartIndex,
351 : int32_t aEndIndex,
352 : bool aValue,
353 : bool aClearAll);
354 : bool ToggleOptionSelectedFromFrame(int32_t aIndex);
355 : /**
356 : * @note This method might destroy the frame, pres shell and other objects.
357 : */
358 : bool SingleSelection(int32_t aClickedIndex, bool aDoToggle);
359 : bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex,
360 : bool aClearAll);
361 : /**
362 : * @note This method might destroy the frame, pres shell and other objects.
363 : */
364 : bool PerformSelection(int32_t aClickedIndex, bool aIsShift,
365 : bool aIsControl);
366 : /**
367 : * @note This method might destroy the frame, pres shell and other objects.
368 : */
369 : bool HandleListSelection(nsIDOMEvent * aDOMEvent, int32_t selectedIndex);
370 : void InitSelectionRange(int32_t aClickedIndex);
371 : void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode,
372 : bool aIsShift, bool aIsControlOrMeta);
373 :
374 : public:
375 0 : nsSelectsAreaFrame* GetOptionsContainer() const {
376 0 : return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame());
377 : }
378 :
379 : protected:
380 0 : nscoord BSizeOfARow() {
381 0 : return GetOptionsContainer()->BSizeOfARow();
382 : }
383 :
384 : /**
385 : * @return how many displayable options/optgroups this frame has.
386 : */
387 : uint32_t GetNumberOfRows();
388 :
389 0 : nsView* GetViewInternal() const override { return mView; }
390 0 : void SetViewInternal(nsView* aView) override { mView = aView; }
391 :
392 : // Data Members
393 : int32_t mStartSelectionIndex;
394 : int32_t mEndSelectionIndex;
395 :
396 : nsIComboboxControlFrame* mComboboxFrame;
397 :
398 : // The view is only created (& non-null) if IsInDropDownMode() is true.
399 : nsView* mView;
400 :
401 : uint32_t mNumDisplayRows;
402 : bool mChangesSinceDragStart:1;
403 : bool mButtonDown:1;
404 :
405 : // Has the user selected a visible item since we showed the dropdown?
406 : bool mItemSelectionStarted:1;
407 :
408 : bool mIsAllContentHere:1;
409 : bool mIsAllFramesHere:1;
410 : bool mHasBeenInitialized:1;
411 : bool mNeedToReset:1;
412 : bool mPostChildrenLoadedReset:1;
413 :
414 : //bool value for multiple discontiguous selection
415 : bool mControlSelectMode:1;
416 :
417 : // True if we're in the middle of a reflow and might need a second
418 : // pass. This only happens for auto heights.
419 : bool mMightNeedSecondPass:1;
420 :
421 : /**
422 : * Set to aPresContext->HasPendingInterrupt() at the start of Reflow.
423 : * Set to false at the end of DidReflow.
424 : */
425 : bool mHasPendingInterruptAtStartOfReflow:1;
426 :
427 : // True if the drop-down can show more rows. Always false if this list
428 : // is not in drop-down mode.
429 : bool mDropdownCanGrow:1;
430 :
431 : // True if the selection can be set to nothing or disabled options.
432 : bool mForceSelection:1;
433 :
434 : // The last computed block size we reflowed at if we're a combobox
435 : // dropdown.
436 : // XXXbz should we be using a subclass here? Or just not worry
437 : // about the extra member on listboxes?
438 : nscoord mLastDropdownComputedBSize;
439 :
440 : // At the time of our last dropdown, the backstop color to draw in case we
441 : // are translucent.
442 : nscolor mLastDropdownBackstopColor;
443 :
444 : RefPtr<nsListEventListener> mEventListener;
445 :
446 : static nsListControlFrame * mFocused;
447 : static nsString * sIncrementalString;
448 :
449 : #ifdef DO_REFLOW_COUNTER
450 : int32_t mReflowId;
451 : #endif
452 :
453 : private:
454 : // for incremental typing navigation
455 : static nsAString& GetIncrementalString ();
456 : static DOMTimeStamp gLastKeyTime;
457 :
458 : class MOZ_RAII AutoIncrementalSearchResetter
459 : {
460 : public:
461 0 : AutoIncrementalSearchResetter() :
462 0 : mCancelled(false)
463 : {
464 0 : }
465 0 : ~AutoIncrementalSearchResetter()
466 0 : {
467 0 : if (!mCancelled) {
468 0 : nsListControlFrame::GetIncrementalString().Truncate();
469 : }
470 0 : }
471 0 : void Cancel()
472 : {
473 0 : mCancelled = true;
474 0 : }
475 : private:
476 : bool mCancelled;
477 : };
478 : };
479 :
480 : #endif /* nsListControlFrame_h___ */
481 :
|