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