/* -*- 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 nsTextControlFrame_h___ #define nsTextControlFrame_h___ #include "mozilla/Attributes.h" #include "mozilla/TextControlElement.h" #include "nsContainerFrame.h" #include "nsIAnonymousContentCreator.h" #include "nsIContent.h" #include "nsITextControlFrame.h" #include "nsIStatefulFrame.h" class nsISelectionController; class EditorInitializerEntryTracker; namespace mozilla { class AutoTextControlHandlingState; class TextEditor; class TextControlState; enum class PseudoStyleType : uint8_t; namespace dom { class Element; } // namespace dom } // namespace mozilla class nsTextControlFrame : public nsContainerFrame, public nsIAnonymousContentCreator, public nsITextControlFrame, public nsIStatefulFrame { using Element = mozilla::dom::Element; public: NS_DECL_FRAMEARENA_HELPERS(nsTextControlFrame) NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ContentScrollPos, nsPoint) protected: nsTextControlFrame(ComputedStyle*, nsPresContext*, nsIFrame::ClassID); public: explicit nsTextControlFrame(ComputedStyle* aStyle, nsPresContext* aPresContext) : nsTextControlFrame(aStyle, aPresContext, kClassID) {} virtual ~nsTextControlFrame(); /** * Destroy() causes preparing to destroy editor and that may cause running * selection listeners of spellchecker selection and document state listeners. * Not sure whether the former does something or not, but nobody should run * content script. The latter is currently only FinderHighlighter to clean up * its fields at destruction. Thus, the latter won't run content script too. * Therefore, this won't run unsafe script. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY void Destroy(DestroyContext&) override; nsIScrollableFrame* GetScrollTargetFrame() const override; nscoord GetMinISize(gfxContext* aRenderingContext) override; nscoord GetPrefISize(gfxContext* aRenderingContext) override; mozilla::LogicalSize ComputeAutoSize( gfxContext* aRenderingContext, mozilla::WritingMode aWM, const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorderPadding, const mozilla::StyleSizeOverrides& aSizeOverrides, mozilla::ComputeSizeFlags aFlags) override; void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; Maybe GetNaturalBaselineBOffset( mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup, BaselineExportContext aExportContext) const override; BaselineSharingGroup GetDefaultBaselineSharingGroup() const override { return BaselineSharingGroup::Last; } static Maybe GetSingleLineTextControlBaseline( const nsIFrame* aFrame, nscoord aFirstBaseline, mozilla::WritingMode aWM, BaselineSharingGroup aBaselineGroup) { if (aFrame->StyleDisplay()->IsContainLayout()) { return Nothing{}; } NS_ASSERTION(aFirstBaseline != NS_INTRINSIC_ISIZE_UNKNOWN, "please call Reflow before asking for the baseline"); return mozilla::Some(aBaselineGroup == BaselineSharingGroup::First ? aFirstBaseline : aFrame->BSize(aWM) - aFirstBaseline); } #ifdef ACCESSIBILITY mozilla::a11y::AccType AccessibleType() override; #endif #ifdef DEBUG_FRAME_DUMP nsresult GetFrameName(nsAString& aResult) const override { aResult.AssignLiteral("nsTextControlFrame"); return NS_OK; } #endif // nsIAnonymousContentCreator nsresult CreateAnonymousContent(nsTArray& aElements) override; void AppendAnonymousContentTo(nsTArray& aElements, uint32_t aFilter) override; void SetInitialChildList(ChildListID, nsFrameList&&) override; void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; //==== BEGIN NSIFORMCONTROLFRAME MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetFocus(bool aOn, bool aRepaint) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult SetFormProperty(nsAtom* aName, const nsAString& aValue) override; //==== END NSIFORMCONTROLFRAME //==== NSITEXTCONTROLFRAME MOZ_CAN_RUN_SCRIPT_BOUNDARY already_AddRefed GetTextEditor() override; MOZ_CAN_RUN_SCRIPT NS_IMETHOD SetSelectionRange(uint32_t aSelectionStart, uint32_t aSelectionEnd, SelectionDirection = SelectionDirection::None) override; NS_IMETHOD GetOwnedSelectionController( nsISelectionController** aSelCon) override; nsFrameSelection* GetOwnedFrameSelection() override { return ControlElement()->GetConstFrameSelection(); } nsISelectionController* GetSelectionController() { return ControlElement()->GetSelectionController(); } void PlaceholderChanged(const nsAttrValue* aOld, const nsAttrValue* aNew); /** * Ensure mEditor is initialized with the proper flags and the default value. * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created * @throws various and sundry other things */ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult EnsureEditorInitialized() override; //==== END NSITEXTCONTROLFRAME //==== NSISTATEFULFRAME mozilla::UniquePtr SaveState() override; NS_IMETHOD RestoreState(mozilla::PresState* aState) override; //=== END NSISTATEFULFRAME //==== OVERLOAD of nsIFrame /** handler for attribute changes to mContent */ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult AttributeChanged( int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType) override; void ElementStateChanged(mozilla::dom::ElementState aStates) override; nsresult PeekOffset(mozilla::PeekOffsetStruct* aPos) override; NS_DECL_QUERYFRAME // Whether we should scroll only the current selection into view in the inner // scroller, or also ancestors as needed. enum class ScrollAncestors { No, Yes }; void ScrollSelectionIntoViewAsync(ScrollAncestors = ScrollAncestors::No); protected: MOZ_CAN_RUN_SCRIPT_BOUNDARY void HandleReadonlyOrDisabledChange(); /** * Launch the reflow on the child frames - see nsTextControlFrame::Reflow() */ void ReflowTextControlChild(nsIFrame* aFrame, nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsReflowStatus& aStatus, ReflowOutput& aParentDesiredSize, nscoord& aButtonBoxISize); public: static Maybe ComputeBaseline(const nsIFrame*, const ReflowInput&, bool aForSingleLineControl); Element* GetRootNode() const { return mRootNode; } Element* GetPreviewNode() const { return mPreviewDiv; } Element* GetPlaceholderNode() const { return mPlaceholderDiv; } Element* GetRevealButton() const { return mRevealButton; } // called by the focus listener nsresult MaybeBeginSecureKeyboardInput(); void MaybeEndSecureKeyboardInput(); mozilla::TextControlElement* ControlElement() const { MOZ_ASSERT(mozilla::TextControlElement::FromNode(GetContent())); return static_cast(GetContent()); } #define DEFINE_TEXTCTRL_CONST_FORWARDER(type, name) \ type name() const { return ControlElement()->name(); } DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsSingleLineTextControl) DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsTextArea) DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsPasswordTextControl) DEFINE_TEXTCTRL_CONST_FORWARDER(int32_t, GetCols) DEFINE_TEXTCTRL_CONST_FORWARDER(int32_t, GetRows) #undef DEFINE_TEXTCTRL_CONST_FORWARDER protected: class EditorInitializer; friend class EditorInitializer; friend class mozilla::AutoTextControlHandlingState; // needs access to // CacheValue friend class mozilla::TextControlState; // needs access to UpdateValueDisplay // Temp reference to scriptrunner NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(TextControlInitializer, EditorInitializer, nsTextControlFrame::RevokeInitializer) static void RevokeInitializer(EditorInitializer* aInitializer) { aInitializer->Revoke(); }; class EditorInitializer : public mozilla::Runnable { public: explicit EditorInitializer(nsTextControlFrame* aFrame) : mozilla::Runnable("nsTextControlFrame::EditorInitializer"), mFrame(aFrame) {} NS_IMETHOD Run() override; // avoids use of AutoWeakFrame void Revoke() { mFrame = nullptr; } private: nsTextControlFrame* mFrame; }; nsresult OffsetToDOMPoint(uint32_t aOffset, nsINode** aResult, uint32_t* aPosition); /** * Update the textnode under our anonymous div to show the new * value. This should only be called when we have no editor yet. * @throws NS_ERROR_UNEXPECTED if the div has no text content */ nsresult UpdateValueDisplay(bool aNotify, bool aBeforeEditorInit = false, const nsAString* aValue = nullptr); /** * Find out whether an attribute exists on the content or not. * @param aAtt the attribute to determine the existence of * @returns false if it does not exist */ bool AttributeExists(nsAtom* aAtt) const { return mContent && mContent->AsElement()->HasAttr(aAtt); } /** * We call this when we are being destroyed or removed from the PFM. * @param aPresContext the current pres context */ void PreDestroy(); // Compute our intrinsic size. This does not include any borders, paddings, // etc. Just the size of our actual area for the text (and the scrollbars, // for