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 #include "mozilla/DebugOnly.h"
8 
9 #include "gfxContext.h"
10 #include "nsCOMPtr.h"
11 #include "nsFontMetrics.h"
12 #include "nsTextControlFrame.h"
13 #include "nsIEditor.h"
14 #include "nsCaret.h"
15 #include "nsCSSPseudoElements.h"
16 #include "nsDisplayList.h"
17 #include "nsGenericHTMLElement.h"
18 #include "nsTextFragment.h"
19 #include "nsNameSpaceManager.h"
20 
21 #include "nsIContent.h"
22 #include "nsIScrollableFrame.h"
23 #include "nsPresContext.h"
24 #include "nsGkAtoms.h"
25 #include "nsLayoutUtils.h"
26 
27 #include <algorithm>
28 #include "nsRange.h"  //for selection setting helper func
29 #include "nsINode.h"
30 #include "nsPIDOMWindow.h"  //needed for notify selection changed to update the menus ect.
31 #include "nsQueryObject.h"
32 #include "nsILayoutHistoryState.h"
33 
34 #include "nsFocusManager.h"
35 #include "mozilla/PresShell.h"
36 #include "mozilla/PresState.h"
37 #include "nsAttrValueInlines.h"
38 #include "mozilla/dom/Selection.h"
39 #include "nsContentUtils.h"
40 #include "nsTextNode.h"
41 #include "mozilla/dom/HTMLInputElement.h"
42 #include "mozilla/dom/HTMLTextAreaElement.h"
43 #include "mozilla/dom/ScriptSettings.h"
44 #include "mozilla/dom/Text.h"
45 #include "mozilla/MathAlgorithms.h"
46 #include "nsFrameSelection.h"
47 
48 #define DEFAULT_COLUMN_WIDTH 20
49 
50 using namespace mozilla;
51 using namespace mozilla::dom;
52 
NS_NewTextControlFrame(PresShell * aPresShell,ComputedStyle * aStyle)53 nsIFrame* NS_NewTextControlFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
54   return new (aPresShell)
55       nsTextControlFrame(aStyle, aPresShell->GetPresContext());
56 }
57 
58 NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
59 
NS_QUERYFRAME_HEAD(nsTextControlFrame)60 NS_QUERYFRAME_HEAD(nsTextControlFrame)
61   NS_QUERYFRAME_ENTRY(nsTextControlFrame)
62   NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
63   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
64   NS_QUERYFRAME_ENTRY(nsITextControlFrame)
65   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
66 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
67 
68 #ifdef ACCESSIBILITY
69 a11y::AccType nsTextControlFrame::AccessibleType() {
70   return a11y::eHTMLTextFieldType;
71 }
72 #endif
73 
74 #ifdef DEBUG
75 class EditorInitializerEntryTracker {
76  public:
EditorInitializerEntryTracker(nsTextControlFrame & frame)77   explicit EditorInitializerEntryTracker(nsTextControlFrame& frame)
78       : mFrame(frame), mFirstEntry(false) {
79     if (!mFrame.mInEditorInitialization) {
80       mFrame.mInEditorInitialization = true;
81       mFirstEntry = true;
82     }
83   }
~EditorInitializerEntryTracker()84   ~EditorInitializerEntryTracker() {
85     if (mFirstEntry) {
86       mFrame.mInEditorInitialization = false;
87     }
88   }
EnteredMoreThanOnce() const89   bool EnteredMoreThanOnce() const { return !mFirstEntry; }
90 
91  private:
92   nsTextControlFrame& mFrame;
93   bool mFirstEntry;
94 };
95 #endif
96 
97 class nsTextControlFrame::nsAnonDivObserver final
98     : public nsStubMutationObserver {
99  public:
nsAnonDivObserver(nsTextControlFrame & aFrame)100   explicit nsAnonDivObserver(nsTextControlFrame& aFrame) : mFrame(aFrame) {}
101   NS_DECL_ISUPPORTS
102   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
103   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
104   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
105   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
106 
107  private:
108   ~nsAnonDivObserver() = default;
109   nsTextControlFrame& mFrame;
110 };
111 
nsTextControlFrame(ComputedStyle * aStyle,nsPresContext * aPresContext,nsIFrame::ClassID aClassID)112 nsTextControlFrame::nsTextControlFrame(ComputedStyle* aStyle,
113                                        nsPresContext* aPresContext,
114                                        nsIFrame::ClassID aClassID)
115     : nsContainerFrame(aStyle, aPresContext, aClassID),
116       mFirstBaseline(NS_INTRINSIC_ISIZE_UNKNOWN),
117       mEditorHasBeenInitialized(false),
118       mIsProcessing(false)
119 #ifdef DEBUG
120       ,
121       mInEditorInitialization(false)
122 #endif
123 {
124   ClearCachedValue();
125 }
126 
127 nsTextControlFrame::~nsTextControlFrame() = default;
128 
GetScrollTargetFrame() const129 nsIScrollableFrame* nsTextControlFrame::GetScrollTargetFrame() const {
130   if (!mRootNode) {
131     return nullptr;
132   }
133   return do_QueryFrame(mRootNode->GetPrimaryFrame());
134 }
135 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)136 void nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot,
137                                      PostDestroyData& aPostDestroyData) {
138   RemoveProperty(TextControlInitializer());
139 
140   // Unbind the text editor state object from the frame.  The editor will live
141   // on, but things like controllers will be released.
142   RefPtr<TextControlElement> textControlElement =
143       TextControlElement::FromNode(GetContent());
144   MOZ_ASSERT(textControlElement);
145   textControlElement->UnbindFromFrame(this);
146 
147   if (mMutationObserver) {
148     mRootNode->RemoveMutationObserver(mMutationObserver);
149     mMutationObserver = nullptr;
150   }
151 
152   // If we're a subclass like nsNumberControlFrame, then it owns the root of the
153   // anonymous subtree where mRootNode is.
154   aPostDestroyData.AddAnonymousContent(mRootNode.forget());
155   aPostDestroyData.AddAnonymousContent(mPlaceholderDiv.forget());
156   aPostDestroyData.AddAnonymousContent(mPreviewDiv.forget());
157 
158   nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
159 }
160 
CalcIntrinsicSize(gfxContext * aRenderingContext,WritingMode aWM,float aFontSizeInflation) const161 LogicalSize nsTextControlFrame::CalcIntrinsicSize(
162     gfxContext* aRenderingContext, WritingMode aWM,
163     float aFontSizeInflation) const {
164   LogicalSize intrinsicSize(aWM);
165   // Get leading and the Average/MaxAdvance char width
166   nscoord lineHeight = 0;
167   nscoord charWidth = 0;
168   nscoord charMaxAdvance = 0;
169 
170   RefPtr<nsFontMetrics> fontMet =
171       nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
172 
173   lineHeight =
174       ReflowInput::CalcLineHeight(GetContent(), Style(), PresContext(),
175                                   NS_UNCONSTRAINEDSIZE, aFontSizeInflation);
176   charWidth = fontMet->AveCharWidth();
177   charMaxAdvance = fontMet->MaxAdvance();
178 
179   // Set the width equal to the width in characters
180   int32_t cols = GetCols();
181   intrinsicSize.ISize(aWM) = cols * charWidth;
182 
183   // To better match IE, take the maximum character width(in twips) and remove
184   // 4 pixels add this on as additional padding(internalPadding). But only do
185   // this if we think we have a fixed-width font.
186   if (mozilla::Abs(charWidth - charMaxAdvance) >
187       (unsigned)nsPresContext::CSSPixelsToAppUnits(1)) {
188     nscoord internalPadding =
189         std::max(0, charMaxAdvance - nsPresContext::CSSPixelsToAppUnits(4));
190     nscoord t = nsPresContext::CSSPixelsToAppUnits(1);
191     // Round to a multiple of t
192     nscoord rest = internalPadding % t;
193     if (rest < t - rest) {
194       internalPadding -= rest;
195     } else {
196       internalPadding += t - rest;
197     }
198     // Now add the extra padding on (so that small input sizes work well)
199     intrinsicSize.ISize(aWM) += internalPadding;
200   } else {
201     // This is to account for the anonymous <br> having a 1 twip width
202     // in Full Standards mode, see BRFrame::Reflow and bug 228752.
203     if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
204       intrinsicSize.ISize(aWM) += 1;
205     }
206   }
207 
208   // Increment width with cols * letter-spacing.
209   {
210     const StyleLength& letterSpacing = StyleText()->mLetterSpacing;
211     if (!letterSpacing.IsZero()) {
212       intrinsicSize.ISize(aWM) += cols * letterSpacing.ToAppUnits();
213     }
214   }
215 
216   // Set the height equal to total number of rows (times the height of each
217   // line, of course)
218   intrinsicSize.BSize(aWM) = lineHeight * GetRows();
219 
220   // Add in the size of the scrollbars for textarea
221   if (IsTextArea()) {
222     nsIScrollableFrame* scrollableFrame = GetScrollTargetFrame();
223     NS_ASSERTION(scrollableFrame, "Child must be scrollable");
224 
225     if (scrollableFrame) {
226       LogicalMargin scrollbarSizes(
227           aWM, scrollableFrame->GetDesiredScrollbarSizes(PresContext(),
228                                                          aRenderingContext));
229 
230       intrinsicSize.ISize(aWM) += scrollbarSizes.IStartEnd(aWM);
231       intrinsicSize.BSize(aWM) += scrollbarSizes.BStartEnd(aWM);
232     }
233   }
234   return intrinsicSize;
235 }
236 
EnsureEditorInitialized()237 nsresult nsTextControlFrame::EnsureEditorInitialized() {
238   // This method initializes our editor, if needed.
239 
240   // This code used to be called from CreateAnonymousContent(), but
241   // when the editor set the initial string, it would trigger a
242   // PresShell listener which called FlushPendingNotifications()
243   // during frame construction. This was causing other form controls
244   // to display wrong values.  Additionally, calling this every time
245   // a text frame control is instantiated means that we're effectively
246   // instantiating the editor for all text fields, even if they
247   // never get used.  So, now this method is being called lazily only
248   // when we actually need an editor.
249 
250   if (mEditorHasBeenInitialized) return NS_OK;
251 
252   Document* doc = mContent->GetComposedDoc();
253   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
254 
255   AutoWeakFrame weakFrame(this);
256 
257   // Flush out content on our document.  Have to do this, because script
258   // blockers don't prevent the sink flushing out content and notifying in the
259   // process, which can destroy frames.
260   doc->FlushPendingNotifications(FlushType::ContentAndNotify);
261   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
262 
263   // Make sure that editor init doesn't do things that would kill us off
264   // (especially off the script blockers it'll create for its DOM mutations).
265   {
266     RefPtr<TextControlElement> textControlElement =
267         TextControlElement::FromNode(GetContent());
268     MOZ_ASSERT(textControlElement);
269 
270     // Hide selection changes during the initialization, as webpages should not
271     // be aware of these initializations
272     AutoHideSelectionChanges hideSelectionChanges(
273         textControlElement->GetConstFrameSelection());
274 
275     nsAutoScriptBlocker scriptBlocker;
276 
277     // Time to mess with our security context... See comments in GetValue()
278     // for why this is needed.
279     mozilla::dom::AutoNoJSAPI nojsapi;
280 
281     // Make sure that we try to focus the content even if the method fails
282     class EnsureSetFocus {
283      public:
284       explicit EnsureSetFocus(nsTextControlFrame* aFrame) : mFrame(aFrame) {}
285       ~EnsureSetFocus() {
286         if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
287           mFrame->SetFocus(true, false);
288       }
289 
290      private:
291       nsTextControlFrame* mFrame;
292     };
293     EnsureSetFocus makeSureSetFocusHappens(this);
294 
295 #ifdef DEBUG
296     // Make sure we are not being called again until we're finished.
297     // If reentrancy happens, just pretend that we don't have an editor.
298     const EditorInitializerEntryTracker tracker(*this);
299     NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
300                  "EnsureEditorInitialized has been called while a previous "
301                  "call was in progress");
302 #endif
303 
304     // Create an editor for the frame, if one doesn't already exist
305     nsresult rv = textControlElement->CreateEditor();
306     NS_ENSURE_SUCCESS(rv, rv);
307     NS_ENSURE_STATE(weakFrame.IsAlive());
308 
309     // Set mEditorHasBeenInitialized so that subsequent calls will use the
310     // editor.
311     mEditorHasBeenInitialized = true;
312 
313     if (weakFrame.IsAlive()) {
314       uint32_t position = 0;
315 
316       // Set the selection to the end of the text field (bug 1287655),
317       // but only if the contents has changed (bug 1337392).
318       if (textControlElement->ValueChanged()) {
319         nsAutoString val;
320         textControlElement->GetTextEditorValue(val, true);
321         position = val.Length();
322       }
323 
324       SetSelectionEndPoints(position, position);
325     }
326   }
327   NS_ENSURE_STATE(weakFrame.IsAlive());
328   return NS_OK;
329 }
330 
MakeAnonElement(PseudoStyleType aPseudoType,Element * aParent,nsAtom * aTag) const331 already_AddRefed<Element> nsTextControlFrame::MakeAnonElement(
332     PseudoStyleType aPseudoType, Element* aParent, nsAtom* aTag) const {
333   MOZ_ASSERT(aPseudoType != PseudoStyleType::NotPseudo);
334   Document* doc = PresContext()->Document();
335   RefPtr<Element> element = doc->CreateHTMLElement(aTag);
336   element->SetPseudoElementType(aPseudoType);
337   if (aPseudoType == PseudoStyleType::mozTextControlEditingRoot) {
338     // Make our root node editable
339     element->SetFlags(NODE_IS_EDITABLE);
340   }
341 
342   if (aPseudoType == PseudoStyleType::mozNumberSpinDown ||
343       aPseudoType == PseudoStyleType::mozNumberSpinUp) {
344     element->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_hidden, u"true"_ns,
345                      false);
346   }
347 
348   if (aParent) {
349     aParent->AppendChildTo(element, false, IgnoreErrors());
350   }
351 
352   return element.forget();
353 }
354 
MakeAnonDivWithTextNode(PseudoStyleType aPseudoType) const355 already_AddRefed<Element> nsTextControlFrame::MakeAnonDivWithTextNode(
356     PseudoStyleType aPseudoType) const {
357   RefPtr<Element> div = MakeAnonElement(aPseudoType);
358 
359   // Create the text node for the anonymous <div> element.
360   nsNodeInfoManager* nim = div->OwnerDoc()->NodeInfoManager();
361   RefPtr<nsTextNode> textNode = new (nim) nsTextNode(nim);
362   // If the anonymous div element is not for the placeholder, we should
363   // mark the text node as "maybe modified frequently" for avoiding ASCII
364   // range checks at every input.
365   if (aPseudoType != PseudoStyleType::placeholder) {
366     textNode->MarkAsMaybeModifiedFrequently();
367     // Additionally, this is a password field, the text node needs to be
368     // marked as "maybe masked" unless it's in placeholder.
369     if (IsPasswordTextControl()) {
370       textNode->MarkAsMaybeMasked();
371     }
372   }
373   div->AppendChildTo(textNode, false, IgnoreErrors());
374   return div.forget();
375 }
376 
CreateAnonymousContent(nsTArray<ContentInfo> & aElements)377 nsresult nsTextControlFrame::CreateAnonymousContent(
378     nsTArray<ContentInfo>& aElements) {
379   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
380   MOZ_ASSERT(mContent, "We should have a content!");
381 
382   AddStateBits(NS_FRAME_INDEPENDENT_SELECTION);
383 
384   RefPtr<TextControlElement> textControlElement =
385       TextControlElement::FromNode(GetContent());
386   MOZ_ASSERT(textControlElement);
387   mRootNode = MakeAnonElement(PseudoStyleType::mozTextControlEditingRoot);
388   if (NS_WARN_IF(!mRootNode)) {
389     return NS_ERROR_FAILURE;
390   }
391 
392   mMutationObserver = new nsAnonDivObserver(*this);
393   mRootNode->AddMutationObserver(mMutationObserver);
394 
395   // Bind the frame to its text control.
396   //
397   // This can realistically fail in paginated mode, where we may replicate
398   // fixed-positioned elements and the replicated frame will not get the chance
399   // to get an editor.
400   nsresult rv = textControlElement->BindToFrame(this);
401   if (NS_WARN_IF(NS_FAILED(rv))) {
402     mRootNode->RemoveMutationObserver(mMutationObserver);
403     mMutationObserver = nullptr;
404     mRootNode = nullptr;
405     return rv;
406   }
407 
408   CreatePlaceholderIfNeeded();
409   if (mPlaceholderDiv) {
410     aElements.AppendElement(mPlaceholderDiv);
411   }
412   CreatePreviewIfNeeded();
413   if (mPreviewDiv) {
414     aElements.AppendElement(mPreviewDiv);
415   }
416 
417   // NOTE(emilio): We want the root node always after the placeholder so that
418   // background on the placeholder doesn't obscure the caret.
419   aElements.AppendElement(mRootNode);
420 
421   rv = UpdateValueDisplay(false);
422   NS_ENSURE_SUCCESS(rv, rv);
423 
424   InitializeEagerlyIfNeeded();
425   return NS_OK;
426 }
427 
ShouldInitializeEagerly() const428 bool nsTextControlFrame::ShouldInitializeEagerly() const {
429   // textareas are eagerly initialized.
430   if (!IsSingleLineTextControl()) {
431     return true;
432   }
433 
434   // Also, input elements which have a cached selection should get eager
435   // editor initialization.
436   TextControlElement* textControlElement =
437       TextControlElement::FromNode(GetContent());
438   MOZ_ASSERT(textControlElement);
439   if (textControlElement->HasCachedSelection()) {
440     return true;
441   }
442 
443   // So do input text controls with spellcheck=true
444   if (auto* htmlElement = nsGenericHTMLElement::FromNode(mContent)) {
445     if (htmlElement->Spellcheck()) {
446       return true;
447     }
448   }
449 
450   return false;
451 }
452 
InitializeEagerlyIfNeeded()453 void nsTextControlFrame::InitializeEagerlyIfNeeded() {
454   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
455              "Someone forgot a script blocker?");
456   if (!ShouldInitializeEagerly()) {
457     return;
458   }
459 
460   EditorInitializer* initializer = new EditorInitializer(this);
461   SetProperty(TextControlInitializer(), initializer);
462   nsContentUtils::AddScriptRunner(initializer);
463 }
464 
CreatePlaceholderIfNeeded()465 void nsTextControlFrame::CreatePlaceholderIfNeeded() {
466   MOZ_ASSERT(!mPlaceholderDiv);
467 
468   // Do we need a placeholder node?
469   nsAutoString placeholder;
470   if (!mContent->AsElement()->GetAttr(nsGkAtoms::placeholder, placeholder)) {
471     return;
472   }
473 
474   mPlaceholderDiv = MakeAnonDivWithTextNode(PseudoStyleType::placeholder);
475   UpdatePlaceholderText(placeholder, false);
476 }
477 
PlaceholderChanged(const nsAttrValue * aOld,const nsAttrValue * aNew)478 void nsTextControlFrame::PlaceholderChanged(const nsAttrValue* aOld,
479                                             const nsAttrValue* aNew) {
480   if (!aOld || !aNew) {
481     return;  // This should be handled by GetAttributeChangeHint.
482   }
483 
484   // If we've changed the attribute but we still haven't reframed, there's
485   // nothing to do either.
486   if (!mPlaceholderDiv) {
487     return;
488   }
489 
490   nsAutoString placeholder;
491   aNew->ToString(placeholder);
492   UpdatePlaceholderText(placeholder, true);
493 }
494 
UpdatePlaceholderText(nsString & aPlaceholder,bool aNotify)495 void nsTextControlFrame::UpdatePlaceholderText(nsString& aPlaceholder,
496                                                bool aNotify) {
497   MOZ_DIAGNOSTIC_ASSERT(mPlaceholderDiv);
498   MOZ_DIAGNOSTIC_ASSERT(mPlaceholderDiv->GetFirstChild());
499 
500   if (IsTextArea()) {  // <textarea>s preserve newlines...
501     nsContentUtils::PlatformToDOMLineBreaks(aPlaceholder);
502   } else {  // ...<input>s don't
503     nsContentUtils::RemoveNewlines(aPlaceholder);
504   }
505 
506   mPlaceholderDiv->GetFirstChild()->AsText()->SetText(aPlaceholder, aNotify);
507 }
508 
CreatePreviewIfNeeded()509 void nsTextControlFrame::CreatePreviewIfNeeded() {
510   RefPtr<TextControlElement> textControlElement =
511       TextControlElement::FromNode(GetContent());
512   MOZ_ASSERT(textControlElement);
513   if (!textControlElement->IsPreviewEnabled()) {
514     return;
515   }
516 
517   mPreviewDiv = MakeAnonDivWithTextNode(PseudoStyleType::mozTextControlPreview);
518 }
519 
AppendAnonymousContentTo(nsTArray<nsIContent * > & aElements,uint32_t aFilter)520 void nsTextControlFrame::AppendAnonymousContentTo(
521     nsTArray<nsIContent*>& aElements, uint32_t aFilter) {
522   if (mPlaceholderDiv && !(aFilter & nsIContent::eSkipPlaceholderContent)) {
523     aElements.AppendElement(mPlaceholderDiv);
524   }
525 
526   if (mPreviewDiv) {
527     aElements.AppendElement(mPreviewDiv);
528   }
529 
530   aElements.AppendElement(mRootNode);
531 }
532 
GetPrefISize(gfxContext * aRenderingContext)533 nscoord nsTextControlFrame::GetPrefISize(gfxContext* aRenderingContext) {
534   nscoord result = 0;
535   DISPLAY_PREF_INLINE_SIZE(this, result);
536   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
537   WritingMode wm = GetWritingMode();
538   result = CalcIntrinsicSize(aRenderingContext, wm, inflation).ISize(wm);
539   return result;
540 }
541 
GetMinISize(gfxContext * aRenderingContext)542 nscoord nsTextControlFrame::GetMinISize(gfxContext* aRenderingContext) {
543   // Our min inline size is just our preferred width if we have auto inline size
544   nscoord result;
545   DISPLAY_MIN_INLINE_SIZE(this, result);
546   result = GetPrefISize(aRenderingContext);
547   return result;
548 }
549 
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)550 LogicalSize nsTextControlFrame::ComputeAutoSize(
551     gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
552     nscoord aAvailableISize, const LogicalSize& aMargin,
553     const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
554     ComputeSizeFlags aFlags) {
555   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
556   LogicalSize autoSize = CalcIntrinsicSize(aRenderingContext, aWM, inflation);
557 
558   // Note: nsContainerFrame::ComputeAutoSize only computes the inline-size (and
559   // only for 'auto'), the block-size it returns is always NS_UNCONSTRAINEDSIZE.
560   const auto& styleISize = aSizeOverrides.mStyleISize
561                                ? *aSizeOverrides.mStyleISize
562                                : StylePosition()->ISize(aWM);
563   if (styleISize.IsAuto()) {
564     if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
565       // CalcIntrinsicSize isn't aware of grid-item margin-box clamping, so we
566       // fall back to nsContainerFrame's ComputeAutoSize to handle that.
567       // XXX maybe a font-inflation issue here? (per the assertion below).
568       autoSize.ISize(aWM) =
569           nsContainerFrame::ComputeAutoSize(
570               aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
571               aBorderPadding, aSizeOverrides, aFlags)
572               .ISize(aWM);
573     }
574 #ifdef DEBUG
575     else {
576       LogicalSize ancestorAutoSize = nsContainerFrame::ComputeAutoSize(
577           aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
578           aBorderPadding, aSizeOverrides, aFlags);
579       // Disabled when there's inflation; see comment in GetXULPrefSize.
580       MOZ_ASSERT(inflation != 1.0f ||
581                      ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM),
582                  "Incorrect size computed by ComputeAutoSize?");
583     }
584 #endif
585   }
586   return autoSize;
587 }
588 
ComputeBaseline(const nsIFrame * aFrame,const ReflowInput & aReflowInput,bool aForSingleLineControl)589 Maybe<nscoord> nsTextControlFrame::ComputeBaseline(
590     const nsIFrame* aFrame, const ReflowInput& aReflowInput,
591     bool aForSingleLineControl) {
592   // If we're layout-contained, we have no baseline.
593   if (aReflowInput.mStyleDisplay->IsContainLayout()) {
594     return Nothing();
595   }
596   WritingMode wm = aReflowInput.GetWritingMode();
597 
598   // Calculate the baseline and store it in mFirstBaseline.
599   nscoord lineHeight = aReflowInput.ComputedBSize();
600   float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
601   if (!aForSingleLineControl || lineHeight == NS_UNCONSTRAINEDSIZE) {
602     lineHeight = ReflowInput::CalcLineHeight(
603         aFrame->GetContent(), aFrame->Style(), aFrame->PresContext(),
604         NS_UNCONSTRAINEDSIZE, inflation);
605   }
606   RefPtr<nsFontMetrics> fontMet =
607       nsLayoutUtils::GetFontMetricsForFrame(aFrame, inflation);
608   return Some(nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight,
609                                                      wm.IsLineInverted()) +
610               aReflowInput.ComputedLogicalBorderPadding(wm).BStart(wm));
611 }
612 
IsButtonBox(const nsIFrame * aFrame)613 static bool IsButtonBox(const nsIFrame* aFrame) {
614   auto pseudoType = aFrame->Style()->GetPseudoType();
615   return pseudoType == PseudoStyleType::mozNumberSpinBox ||
616          pseudoType == PseudoStyleType::mozSearchClearButton;
617 }
618 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)619 void nsTextControlFrame::Reflow(nsPresContext* aPresContext,
620                                 ReflowOutput& aDesiredSize,
621                                 const ReflowInput& aReflowInput,
622                                 nsReflowStatus& aStatus) {
623   MarkInReflow();
624   DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
625   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
626   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
627 
628   // set values of reflow's out parameters
629   WritingMode wm = aReflowInput.GetWritingMode();
630   aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
631 
632   {
633     auto baseline =
634         ComputeBaseline(this, aReflowInput, IsSingleLineTextControl());
635     mFirstBaseline = baseline.valueOr(NS_INTRINSIC_ISIZE_UNKNOWN);
636     if (baseline) {
637       aDesiredSize.SetBlockStartAscent(*baseline);
638     }
639   }
640 
641   // overflow handling
642   aDesiredSize.SetOverflowAreasToDesiredBounds();
643 
644   nsIFrame* buttonBox = [&]() -> nsIFrame* {
645     nsIFrame* last = mFrames.LastChild();
646     if (!last || !IsButtonBox(last)) {
647       return nullptr;
648     }
649     return last;
650   }();
651 
652   // Reflow the button box first, so that we can use its size for the other
653   // frames.
654   nscoord buttonBoxISize = 0;
655   if (buttonBox) {
656     ReflowTextControlChild(buttonBox, aPresContext, aReflowInput, aStatus,
657                            aDesiredSize, buttonBoxISize);
658   }
659 
660   // perform reflow on all kids
661   nsIFrame* kid = mFrames.FirstChild();
662   while (kid) {
663     if (kid != buttonBox) {
664       MOZ_ASSERT(!IsButtonBox(kid),
665                  "Should only have one button box, and should be last");
666       ReflowTextControlChild(kid, aPresContext, aReflowInput, aStatus,
667                              aDesiredSize, buttonBoxISize);
668     }
669     kid = kid->GetNextSibling();
670   }
671 
672   // take into account css properties that affect overflow handling
673   FinishAndStoreOverflow(&aDesiredSize);
674 
675   aStatus.Reset();  // This type of frame can't be split.
676   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
677 }
678 
ReflowTextControlChild(nsIFrame * aKid,nsPresContext * aPresContext,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,ReflowOutput & aParentDesiredSize,nscoord & aButtonBoxISize)679 void nsTextControlFrame::ReflowTextControlChild(
680     nsIFrame* aKid, nsPresContext* aPresContext,
681     const ReflowInput& aReflowInput, nsReflowStatus& aStatus,
682     ReflowOutput& aParentDesiredSize, nscoord& aButtonBoxISize) {
683   const WritingMode outerWM = aReflowInput.GetWritingMode();
684   // compute available size and frame offsets for child
685   const WritingMode wm = aKid->GetWritingMode();
686   LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm);
687   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
688 
689   bool isButtonBox = IsButtonBox(aKid);
690 
691   ReflowInput kidReflowInput(aPresContext, aReflowInput, aKid, availSize,
692                              Nothing(), ReflowInput::InitFlag::CallerWillInit);
693 
694   // Override padding with our computed padding in case we got it from theming
695   // or percentage, if we're not the button box.
696   auto overridePadding =
697       isButtonBox ? Nothing() : Some(aReflowInput.ComputedLogicalPadding(wm));
698   // We want to let our button box fill the frame in the block axis, up to the
699   // edge of the control's border. So, we use the control's padding-box as the
700   // containing block size for our button box.
701   auto overrideCBSize =
702       isButtonBox ? Some(aReflowInput.ComputedSizeWithPadding(wm)) : Nothing();
703   kidReflowInput.Init(aPresContext, overrideCBSize, Nothing(), overridePadding);
704 
705   LogicalPoint position(wm);
706   const auto& bp = aReflowInput.ComputedLogicalBorderPadding(outerWM);
707 
708   if (!isButtonBox) {
709     MOZ_ASSERT(wm == outerWM,
710                "Shouldn't have to care about orthogonal "
711                "writing-modes and such inside the control, "
712                "except for the number spin-box which forces "
713                "horizontal-tb");
714 
715     // Offset the frame by the size of the parent's border
716     const auto& padding = aReflowInput.ComputedLogicalPadding(wm);
717     position.B(wm) = bp.BStart(wm) - padding.BStart(wm);
718     position.I(wm) = bp.IStart(wm) - padding.IStart(wm);
719 
720     // Set computed width and computed height for the child (the button box is
721     // the only exception, which has an auto size).
722     kidReflowInput.SetComputedISize(
723         std::max(0, aReflowInput.ComputedISize() - aButtonBoxISize));
724     kidReflowInput.SetComputedBSize(aReflowInput.ComputedBSize());
725   }
726 
727   // reflow the child
728   ReflowOutput desiredSize(aReflowInput);
729   const nsSize containerSize =
730       aReflowInput.ComputedSizeWithBorderPadding(outerWM).GetPhysicalSize(
731           outerWM);
732   ReflowChild(aKid, aPresContext, desiredSize, kidReflowInput, wm, position,
733               containerSize, ReflowChildFlags::Default, aStatus);
734 
735   if (isButtonBox) {
736     auto size = desiredSize.Size(outerWM);
737     // Center button in the block axis of our content box. We do this
738     // computation in terms of outerWM for simplicity.
739     position = LogicalPoint(outerWM);
740     position.B(outerWM) =
741         bp.BStart(outerWM) +
742         (aReflowInput.ComputedBSize() - size.BSize(outerWM)) / 2;
743     // Align to the inline-end of the content box.
744     position.I(outerWM) =
745         bp.IStart(outerWM) + aReflowInput.ComputedISize() - size.ISize(outerWM);
746     position = position.ConvertTo(wm, outerWM, containerSize);
747     aButtonBoxISize = size.ISize(outerWM);
748   }
749 
750   // place the child
751   FinishReflowChild(aKid, aPresContext, desiredSize, &kidReflowInput, wm,
752                     position, containerSize, ReflowChildFlags::Default);
753 
754   // consider the overflow
755   aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas);
756 }
757 
GetXULMinSize(nsBoxLayoutState & aState)758 nsSize nsTextControlFrame::GetXULMinSize(nsBoxLayoutState& aState) {
759   // XXXbz why?  Why not the nsBoxFrame sizes?
760   return nsIFrame::GetUncachedXULMinSize(aState);
761 }
762 
IsXULCollapsed()763 bool nsTextControlFrame::IsXULCollapsed() {
764   // We're never collapsed in the box sense.
765   return false;
766 }
767 
768 // IMPLEMENTING NS_IFORMCONTROLFRAME
SetFocus(bool aOn,bool aRepaint)769 void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint) {
770   TextControlElement* textControlElement =
771       TextControlElement::FromNode(GetContent());
772   MOZ_ASSERT(textControlElement);
773 
774   // If 'dom.placeholeder.show_on_focus' preference is 'false', focusing or
775   // blurring the frame can have an impact on the placeholder visibility.
776   if (!aOn) {
777     return;
778   }
779 
780   nsISelectionController* selCon = textControlElement->GetSelectionController();
781   if (!selCon) {
782     return;
783   }
784 
785   RefPtr<Selection> ourSel =
786       selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
787   if (!ourSel) {
788     return;
789   }
790 
791   mozilla::PresShell* presShell = PresContext()->GetPresShell();
792   RefPtr<nsCaret> caret = presShell->GetCaret();
793   if (!caret) {
794     return;
795   }
796 
797   // Tell the caret to use our selection
798   caret->SetSelection(ourSel);
799 
800   // mutual-exclusion: the selection is either controlled by the
801   // document or by the text input/area. Clear any selection in the
802   // document since the focus is now on our independent selection.
803 
804   RefPtr<Selection> docSel =
805       presShell->GetSelection(nsISelectionController::SELECTION_NORMAL);
806   if (!docSel) {
807     return;
808   }
809 
810   if (!docSel->IsCollapsed()) {
811     docSel->RemoveAllRanges(IgnoreErrors());
812   }
813 
814   // If the focus moved to a text control during text selection by pointer
815   // device, stop extending the selection.
816   if (RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection()) {
817     frameSelection->SetDragState(false);
818   }
819 }
820 
SetFormProperty(nsAtom * aName,const nsAString & aValue)821 nsresult nsTextControlFrame::SetFormProperty(nsAtom* aName,
822                                              const nsAString& aValue) {
823   if (!mIsProcessing) {  // some kind of lock.
824     mIsProcessing = true;
825     if (nsGkAtoms::select == aName) {
826       // Select all the text.
827       //
828       // XXX: This is lame, we can't call editor's SelectAll method
829       //      because that triggers AutoCopies in unix builds.
830       //      Instead, we have to call our own homegrown version
831       //      of select all which merely builds a range that selects
832       //      all of the content and adds that to the selection.
833 
834       AutoWeakFrame weakThis = this;
835       SelectAllOrCollapseToEndOfText(true);  // NOTE: can destroy the world
836       if (!weakThis.IsAlive()) {
837         return NS_OK;
838       }
839     }
840     mIsProcessing = false;
841   }
842   return NS_OK;
843 }
844 
GetTextEditor()845 already_AddRefed<TextEditor> nsTextControlFrame::GetTextEditor() {
846   if (NS_WARN_IF(NS_FAILED(EnsureEditorInitialized()))) {
847     return nullptr;
848   }
849 
850   RefPtr<TextControlElement> textControlElement =
851       TextControlElement::FromNode(GetContent());
852   MOZ_ASSERT(textControlElement);
853   RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor();
854   return textEditor.forget();
855 }
856 
SetSelectionInternal(nsINode * aStartNode,uint32_t aStartOffset,nsINode * aEndNode,uint32_t aEndOffset,nsITextControlFrame::SelectionDirection aDirection)857 nsresult nsTextControlFrame::SetSelectionInternal(
858     nsINode* aStartNode, uint32_t aStartOffset, nsINode* aEndNode,
859     uint32_t aEndOffset, nsITextControlFrame::SelectionDirection aDirection) {
860   // Get the selection, clear it and add the new range to it!
861   TextControlElement* textControlElement =
862       TextControlElement::FromNode(GetContent());
863   MOZ_ASSERT(textControlElement);
864   nsISelectionController* selCon = textControlElement->GetSelectionController();
865   NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
866 
867   RefPtr<Selection> selection =
868       selCon->GetSelection(nsISelectionController::SELECTION_NORMAL);
869   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
870 
871   nsDirection direction;
872   if (aDirection == eNone) {
873     // Preserve the direction
874     direction = selection->GetDirection();
875   } else {
876     direction = (aDirection == eBackward) ? eDirPrevious : eDirNext;
877   }
878 
879   MOZ_TRY(selection->SetStartAndEndInLimiter(*aStartNode, aStartOffset,
880                                              *aEndNode, aEndOffset, direction,
881                                              nsISelectionListener::JS_REASON));
882   return NS_OK;
883 }
884 
ScrollSelectionIntoViewAsync(ScrollAncestors aScrollAncestors)885 void nsTextControlFrame::ScrollSelectionIntoViewAsync(
886     ScrollAncestors aScrollAncestors) {
887   auto* textControlElement = TextControlElement::FromNode(GetContent());
888   MOZ_ASSERT(textControlElement);
889   nsISelectionController* selCon = textControlElement->GetSelectionController();
890   if (!selCon) {
891     return;
892   }
893 
894   int16_t flags = aScrollAncestors == ScrollAncestors::Yes
895                       ? 0
896                       : nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY;
897 
898   // Scroll the selection into view (see bug 231389).
899   selCon->ScrollSelectionIntoView(
900       nsISelectionController::SELECTION_NORMAL,
901       nsISelectionController::SELECTION_FOCUS_REGION, flags);
902 }
903 
SelectAllOrCollapseToEndOfText(bool aSelect)904 nsresult nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect) {
905   nsresult rv = EnsureEditorInitialized();
906   if (NS_WARN_IF(NS_FAILED(rv))) {
907     return rv;
908   }
909 
910   RefPtr<nsINode> rootNode = mRootNode;
911   NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
912 
913   RefPtr<Text> text = Text::FromNodeOrNull(rootNode->GetFirstChild());
914   MOZ_ASSERT(text);
915 
916   uint32_t length = text->Length();
917 
918   rv = SetSelectionInternal(text, aSelect ? 0 : length, text, length);
919   NS_ENSURE_SUCCESS(rv, rv);
920 
921   ScrollSelectionIntoViewAsync();
922   return NS_OK;
923 }
924 
SetSelectionEndPoints(uint32_t aSelStart,uint32_t aSelEnd,nsITextControlFrame::SelectionDirection aDirection)925 nsresult nsTextControlFrame::SetSelectionEndPoints(
926     uint32_t aSelStart, uint32_t aSelEnd,
927     nsITextControlFrame::SelectionDirection aDirection) {
928   NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
929 
930   if (aSelStart > aSelEnd) return NS_ERROR_FAILURE;
931 
932   nsCOMPtr<nsINode> startNode, endNode;
933   uint32_t startOffset, endOffset;
934 
935   // Calculate the selection start point.
936 
937   nsresult rv =
938       OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
939 
940   NS_ENSURE_SUCCESS(rv, rv);
941 
942   if (aSelStart == aSelEnd) {
943     // Collapsed selection, so start and end are the same!
944     endNode = startNode;
945     endOffset = startOffset;
946   } else {
947     // Selection isn't collapsed so we have to calculate
948     // the end point too.
949 
950     rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
951 
952     NS_ENSURE_SUCCESS(rv, rv);
953   }
954 
955   return SetSelectionInternal(startNode, startOffset, endNode, endOffset,
956                               aDirection);
957 }
958 
959 NS_IMETHODIMP
SetSelectionRange(uint32_t aSelStart,uint32_t aSelEnd,nsITextControlFrame::SelectionDirection aDirection)960 nsTextControlFrame::SetSelectionRange(
961     uint32_t aSelStart, uint32_t aSelEnd,
962     nsITextControlFrame::SelectionDirection aDirection) {
963   nsresult rv = EnsureEditorInitialized();
964   NS_ENSURE_SUCCESS(rv, rv);
965 
966   if (aSelStart > aSelEnd) {
967     // Simulate what we'd see SetSelectionStart() was called, followed
968     // by a SetSelectionEnd().
969 
970     aSelStart = aSelEnd;
971   }
972 
973   return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection);
974 }
975 
OffsetToDOMPoint(uint32_t aOffset,nsINode ** aResult,uint32_t * aPosition)976 nsresult nsTextControlFrame::OffsetToDOMPoint(uint32_t aOffset,
977                                               nsINode** aResult,
978                                               uint32_t* aPosition) {
979   NS_ENSURE_ARG_POINTER(aResult && aPosition);
980 
981   *aResult = nullptr;
982   *aPosition = 0;
983 
984   nsresult rv = EnsureEditorInitialized();
985   if (NS_WARN_IF(NS_FAILED(rv))) {
986     return rv;
987   }
988 
989   RefPtr<Element> rootNode = mRootNode;
990   NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
991 
992   nsCOMPtr<nsINodeList> nodeList = rootNode->ChildNodes();
993   uint32_t length = nodeList->Length();
994 
995   NS_ASSERTION(length <= 2,
996                "We should have one text node and one mozBR at most");
997 
998   nsCOMPtr<nsINode> firstNode = nodeList->Item(0);
999   Text* textNode = firstNode ? firstNode->GetAsText() : nullptr;
1000 
1001   if (length == 0) {
1002     rootNode.forget(aResult);
1003     *aPosition = 0;
1004   } else if (textNode) {
1005     uint32_t textLength = textNode->Length();
1006     firstNode.forget(aResult);
1007     *aPosition = std::min(aOffset, textLength);
1008   } else {
1009     rootNode.forget(aResult);
1010     *aPosition = 0;
1011   }
1012 
1013   return NS_OK;
1014 }
1015 
1016 /////END INTERFACE IMPLEMENTATIONS
1017 
1018 ////NSIFRAME
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)1019 nsresult nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
1020                                               nsAtom* aAttribute,
1021                                               int32_t aModType) {
1022   auto* textControlElement = TextControlElement::FromNode(GetContent());
1023   MOZ_ASSERT(textControlElement);
1024   nsISelectionController* selCon = textControlElement->GetSelectionController();
1025   const bool needEditor =
1026       nsGkAtoms::maxlength == aAttribute || nsGkAtoms::readonly == aAttribute ||
1027       nsGkAtoms::disabled == aAttribute || nsGkAtoms::spellcheck == aAttribute;
1028   RefPtr<TextEditor> textEditor = needEditor ? GetTextEditor() : nullptr;
1029   if ((needEditor && !textEditor) || !selCon) {
1030     return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
1031                                               aModType);
1032   }
1033 
1034   if (nsGkAtoms::maxlength == aAttribute) {
1035     if (textEditor) {
1036       textEditor->SetMaxTextLength(textControlElement->UsedMaxLength());
1037     }
1038     return NS_OK;
1039   }
1040 
1041   if (nsGkAtoms::readonly == aAttribute || nsGkAtoms::disabled == aAttribute) {
1042     if (AttributeExists(aAttribute)) {
1043       if (nsContentUtils::IsFocusedContent(mContent)) {
1044         selCon->SetCaretEnabled(false);
1045       }
1046       textEditor->AddFlags(nsIEditor::eEditorReadonlyMask);
1047     } else {
1048       if (!AttributeExists(aAttribute == nsGkAtoms::readonly
1049                                ? nsGkAtoms::disabled
1050                                : nsGkAtoms::readonly)) {
1051         if (nsContentUtils::IsFocusedContent(mContent)) {
1052           selCon->SetCaretEnabled(true);
1053         }
1054         textEditor->RemoveFlags(nsIEditor::eEditorReadonlyMask);
1055       }
1056     }
1057     return NS_OK;
1058   }
1059 
1060   if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) {
1061     UpdateValueDisplay(true);
1062     return NS_OK;
1063   }
1064 
1065   // Allow the base class to handle common attributes supported by all form
1066   // elements...
1067   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1068 }
1069 
GetText(nsString & aText)1070 void nsTextControlFrame::GetText(nsString& aText) {
1071   if (HTMLInputElement* inputElement = HTMLInputElement::FromNode(mContent)) {
1072     if (IsSingleLineTextControl()) {
1073       // There will be no line breaks so we can ignore the wrap property.
1074       inputElement->GetTextEditorValue(aText, true);
1075       return;
1076     }
1077     aText.Truncate();
1078     return;
1079   }
1080 
1081   MOZ_ASSERT(!IsSingleLineTextControl());
1082   if (HTMLTextAreaElement* textAreaElement =
1083           HTMLTextAreaElement::FromNode(mContent)) {
1084     textAreaElement->GetValue(aText);
1085     return;
1086   }
1087 
1088   MOZ_ASSERT(aText.IsEmpty());
1089   aText.Truncate();
1090 }
1091 
TextEquals(const nsAString & aText) const1092 bool nsTextControlFrame::TextEquals(const nsAString& aText) const {
1093   if (HTMLInputElement* inputElement = HTMLInputElement::FromNode(mContent)) {
1094     if (IsSingleLineTextControl()) {
1095       // There will be no line breaks so we can ignore the wrap property.
1096       return inputElement->TextEditorValueEquals(aText);
1097     }
1098     return aText.IsEmpty();
1099   }
1100 
1101   MOZ_ASSERT(!IsSingleLineTextControl());
1102   if (HTMLTextAreaElement* textAreaElement =
1103           HTMLTextAreaElement::FromNode(mContent)) {
1104     return textAreaElement->ValueEquals(aText);
1105   }
1106 
1107   return aText.IsEmpty();
1108 }
1109 
1110 /// END NSIFRAME OVERLOADS
1111 
1112 // NOTE(emilio): This is needed because the root->primary frame map is not set
1113 // up by the time this is called.
FindRootNodeFrame(const nsFrameList & aChildList,const nsIContent * aRoot)1114 static nsIFrame* FindRootNodeFrame(const nsFrameList& aChildList,
1115                                    const nsIContent* aRoot) {
1116   for (nsIFrame* f : aChildList) {
1117     if (f->GetContent() == aRoot) {
1118       return f;
1119     }
1120     if (nsIFrame* root = FindRootNodeFrame(f->PrincipalChildList(), aRoot)) {
1121       return root;
1122     }
1123   }
1124   return nullptr;
1125 }
1126 
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)1127 void nsTextControlFrame::SetInitialChildList(ChildListID aListID,
1128                                              nsFrameList& aChildList) {
1129   nsContainerFrame::SetInitialChildList(aListID, aChildList);
1130   if (aListID != kPrincipalList) {
1131     return;
1132   }
1133 
1134   // Mark the scroll frame as being a reflow root. This will allow incremental
1135   // reflows to be initiated at the scroll frame, rather than descending from
1136   // the root frame of the frame hierarchy.
1137   if (nsIFrame* frame = FindRootNodeFrame(PrincipalChildList(), mRootNode)) {
1138     frame->AddStateBits(NS_FRAME_REFLOW_ROOT);
1139 
1140     auto* textControlElement = TextControlElement::FromNode(GetContent());
1141     MOZ_ASSERT(textControlElement);
1142     textControlElement->InitializeKeyboardEventListeners();
1143 
1144     if (nsPoint* contentScrollPos = TakeProperty(ContentScrollPos())) {
1145       // If we have a scroll pos stored to be passed to our anonymous
1146       // div, do it here!
1147       nsIStatefulFrame* statefulFrame = do_QueryFrame(frame);
1148       NS_ASSERTION(statefulFrame,
1149                    "unexpected type of frame for the anonymous div");
1150       UniquePtr<PresState> fakePresState = NewPresState();
1151       fakePresState->scrollState() = *contentScrollPos;
1152       statefulFrame->RestoreState(fakePresState.get());
1153       delete contentScrollPos;
1154     }
1155   } else {
1156     MOZ_ASSERT(!mRootNode || PrincipalChildList().IsEmpty());
1157   }
1158 }
1159 
UpdateValueDisplay(bool aNotify,bool aBeforeEditorInit,const nsAString * aValue)1160 nsresult nsTextControlFrame::UpdateValueDisplay(bool aNotify,
1161                                                 bool aBeforeEditorInit,
1162                                                 const nsAString* aValue) {
1163   if (!IsSingleLineTextControl()) {  // textareas don't use this
1164     return NS_OK;
1165   }
1166 
1167   MOZ_ASSERT(mRootNode, "Must have a div content\n");
1168   MOZ_ASSERT(!mEditorHasBeenInitialized,
1169              "Do not call this after editor has been initialized");
1170 
1171   nsIContent* childContent = mRootNode->GetFirstChild();
1172   Text* textContent;
1173   if (!childContent) {
1174     // Set up a textnode with our value
1175     RefPtr<nsTextNode> textNode = new (mContent->NodeInfo()->NodeInfoManager())
1176         nsTextNode(mContent->NodeInfo()->NodeInfoManager());
1177     textNode->MarkAsMaybeModifiedFrequently();
1178     if (IsPasswordTextControl()) {
1179       textNode->MarkAsMaybeMasked();
1180     }
1181 
1182     mRootNode->AppendChildTo(textNode, aNotify, IgnoreErrors());
1183     textContent = textNode;
1184   } else {
1185     textContent = childContent->GetAsText();
1186   }
1187 
1188   NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
1189 
1190   TextControlElement* textControlElement =
1191       TextControlElement::FromNode(GetContent());
1192   MOZ_ASSERT(textControlElement);
1193 
1194   // Get the current value of the textfield from the content.
1195   nsAutoString value;
1196   if (aValue) {
1197     value = *aValue;
1198   } else {
1199     textControlElement->GetTextEditorValue(value, true);
1200   }
1201 
1202   return textContent->SetText(value, aNotify);
1203 }
1204 
1205 NS_IMETHODIMP
GetOwnedSelectionController(nsISelectionController ** aSelCon)1206 nsTextControlFrame::GetOwnedSelectionController(
1207     nsISelectionController** aSelCon) {
1208   NS_ENSURE_ARG_POINTER(aSelCon);
1209 
1210   TextControlElement* textControlElement =
1211       TextControlElement::FromNode(GetContent());
1212   MOZ_ASSERT(textControlElement);
1213 
1214   *aSelCon = textControlElement->GetSelectionController();
1215   NS_IF_ADDREF(*aSelCon);
1216 
1217   return NS_OK;
1218 }
1219 
GetOwnedFrameSelection()1220 nsFrameSelection* nsTextControlFrame::GetOwnedFrameSelection() {
1221   auto* textControlElement = TextControlElement::FromNode(GetContent());
1222   MOZ_ASSERT(textControlElement);
1223   return textControlElement->GetConstFrameSelection();
1224 }
1225 
SaveState()1226 UniquePtr<PresState> nsTextControlFrame::SaveState() {
1227   if (nsIStatefulFrame* scrollStateFrame =
1228           do_QueryFrame(GetScrollTargetFrame())) {
1229     return scrollStateFrame->SaveState();
1230   }
1231 
1232   return nullptr;
1233 }
1234 
1235 NS_IMETHODIMP
RestoreState(PresState * aState)1236 nsTextControlFrame::RestoreState(PresState* aState) {
1237   NS_ENSURE_ARG_POINTER(aState);
1238 
1239   // Query the nsIStatefulFrame from the HTMLScrollFrame
1240   if (nsIStatefulFrame* scrollStateFrame =
1241           do_QueryFrame(GetScrollTargetFrame())) {
1242     return scrollStateFrame->RestoreState(aState);
1243   }
1244 
1245   // Most likely, we don't have our anonymous content constructed yet, which
1246   // would cause us to end up here.  In this case, we'll just store the scroll
1247   // pos ourselves, and forward it to the scroll frame later when it's created.
1248   SetProperty(ContentScrollPos(), new nsPoint(aState->scrollState()));
1249   return NS_OK;
1250 }
1251 
PeekOffset(nsPeekOffsetStruct * aPos)1252 nsresult nsTextControlFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
1253   return NS_ERROR_FAILURE;
1254 }
1255 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)1256 void nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1257                                           const nsDisplayListSet& aLists) {
1258   DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame");
1259 
1260   DisplayBorderBackgroundOutline(aBuilder, aLists);
1261 
1262   // Redirect all lists to the Content list so that nothing can escape, ie
1263   // opacity creating stacking contexts that then get sorted with stacking
1264   // contexts external to us.
1265   nsDisplayList* content = aLists.Content();
1266   nsDisplayListSet set(content, content, content, content, content, content);
1267 
1268   for (auto* kid : mFrames) {
1269     BuildDisplayListForChild(aBuilder, kid, set);
1270   }
1271 }
1272 
1273 NS_IMETHODIMP
Run()1274 nsTextControlFrame::EditorInitializer::Run() {
1275   if (!mFrame) {
1276     return NS_OK;
1277   }
1278 
1279   // Need to block script to avoid bug 669767.
1280   nsAutoScriptBlocker scriptBlocker;
1281 
1282   RefPtr<mozilla::PresShell> presShell = mFrame->PresShell();
1283   bool observes = presShell->ObservesNativeAnonMutationsForPrint();
1284   presShell->ObserveNativeAnonMutationsForPrint(true);
1285   // This can cause the frame to be destroyed (and call Revoke()).
1286   mFrame->EnsureEditorInitialized();
1287   presShell->ObserveNativeAnonMutationsForPrint(observes);
1288 
1289   // The frame can *still* be destroyed even though we have a scriptblocker,
1290   // bug 682684.
1291   if (!mFrame) {
1292     return NS_ERROR_FAILURE;
1293   }
1294 
1295   mFrame->FinishedInitializer();
1296   return NS_OK;
1297 }
1298 
NS_IMPL_ISUPPORTS(nsTextControlFrame::nsAnonDivObserver,nsIMutationObserver)1299 NS_IMPL_ISUPPORTS(nsTextControlFrame::nsAnonDivObserver, nsIMutationObserver)
1300 
1301 void nsTextControlFrame::nsAnonDivObserver::CharacterDataChanged(
1302     nsIContent* aContent, const CharacterDataChangeInfo&) {
1303   mFrame.ClearCachedValue();
1304 }
1305 
ContentAppended(nsIContent * aFirstNewContent)1306 void nsTextControlFrame::nsAnonDivObserver::ContentAppended(
1307     nsIContent* aFirstNewContent) {
1308   mFrame.ClearCachedValue();
1309 }
1310 
ContentInserted(nsIContent * aChild)1311 void nsTextControlFrame::nsAnonDivObserver::ContentInserted(
1312     nsIContent* aChild) {
1313   mFrame.ClearCachedValue();
1314 }
1315 
ContentRemoved(nsIContent * aChild,nsIContent * aPreviousSibling)1316 void nsTextControlFrame::nsAnonDivObserver::ContentRemoved(
1317     nsIContent* aChild, nsIContent* aPreviousSibling) {
1318   mFrame.ClearCachedValue();
1319 }
1320