/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "nsISelectControlFrame.h" #include "nsSelectsAreaFrame.h" // X.h defines KeyPress #ifdef KeyPress # undef KeyPress #endif class nsComboboxControlFrame; class nsPresContext; class nsListEventListener; namespace mozilla { class PresShell; namespace dom { class Event; class HTMLOptionElement; class HTMLSelectElement; class HTMLOptionsCollection; } // namespace dom } // namespace mozilla /** * Frame-based listbox. */ class nsListControlFrame final : public nsHTMLScrollFrame, public nsIFormControlFrame, public nsISelectControlFrame { public: typedef mozilla::dom::HTMLOptionElement HTMLOptionElement; friend nsContainerFrame* NS_NewListControlFrame( mozilla::PresShell* aPresShell, ComputedStyle* aStyle); NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS(nsListControlFrame) // nsIFrame nsresult HandleEvent(nsPresContext* aPresContext, mozilla::WidgetGUIEvent* aEvent, nsEventStatus* aEventStatus) final; void SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) final; nscoord GetPrefISize(gfxContext* aRenderingContext) final; nscoord GetMinISize(gfxContext* aRenderingContext) final; void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) final; void Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) final; MOZ_CAN_RUN_SCRIPT_BOUNDARY void DidReflow(nsPresContext* aPresContext, const ReflowInput* aReflowInput) final; void DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) final; void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) final; nsContainerFrame* GetContentInsertionFrame() final; bool IsFrameOfType(uint32_t aFlags) const final { return nsHTMLScrollFrame::IsFrameOfType( aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); } #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const final; #endif // nsIFormControlFrame nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) final; MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetFocus(bool aOn = true, bool aRepaint = false) final; mozilla::ScrollStyles GetScrollStyles() const final; bool ShouldPropagateComputedBSizeToScrolledContent() const final; // for accessibility purposes #ifdef ACCESSIBILITY mozilla::a11y::AccType AccessibleType() final; #endif void SetComboboxFrame(nsIFrame* aComboboxFrame); int32_t GetSelectedIndex(); HTMLOptionElement* GetCurrentOption(); /** * 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. */ void GetOptionText(uint32_t aIndex, nsAString& aStr); void CaptureMouseEvents(bool aGrabMouseEvents); nscoord GetBSizeOfARow(); uint32_t GetNumberOfOptions(); MOZ_CAN_RUN_SCRIPT_BOUNDARY void AboutToDropDown(); /** * @note This method might destroy the frame, pres shell and other objects. */ void AboutToRollup(); /** * Dispatch a DOM oninput and onchange event synchroniously. * @note This method might destroy the frame, pres shell and other objects. */ MOZ_CAN_RUN_SCRIPT void FireOnInputAndOnChange(); /** * Makes aIndex the selected option of a combobox list. * @note This method might destroy the frame, pres shell and other objects. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY void ComboboxFinish(int32_t aIndex); MOZ_CAN_RUN_SCRIPT_BOUNDARY void OnContentReset(); // nsISelectControlFrame NS_IMETHOD AddOption(int32_t index) final; NS_IMETHOD RemoveOption(int32_t index) final; MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD DoneAddingChildren(bool aIsDone) final; /** * Gets the content (an option) by index and then set it as * being selected or not selected. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) final; MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD_(void) OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) final; /** * Mouse event listeners. * @note These methods might destroy the frame, pres shell and other objects. */ MOZ_CAN_RUN_SCRIPT nsresult MouseDown(mozilla::dom::Event* aMouseEvent); MOZ_CAN_RUN_SCRIPT nsresult MouseUp(mozilla::dom::Event* aMouseEvent); MOZ_CAN_RUN_SCRIPT nsresult MouseMove(mozilla::dom::Event* aMouseEvent); MOZ_CAN_RUN_SCRIPT nsresult DragMove(mozilla::dom::Event* aMouseEvent); MOZ_CAN_RUN_SCRIPT nsresult KeyDown(mozilla::dom::Event* aKeyEvent); MOZ_CAN_RUN_SCRIPT nsresult KeyPress(mozilla::dom::Event* 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. */ 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; } /** * 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: /** * Return the first non-disabled option starting at aFromIndex (inclusive). * @param aFoundIndex if non-null, set to the index of the returned option */ HTMLOptionElement* GetNonDisabledOptionFrom(int32_t aFromIndex, int32_t* aFoundIndex = nullptr); /** * 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|. */ MOZ_CAN_RUN_SCRIPT bool UpdateSelection(); /** * Returns whether mContent supports multiple selection. */ bool GetMultiple() const { return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple); } /** * Toggles (show/hide) the combobox dropdown menu. * @note This method might destroy the frame, pres shell and other objects. */ MOZ_CAN_RUN_SCRIPT void DropDownToggleKey(mozilla::dom::Event* aKeyEvent); /** * @return true if the