123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #ifndef nsListControlFrame_h___
- #define nsListControlFrame_h___
- #ifdef DEBUG_evaughan
- //#define DEBUG_rods
- #endif
- #ifdef DEBUG_rods
- //#define DO_REFLOW_DEBUG
- //#define DO_REFLOW_COUNTER
- //#define DO_UNCONSTRAINED_CHECK
- //#define DO_PIXELS
- #endif
- #include "mozilla/Attributes.h"
- #include "nsGfxScrollFrame.h"
- #include "nsIFormControlFrame.h"
- #include "nsIListControlFrame.h"
- #include "nsISelectControlFrame.h"
- #include "nsSelectsAreaFrame.h"
- // X.h defines KeyPress
- #ifdef KeyPress
- #undef KeyPress
- #endif
- class nsIComboboxControlFrame;
- class nsPresContext;
- class nsListEventListener;
- namespace mozilla {
- namespace dom {
- class HTMLOptionElement;
- class HTMLOptionsCollection;
- } // namespace dom
- } // namespace mozilla
- /**
- * Frame-based listbox.
- */
- class nsListControlFrame final : public nsHTMLScrollFrame,
- public nsIFormControlFrame,
- public nsIListControlFrame,
- public nsISelectControlFrame
- {
- public:
- friend nsContainerFrame* NS_NewListControlFrame(nsIPresShell* aPresShell,
- nsStyleContext* aContext);
- NS_DECL_QUERYFRAME
- NS_DECL_FRAMEARENA_HELPERS
- // nsIFrame
- virtual nsresult HandleEvent(nsPresContext* aPresContext,
- mozilla::WidgetGUIEvent* aEvent,
- nsEventStatus* aEventStatus) override;
- virtual void SetInitialChildList(ChildListID aListID,
- nsFrameList& aChildList) override;
- virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
- virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
- virtual void Reflow(nsPresContext* aCX,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nsReflowStatus& aStatus) override;
- virtual void Init(nsIContent* aContent,
- nsContainerFrame* aParent,
- nsIFrame* aPrevInFlow) override;
- virtual void DidReflow(nsPresContext* aPresContext,
- const ReflowInput* aReflowInput,
- nsDidReflowStatus aStatus) override;
- virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
- virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsDisplayListSet& aLists) override;
- virtual nsContainerFrame* GetContentInsertionFrame() override;
- /**
- * Get the "type" of the frame
- *
- * @see nsGkAtoms::scrollFrame
- */
- virtual nsIAtom* GetType() const override;
- virtual bool IsFrameOfType(uint32_t aFlags) const override
- {
- return nsHTMLScrollFrame::IsFrameOfType(aFlags &
- ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock));
- }
- #ifdef DEBUG_FRAME_DUMP
- virtual nsresult GetFrameName(nsAString& aResult) const override;
- #endif
- // nsIFormControlFrame
- virtual nsresult SetFormProperty(nsIAtom* aName, const nsAString& aValue) override;
- virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
- virtual mozilla::ScrollStyles GetScrollStyles() const override;
- virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;
- // for accessibility purposes
- #ifdef ACCESSIBILITY
- virtual mozilla::a11y::AccType AccessibleType() override;
- #endif
- // nsIListControlFrame
- virtual void SetComboboxFrame(nsIFrame* aComboboxFrame) override;
- virtual int32_t GetSelectedIndex() override;
- virtual mozilla::dom::HTMLOptionElement* GetCurrentOption() override;
- /**
- * Gets the text of the currently selected item.
- * If the there are zero items then an empty string is returned
- * If there is nothing selected, then the 0th item's text is returned.
- */
- virtual void GetOptionText(uint32_t aIndex, nsAString& aStr) override;
- virtual void CaptureMouseEvents(bool aGrabMouseEvents) override;
- virtual nscoord GetBSizeOfARow() override;
- virtual uint32_t GetNumberOfOptions() override;
- virtual void AboutToDropDown() override;
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- virtual void AboutToRollup() override;
- /**
- * Dispatch a DOM oninput and onchange event synchroniously.
- * @note This method might destroy the frame, pres shell and other objects.
- */
- virtual void FireOnInputAndOnChange() override;
- /**
- * Makes aIndex the selected option of a combobox list.
- * @note This method might destroy the frame, pres shell and other objects.
- */
- virtual void ComboboxFinish(int32_t aIndex) override;
- virtual void OnContentReset() override;
- // nsISelectControlFrame
- NS_IMETHOD AddOption(int32_t index) override;
- NS_IMETHOD RemoveOption(int32_t index) override;
- NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
- /**
- * Gets the content (an option) by index and then set it as
- * being selected or not selected.
- */
- NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
- NS_IMETHOD OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
- /**
- * Mouse event listeners.
- * @note These methods might destroy the frame, pres shell and other objects.
- */
- nsresult MouseDown(nsIDOMEvent* aMouseEvent);
- nsresult MouseUp(nsIDOMEvent* aMouseEvent);
- nsresult MouseMove(nsIDOMEvent* aMouseEvent);
- nsresult DragMove(nsIDOMEvent* aMouseEvent);
- nsresult KeyDown(nsIDOMEvent* aKeyEvent);
- nsresult KeyPress(nsIDOMEvent* aKeyEvent);
- /**
- * Returns the options collection for mContent, if any.
- */
- mozilla::dom::HTMLOptionsCollection* GetOptions() const;
- /**
- * Returns the HTMLOptionElement for a given index in mContent's collection.
- */
- mozilla::dom::HTMLOptionElement* GetOption(uint32_t aIndex) const;
- static void ComboboxFocusSet();
- // Helper
- bool IsFocused() { return this == mFocused; }
- /**
- * Function to paint the focus rect when our nsSelectsAreaFrame is painting.
- * @param aPt the offset of this frame, relative to the rendering reference
- * frame
- */
- void PaintFocus(mozilla::gfx::DrawTarget* aDrawTarget, nsPoint aPt);
- /**
- * If this frame IsFocused(), invalidates an area that includes anything
- * that PaintFocus will or could have painted --- basically the whole
- * GetOptionsContainer, plus some extra stuff if there are no options. This
- * must be called every time mEndSelectionIndex changes.
- */
- void InvalidateFocus();
- /**
- * Function to calculate the block size of a row, for use with the
- * "size" attribute.
- * Can't be const because GetNumberOfOptions() isn't const.
- */
- nscoord CalcBSizeOfARow();
- /**
- * Function to ask whether we're currently in what might be the
- * first pass of a two-pass reflow.
- */
- bool MightNeedSecondPass() const {
- return mMightNeedSecondPass;
- }
- void SetSuppressScrollbarUpdate(bool aSuppress) {
- nsHTMLScrollFrame::SetSuppressScrollbarUpdate(aSuppress);
- }
- /**
- * Return whether the list is in dropdown mode.
- */
- bool IsInDropDownMode() const;
- /**
- * Return the number of displayed rows in the list.
- */
- uint32_t GetNumDisplayRows() const { return mNumDisplayRows; }
- /**
- * Return true if the drop-down list can display more rows.
- * (always false if not in drop-down mode)
- */
- bool GetDropdownCanGrow() const { return mDropdownCanGrow; }
- /**
- * Dropdowns need views
- */
- virtual bool NeedsView() override { return IsInDropDownMode(); }
- /**
- * Frees statics owned by this class.
- */
- static void Shutdown();
- #ifdef ACCESSIBILITY
- /**
- * Post a custom DOM event for the change, so that accessibility can
- * fire a native focus event for accessibility
- * (Some 3rd party products need to track our focus)
- */
- void FireMenuItemActiveEvent(); // Inform assistive tech what got focused
- #endif
- protected:
- /**
- * Updates the selected text in a combobox and then calls FireOnChange().
- * @note This method might destroy the frame, pres shell and other objects.
- * Returns false if calling it destroyed |this|.
- */
- bool UpdateSelection();
- /**
- * Returns whether mContent supports multiple selection.
- */
- bool GetMultiple() const {
- return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple);
- }
- /**
- * Toggles (show/hide) the combobox dropdown menu.
- * @note This method might destroy the frame, pres shell and other objects.
- */
- void DropDownToggleKey(nsIDOMEvent* aKeyEvent);
- nsresult IsOptionDisabled(int32_t anIndex, bool &aIsDisabled);
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- void ScrollToFrame(mozilla::dom::HTMLOptionElement& aOptElement);
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- void ScrollToIndex(int32_t anIndex);
- /**
- * When the user clicks on the comboboxframe to show the dropdown
- * listbox, they then have to move the mouse into the list. We don't
- * want to process those mouse events as selection events (i.e., to
- * scroll list items into view). So we ignore the events until
- * the mouse moves below our border-inner-edge, when
- * mItemSelectionStarted is set.
- *
- * @param aPoint relative to this frame
- */
- bool IgnoreMouseEventForSelection(nsIDOMEvent* aEvent);
- /**
- * If the dropdown is showing and the mouse has moved below our
- * border-inner-edge, then set mItemSelectionStarted.
- */
- void UpdateInListState(nsIDOMEvent* aEvent);
- void AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t &anNewIndex,
- int32_t aNumOptions, int32_t aDoAdjustInc, int32_t aDoAdjustIncNext);
- /**
- * Resets the select back to it's original default values;
- * those values as determined by the original HTML
- */
- virtual void ResetList(bool aAllowScrolling);
- explicit nsListControlFrame(nsStyleContext* aContext);
- virtual ~nsListControlFrame();
- /**
- * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what
- * item was selected using content
- * @param aPoint the event point, in listcontrolframe coordinates
- * @return NS_OK if it successfully found the selection
- */
- nsresult GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, int32_t& aCurIndex);
- bool CheckIfAllFramesHere();
- bool IsLeftButton(nsIDOMEvent* aMouseEvent);
- // guess at a row block size based on our own style.
- nscoord CalcFallbackRowBSize(float aFontSizeInflation);
- // CalcIntrinsicBSize computes our intrinsic block size (taking the
- // "size" attribute into account). This should only be called in
- // non-dropdown mode.
- nscoord CalcIntrinsicBSize(nscoord aBSizeOfARow, int32_t aNumberOfOptions);
- // Dropped down stuff
- void SetComboboxItem(int32_t aIndex);
- /**
- * Method to reflow ourselves as a dropdown list. This differs from
- * reflow as a listbox because the criteria for needing a second
- * pass are different. This will be called from Reflow() as needed.
- */
- void ReflowAsDropdown(nsPresContext* aPresContext,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nsReflowStatus& aStatus);
- // Selection
- bool SetOptionsSelectedFromFrame(int32_t aStartIndex,
- int32_t aEndIndex,
- bool aValue,
- bool aClearAll);
- bool ToggleOptionSelectedFromFrame(int32_t aIndex);
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- bool SingleSelection(int32_t aClickedIndex, bool aDoToggle);
- bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex,
- bool aClearAll);
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- bool PerformSelection(int32_t aClickedIndex, bool aIsShift,
- bool aIsControl);
- /**
- * @note This method might destroy the frame, pres shell and other objects.
- */
- bool HandleListSelection(nsIDOMEvent * aDOMEvent, int32_t selectedIndex);
- void InitSelectionRange(int32_t aClickedIndex);
- void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode,
- bool aIsShift, bool aIsControlOrMeta);
- public:
- nsSelectsAreaFrame* GetOptionsContainer() const {
- return static_cast<nsSelectsAreaFrame*>(GetScrolledFrame());
- }
- protected:
- nscoord BSizeOfARow() {
- return GetOptionsContainer()->BSizeOfARow();
- }
- /**
- * @return how many displayable options/optgroups this frame has.
- */
- uint32_t GetNumberOfRows();
- // Data Members
- int32_t mStartSelectionIndex;
- int32_t mEndSelectionIndex;
- nsIComboboxControlFrame *mComboboxFrame;
- uint32_t mNumDisplayRows;
- bool mChangesSinceDragStart:1;
- bool mButtonDown:1;
- // Has the user selected a visible item since we showed the
- // dropdown?
- bool mItemSelectionStarted:1;
- bool mIsAllContentHere:1;
- bool mIsAllFramesHere:1;
- bool mHasBeenInitialized:1;
- bool mNeedToReset:1;
- bool mPostChildrenLoadedReset:1;
- //bool value for multiple discontiguous selection
- bool mControlSelectMode:1;
- // True if we're in the middle of a reflow and might need a second
- // pass. This only happens for auto heights.
- bool mMightNeedSecondPass:1;
- /**
- * Set to aPresContext->HasPendingInterrupt() at the start of Reflow.
- * Set to false at the end of DidReflow.
- */
- bool mHasPendingInterruptAtStartOfReflow:1;
- // True if the drop-down can show more rows. Always false if this list
- // is not in drop-down mode.
- bool mDropdownCanGrow:1;
- // True if the selection can be set to nothing or disabled options.
- bool mForceSelection:1;
- // The last computed block size we reflowed at if we're a combobox
- // dropdown.
- // XXXbz should we be using a subclass here? Or just not worry
- // about the extra member on listboxes?
- nscoord mLastDropdownComputedBSize;
- // At the time of our last dropdown, the backstop color to draw in case we
- // are translucent.
- nscolor mLastDropdownBackstopColor;
- RefPtr<nsListEventListener> mEventListener;
- static nsListControlFrame * mFocused;
- static nsString * sIncrementalString;
- #ifdef DO_REFLOW_COUNTER
- int32_t mReflowId;
- #endif
- private:
- // for incremental typing navigation
- static nsAString& GetIncrementalString ();
- static DOMTimeStamp gLastKeyTime;
- class MOZ_RAII AutoIncrementalSearchResetter
- {
- public:
- AutoIncrementalSearchResetter() :
- mCancelled(false)
- {
- }
- ~AutoIncrementalSearchResetter()
- {
- if (!mCancelled) {
- nsListControlFrame::GetIncrementalString().Truncate();
- }
- }
- void Cancel()
- {
- mCancelled = true;
- }
- private:
- bool mCancelled;
- };
- };
- #endif /* nsListControlFrame_h___ */
|