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 7 #ifndef nsTextControlFrame_h___ 8 #define nsTextControlFrame_h___ 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/TextControlElement.h" 12 #include "nsContainerFrame.h" 13 #include "nsIAnonymousContentCreator.h" 14 #include "nsIContent.h" 15 #include "nsITextControlFrame.h" 16 #include "nsIStatefulFrame.h" 17 18 class nsISelectionController; 19 class EditorInitializerEntryTracker; 20 namespace mozilla { 21 class AutoTextControlHandlingState; 22 class TextEditor; 23 class TextControlState; 24 enum class PseudoStyleType : uint8_t; 25 namespace dom { 26 class Element; 27 } // namespace dom 28 } // namespace mozilla 29 30 class nsTextControlFrame : public nsContainerFrame, 31 public nsIAnonymousContentCreator, 32 public nsITextControlFrame, 33 public nsIStatefulFrame { 34 using Element = mozilla::dom::Element; 35 36 public: 37 NS_DECL_FRAMEARENA_HELPERS(nsTextControlFrame) 38 39 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ContentScrollPos, nsPoint) 40 41 protected: 42 nsTextControlFrame(ComputedStyle*, nsPresContext*, nsIFrame::ClassID); 43 44 public: nsTextControlFrame(ComputedStyle * aStyle,nsPresContext * aPresContext)45 explicit nsTextControlFrame(ComputedStyle* aStyle, 46 nsPresContext* aPresContext) 47 : nsTextControlFrame(aStyle, aPresContext, kClassID) {} 48 49 virtual ~nsTextControlFrame(); 50 51 /** 52 * DestroyFrom() causes preparing to destroy editor and that may cause 53 * running selection listeners of specllchecker selection and document 54 * state listeners. Not sure whether the former does something or not, 55 * but nobody should run content script. The latter is currently only 56 * FinderHighlighter to clean up its fields at destruction. Thus, the 57 * latter won't run content script too. Therefore, this won't run 58 * unsafe script. 59 */ 60 MOZ_CAN_RUN_SCRIPT_BOUNDARY void DestroyFrom(nsIFrame* aDestructRoot, 61 PostDestroyData&) override; 62 63 nsIScrollableFrame* GetScrollTargetFrame() const override; 64 65 nscoord GetMinISize(gfxContext* aRenderingContext) override; 66 nscoord GetPrefISize(gfxContext* aRenderingContext) override; 67 68 mozilla::LogicalSize ComputeAutoSize( 69 gfxContext* aRenderingContext, mozilla::WritingMode aWM, 70 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize, 71 const mozilla::LogicalSize& aMargin, 72 const mozilla::LogicalSize& aBorderPadding, 73 const mozilla::StyleSizeOverrides& aSizeOverrides, 74 mozilla::ComputeSizeFlags aFlags) override; 75 76 void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize, 77 const ReflowInput& aReflowInput, 78 nsReflowStatus& aStatus) override; 79 GetVerticalAlignBaseline(mozilla::WritingMode aWM,nscoord * aBaseline)80 bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, 81 nscoord* aBaseline) const override { 82 return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::First, 83 aBaseline); 84 } 85 GetNaturalBaselineBOffset(mozilla::WritingMode aWM,BaselineSharingGroup aBaselineGroup,nscoord * aBaseline)86 bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, 87 BaselineSharingGroup aBaselineGroup, 88 nscoord* aBaseline) const override { 89 if (!IsSingleLineTextControl()) { 90 return false; 91 } 92 return GetSingleLineTextControlBaseline(this, mFirstBaseline, aWM, 93 aBaselineGroup, aBaseline); 94 } 95 GetSingleLineTextControlBaseline(const nsIFrame * aFrame,nscoord aFirstBaseline,mozilla::WritingMode aWM,BaselineSharingGroup aBaselineGroup,nscoord * aBaseline)96 static bool GetSingleLineTextControlBaseline( 97 const nsIFrame* aFrame, nscoord aFirstBaseline, mozilla::WritingMode aWM, 98 BaselineSharingGroup aBaselineGroup, nscoord* aBaseline) { 99 if (aFrame->StyleDisplay()->IsContainLayout()) { 100 return false; 101 } 102 NS_ASSERTION(aFirstBaseline != NS_INTRINSIC_ISIZE_UNKNOWN, 103 "please call Reflow before asking for the baseline"); 104 *aBaseline = aBaselineGroup == BaselineSharingGroup::First 105 ? aFirstBaseline 106 : aFrame->BSize(aWM) - aFirstBaseline; 107 return true; 108 } 109 110 nsSize GetXULMinSize(nsBoxLayoutState&) override; 111 bool IsXULCollapsed() override; 112 113 #ifdef ACCESSIBILITY 114 mozilla::a11y::AccType AccessibleType() override; 115 #endif 116 117 #ifdef DEBUG_FRAME_DUMP GetFrameName(nsAString & aResult)118 nsresult GetFrameName(nsAString& aResult) const override { 119 aResult.AssignLiteral("nsTextControlFrame"); 120 return NS_OK; 121 } 122 #endif 123 IsFrameOfType(uint32_t aFlags)124 bool IsFrameOfType(uint32_t aFlags) const override { 125 return nsContainerFrame::IsFrameOfType( 126 aFlags & ~(nsIFrame::eReplaced | nsIFrame::eReplacedContainsBlock)); 127 } 128 129 #ifdef DEBUG MarkIntrinsicISizesDirty()130 void MarkIntrinsicISizesDirty() override { 131 // Need another Reflow to have a correct baseline value again. 132 mFirstBaseline = NS_INTRINSIC_ISIZE_UNKNOWN; 133 } 134 #endif 135 136 // nsIAnonymousContentCreator 137 nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override; 138 void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, 139 uint32_t aFilter) override; 140 141 void SetInitialChildList(ChildListID, nsFrameList&) override; 142 143 void BuildDisplayList(nsDisplayListBuilder* aBuilder, 144 const nsDisplayListSet& aLists) override; 145 146 //==== BEGIN NSIFORMCONTROLFRAME 147 MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetFocus(bool aOn, bool aRepaint) override; 148 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult 149 SetFormProperty(nsAtom* aName, const nsAString& aValue) override; 150 151 //==== END NSIFORMCONTROLFRAME 152 153 //==== NSITEXTCONTROLFRAME 154 155 MOZ_CAN_RUN_SCRIPT_BOUNDARY already_AddRefed<mozilla::TextEditor> 156 GetTextEditor() override; 157 MOZ_CAN_RUN_SCRIPT NS_IMETHOD 158 SetSelectionRange(uint32_t aSelectionStart, uint32_t aSelectionEnd, 159 SelectionDirection aDirection = eNone) override; 160 NS_IMETHOD GetOwnedSelectionController( 161 nsISelectionController** aSelCon) override; 162 nsFrameSelection* GetOwnedFrameSelection() override; 163 164 void PlaceholderChanged(const nsAttrValue* aOld, const nsAttrValue* aNew); 165 166 /** 167 * Ensure mEditor is initialized with the proper flags and the default value. 168 * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created 169 * @throws various and sundry other things 170 */ 171 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult EnsureEditorInitialized() override; 172 173 //==== END NSITEXTCONTROLFRAME 174 175 //==== NSISTATEFULFRAME 176 177 mozilla::UniquePtr<mozilla::PresState> SaveState() override; 178 NS_IMETHOD RestoreState(mozilla::PresState* aState) override; 179 180 //=== END NSISTATEFULFRAME 181 182 //==== OVERLOAD of nsIFrame 183 184 /** handler for attribute changes to mContent */ 185 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult AttributeChanged( 186 int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType) override; 187 188 void GetText(nsString& aText); 189 190 /** 191 * TextEquals() is designed for internal use so that aValue shouldn't 192 * include \r character. It should be handled before calling this with 193 * nsContentUtils::PlatformToDOMLineBreaks(). 194 */ 195 bool TextEquals(const nsAString& aText) const; 196 197 nsresult PeekOffset(nsPeekOffsetStruct* aPos) override; 198 199 NS_DECL_QUERYFRAME 200 201 // Whether we should scroll only the current selection into view in the inner 202 // scroller, or also ancestors as needed. 203 enum class ScrollAncestors { No, Yes }; 204 void ScrollSelectionIntoViewAsync(ScrollAncestors = ScrollAncestors::No); 205 206 protected: 207 /** 208 * Launch the reflow on the child frames - see nsTextControlFrame::Reflow() 209 */ 210 void ReflowTextControlChild(nsIFrame* aFrame, nsPresContext* aPresContext, 211 const ReflowInput& aReflowInput, 212 nsReflowStatus& aStatus, 213 ReflowOutput& aParentDesiredSize, 214 nscoord& aButtonBoxISize); 215 216 public: 217 static Maybe<nscoord> ComputeBaseline(const nsIFrame*, const ReflowInput&, 218 bool aForSingleLineControl); 219 GetRootNode()220 Element* GetRootNode() const { return mRootNode; } 221 GetPreviewNode()222 Element* GetPreviewNode() const { return mPreviewDiv; } 223 GetPlaceholderNode()224 Element* GetPlaceholderNode() const { return mPlaceholderDiv; } 225 226 // called by the focus listener 227 nsresult MaybeBeginSecureKeyboardInput(); 228 void MaybeEndSecureKeyboardInput(); 229 230 #define DEFINE_TEXTCTRL_CONST_FORWARDER(type, name) \ 231 type name() const { \ 232 mozilla::TextControlElement* textControlElement = \ 233 mozilla::TextControlElement::FromNode(GetContent()); \ 234 return textControlElement->name(); \ 235 } 236 237 DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsSingleLineTextControl) 238 DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsTextArea) 239 DEFINE_TEXTCTRL_CONST_FORWARDER(bool, IsPasswordTextControl) 240 DEFINE_TEXTCTRL_CONST_FORWARDER(int32_t, GetCols) 241 DEFINE_TEXTCTRL_CONST_FORWARDER(int32_t, GetRows) 242 243 #undef DEFINE_TEXTCTRL_CONST_FORWARDER 244 245 protected: 246 class EditorInitializer; 247 friend class EditorInitializer; 248 friend class mozilla::AutoTextControlHandlingState; // needs access to 249 // CacheValue 250 friend class mozilla::TextControlState; // needs access to UpdateValueDisplay 251 252 // Temp reference to scriptrunner NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(TextControlInitializer,EditorInitializer,nsTextControlFrame::RevokeInitializer)253 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(TextControlInitializer, EditorInitializer, 254 nsTextControlFrame::RevokeInitializer) 255 256 static void RevokeInitializer(EditorInitializer* aInitializer) { 257 aInitializer->Revoke(); 258 }; 259 260 class EditorInitializer : public mozilla::Runnable { 261 public: EditorInitializer(nsTextControlFrame * aFrame)262 explicit EditorInitializer(nsTextControlFrame* aFrame) 263 : mozilla::Runnable("nsTextControlFrame::EditorInitializer"), 264 mFrame(aFrame) {} 265 266 NS_IMETHOD Run() override; 267 268 // avoids use of AutoWeakFrame Revoke()269 void Revoke() { mFrame = nullptr; } 270 271 private: 272 nsTextControlFrame* mFrame; 273 }; 274 275 nsresult OffsetToDOMPoint(uint32_t aOffset, nsINode** aResult, 276 uint32_t* aPosition); 277 278 /** 279 * Update the textnode under our anonymous div to show the new 280 * value. This should only be called when we have no editor yet. 281 * @throws NS_ERROR_UNEXPECTED if the div has no text content 282 */ 283 nsresult UpdateValueDisplay(bool aNotify, bool aBeforeEditorInit = false, 284 const nsAString* aValue = nullptr); 285 286 /** 287 * Find out whether an attribute exists on the content or not. 288 * @param aAtt the attribute to determine the existence of 289 * @returns false if it does not exist 290 */ AttributeExists(nsAtom * aAtt)291 bool AttributeExists(nsAtom* aAtt) const { 292 return mContent && mContent->AsElement()->HasAttr(kNameSpaceID_None, aAtt); 293 } 294 295 /** 296 * We call this when we are being destroyed or removed from the PFM. 297 * @param aPresContext the current pres context 298 */ 299 void PreDestroy(); 300 301 // Compute our intrinsic size. This does not include any borders, paddings, 302 // etc. Just the size of our actual area for the text (and the scrollbars, 303 // for <textarea>). 304 mozilla::LogicalSize CalcIntrinsicSize(gfxContext* aRenderingContext, 305 mozilla::WritingMode aWM, 306 float aFontSizeInflation) const; 307 308 private: 309 // helper methods 310 MOZ_CAN_RUN_SCRIPT nsresult SetSelectionInternal( 311 nsINode* aStartNode, uint32_t aStartOffset, nsINode* aEndNode, 312 uint32_t aEndOffset, SelectionDirection aDirection = eNone); 313 MOZ_CAN_RUN_SCRIPT nsresult SelectAllOrCollapseToEndOfText(bool aSelect); 314 MOZ_CAN_RUN_SCRIPT nsresult 315 SetSelectionEndPoints(uint32_t aSelStart, uint32_t aSelEnd, 316 SelectionDirection aDirection = eNone); 317 FinishedInitializer()318 void FinishedInitializer() { RemoveProperty(TextControlInitializer()); } 319 CachedValue()320 const nsAString& CachedValue() const { return mCachedValue; } 321 ClearCachedValue()322 void ClearCachedValue() { mCachedValue.SetIsVoid(true); } 323 CacheValue(const nsAString & aValue)324 void CacheValue(const nsAString& aValue) { mCachedValue.Assign(aValue); } 325 CacheValue(const nsAString & aValue,const mozilla::fallible_t & aFallible)326 [[nodiscard]] bool CacheValue(const nsAString& aValue, 327 const mozilla::fallible_t& aFallible) { 328 if (!mCachedValue.Assign(aValue, aFallible)) { 329 ClearCachedValue(); 330 return false; 331 } 332 return true; 333 } 334 335 protected: 336 class nsAnonDivObserver; 337 338 nsresult CreateRootNode(); 339 void CreatePlaceholderIfNeeded(); 340 void UpdatePlaceholderText(nsString&, bool aNotify); 341 void CreatePreviewIfNeeded(); 342 already_AddRefed<Element> MakeAnonElement( 343 mozilla::PseudoStyleType, Element* aParent = nullptr, 344 nsAtom* aTag = nsGkAtoms::div) const; 345 already_AddRefed<Element> MakeAnonDivWithTextNode( 346 mozilla::PseudoStyleType) const; 347 348 bool ShouldInitializeEagerly() const; 349 void InitializeEagerlyIfNeeded(); 350 351 RefPtr<Element> mRootNode; 352 RefPtr<Element> mPlaceholderDiv; 353 RefPtr<Element> mPreviewDiv; 354 RefPtr<nsAnonDivObserver> mMutationObserver; 355 // Cache of the |.value| of <input> or <textarea> element without hard-wrap. 356 // If its IsVoid() returns true, it doesn't cache |.value|. 357 // Otherwise, it's cached when setting specific value or getting value from 358 // TextEditor. Additionally, when contents in the anonymous <div> element 359 // is modified, this is cleared. 360 // 361 // FIXME(bug 1402545): Consider using an nsAutoString here. 362 nsString mCachedValue; 363 364 // Our first baseline, or NS_INTRINSIC_ISIZE_UNKNOWN if we have a pending 365 // Reflow (or if we're contain:layout, which means we have no baseline). 366 nscoord mFirstBaseline; 367 368 // these packed bools could instead use the high order bits on mState, saving 369 // 4 bytes 370 bool mEditorHasBeenInitialized; 371 bool mIsProcessing; 372 373 #ifdef DEBUG 374 bool mInEditorInitialization; 375 friend class EditorInitializerEntryTracker; 376 #endif 377 }; 378 379 #endif 380