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