Bumping manifests a=b2g-bump
[gecko.git] / layout / forms / nsTextControlFrame.cpp
blobc3ca51fbff8fb815a58b6f570a53320712a2db61
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/DebugOnly.h"
8 #include "nsCOMPtr.h"
9 #include "nsFontMetrics.h"
10 #include "nsTextControlFrame.h"
11 #include "nsIPlaintextEditor.h"
12 #include "nsCaret.h"
13 #include "nsGenericHTMLElement.h"
14 #include "nsIEditor.h"
15 #include "nsIEditorIMESupport.h"
16 #include "nsIPhonetic.h"
17 #include "nsTextFragment.h"
18 #include "nsIDOMHTMLTextAreaElement.h"
19 #include "nsNameSpaceManager.h"
20 #include "nsFormControlFrame.h" //for registering accesskeys
22 #include "nsIContent.h"
23 #include "nsPresContext.h"
24 #include "nsRenderingContext.h"
25 #include "nsGkAtoms.h"
26 #include "nsLayoutUtils.h"
27 #include "nsIDOMElement.h"
28 #include "nsIDOMHTMLElement.h"
29 #include "nsIPresShell.h"
31 #include <algorithm>
32 #include "nsIDOMNodeList.h" //for selection setting helper func
33 #include "nsIDOMRange.h" //for selection setting helper func
34 #include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect.
35 #include "nsIDOMNode.h"
37 #include "nsIDOMText.h" //for multiline getselection
38 #include "nsFocusManager.h"
39 #include "nsTextEditRules.h"
40 #include "nsPresState.h"
41 #include "nsContentList.h"
42 #include "nsAttrValueInlines.h"
43 #include "mozilla/dom/Selection.h"
44 #include "nsContentUtils.h"
45 #include "nsTextNode.h"
46 #include "nsStyleSet.h"
47 #include "mozilla/dom/ScriptSettings.h"
48 #include "mozilla/MathAlgorithms.h"
50 #define DEFAULT_COLUMN_WIDTH 20
52 using namespace mozilla;
54 nsIFrame*
55 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
57 return new (aPresShell) nsTextControlFrame(aContext);
60 NS_IMPL_FRAMEARENA_HELPERS(nsTextControlFrame)
62 NS_QUERYFRAME_HEAD(nsTextControlFrame)
63 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
64 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
65 NS_QUERYFRAME_ENTRY(nsITextControlFrame)
66 NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
67 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
69 #ifdef ACCESSIBILITY
70 a11y::AccType
71 nsTextControlFrame::AccessibleType()
73 return a11y::eHTMLTextFieldType;
75 #endif
77 #ifdef DEBUG
78 class EditorInitializerEntryTracker {
79 public:
80 explicit EditorInitializerEntryTracker(nsTextControlFrame &frame)
81 : mFrame(frame)
82 , mFirstEntry(false)
84 if (!mFrame.mInEditorInitialization) {
85 mFrame.mInEditorInitialization = true;
86 mFirstEntry = true;
89 ~EditorInitializerEntryTracker()
91 if (mFirstEntry) {
92 mFrame.mInEditorInitialization = false;
95 bool EnteredMoreThanOnce() const { return !mFirstEntry; }
96 private:
97 nsTextControlFrame &mFrame;
98 bool mFirstEntry;
100 #endif
102 nsTextControlFrame::nsTextControlFrame(nsStyleContext* aContext)
103 : nsContainerFrame(aContext)
104 , mEditorHasBeenInitialized(false)
105 , mIsProcessing(false)
106 #ifdef DEBUG
107 , mInEditorInitialization(false)
108 #endif
112 nsTextControlFrame::~nsTextControlFrame()
116 void
117 nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
119 mScrollEvent.Revoke();
121 EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
122 if (initializer) {
123 initializer->Revoke();
124 Properties().Delete(TextControlInitializer());
127 // Unbind the text editor state object from the frame. The editor will live
128 // on, but things like controllers will be released.
129 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
130 NS_ASSERTION(txtCtrl, "Content not a text control element");
131 txtCtrl->UnbindFromFrame(this);
133 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
135 nsContainerFrame::DestroyFrom(aDestructRoot);
138 nsIAtom*
139 nsTextControlFrame::GetType() const
141 return nsGkAtoms::textInputFrame;
144 nsresult
145 nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext,
146 WritingMode aWM,
147 LogicalSize& aIntrinsicSize,
148 float aFontSizeInflation)
150 // Get leading and the Average/MaxAdvance char width
151 nscoord lineHeight = 0;
152 nscoord charWidth = 0;
153 nscoord charMaxAdvance = 0;
155 nsRefPtr<nsFontMetrics> fontMet;
156 nsresult rv =
157 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
158 aFontSizeInflation);
159 NS_ENSURE_SUCCESS(rv, rv);
161 lineHeight =
162 nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
163 NS_AUTOHEIGHT, aFontSizeInflation);
164 charWidth = fontMet->AveCharWidth();
165 charMaxAdvance = fontMet->MaxAdvance();
167 // Set the width equal to the width in characters
168 int32_t cols = GetCols();
169 aIntrinsicSize.ISize(aWM) = cols * charWidth;
171 // To better match IE, take the maximum character width(in twips) and remove
172 // 4 pixels add this on as additional padding(internalPadding). But only do
173 // this if we think we have a fixed-width font.
174 if (mozilla::Abs(charWidth - charMaxAdvance) > (unsigned)nsPresContext::CSSPixelsToAppUnits(1)) {
175 nscoord internalPadding =
176 std::max(0, charMaxAdvance - nsPresContext::CSSPixelsToAppUnits(4));
177 nscoord t = nsPresContext::CSSPixelsToAppUnits(1);
178 // Round to a multiple of t
179 nscoord rest = internalPadding % t;
180 if (rest < t - rest) {
181 internalPadding -= rest;
182 } else {
183 internalPadding += t - rest;
185 // Now add the extra padding on (so that small input sizes work well)
186 aIntrinsicSize.ISize(aWM) += internalPadding;
187 } else {
188 // This is to account for the anonymous <br> having a 1 twip width
189 // in Full Standards mode, see BRFrame::Reflow and bug 228752.
190 if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
191 aIntrinsicSize.ISize(aWM) += 1;
195 // Increment width with cols * letter-spacing.
197 const nsStyleCoord& lsCoord = StyleText()->mLetterSpacing;
198 if (eStyleUnit_Coord == lsCoord.GetUnit()) {
199 nscoord letterSpacing = lsCoord.GetCoordValue();
200 if (letterSpacing != 0) {
201 aIntrinsicSize.ISize(aWM) += cols * letterSpacing;
206 // Set the height equal to total number of rows (times the height of each
207 // line, of course)
208 aIntrinsicSize.BSize(aWM) = lineHeight * GetRows();
210 // Add in the size of the scrollbars for textarea
211 if (IsTextArea()) {
212 nsIFrame* first = GetFirstPrincipalChild();
214 nsIScrollableFrame *scrollableFrame = do_QueryFrame(first);
215 NS_ASSERTION(scrollableFrame, "Child must be scrollable");
217 if (scrollableFrame) {
218 nsMargin scrollbarSizes =
219 scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext);
221 aIntrinsicSize.Width(aWM) += scrollbarSizes.LeftRight();
222 aIntrinsicSize.Height(aWM) += scrollbarSizes.TopBottom();
226 return NS_OK;
229 nsresult
230 nsTextControlFrame::EnsureEditorInitialized()
232 // This method initializes our editor, if needed.
234 // This code used to be called from CreateAnonymousContent(), but
235 // when the editor set the initial string, it would trigger a
236 // PresShell listener which called FlushPendingNotifications()
237 // during frame construction. This was causing other form controls
238 // to display wrong values. Additionally, calling this every time
239 // a text frame control is instantiated means that we're effectively
240 // instantiating the editor for all text fields, even if they
241 // never get used. So, now this method is being called lazily only
242 // when we actually need an editor.
244 if (mEditorHasBeenInitialized)
245 return NS_OK;
247 nsIDocument* doc = mContent->GetComposedDoc();
248 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
250 nsWeakFrame weakFrame(this);
252 // Flush out content on our document. Have to do this, because script
253 // blockers don't prevent the sink flushing out content and notifying in the
254 // process, which can destroy frames.
255 doc->FlushPendingNotifications(Flush_ContentAndNotify);
256 NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
258 // Make sure that editor init doesn't do things that would kill us off
259 // (especially off the script blockers it'll create for its DOM mutations).
261 nsAutoScriptBlocker scriptBlocker;
263 // Time to mess with our security context... See comments in GetValue()
264 // for why this is needed.
265 mozilla::dom::AutoNoJSAPI nojsapi;
267 // Make sure that we try to focus the content even if the method fails
268 class EnsureSetFocus {
269 public:
270 explicit EnsureSetFocus(nsTextControlFrame* aFrame)
271 : mFrame(aFrame) {}
272 ~EnsureSetFocus() {
273 if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
274 mFrame->SetFocus(true, false);
276 private:
277 nsTextControlFrame *mFrame;
279 EnsureSetFocus makeSureSetFocusHappens(this);
281 #ifdef DEBUG
282 // Make sure we are not being called again until we're finished.
283 // If reentrancy happens, just pretend that we don't have an editor.
284 const EditorInitializerEntryTracker tracker(*this);
285 NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
286 "EnsureEditorInitialized has been called while a previous call was in progress");
287 #endif
289 // Create an editor for the frame, if one doesn't already exist
290 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
291 NS_ASSERTION(txtCtrl, "Content not a text control element");
292 nsresult rv = txtCtrl->CreateEditor();
293 NS_ENSURE_SUCCESS(rv, rv);
294 NS_ENSURE_STATE(weakFrame.IsAlive());
296 // Set mEditorHasBeenInitialized so that subsequent calls will use the
297 // editor.
298 mEditorHasBeenInitialized = true;
300 // Set the selection to the beginning of the text field.
301 if (weakFrame.IsAlive()) {
302 SetSelectionEndPoints(0, 0);
305 NS_ENSURE_STATE(weakFrame.IsAlive());
306 return NS_OK;
309 nsresult
310 nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
312 NS_ASSERTION(mContent, "We should have a content!");
314 mState |= NS_FRAME_INDEPENDENT_SELECTION;
316 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
317 NS_ASSERTION(txtCtrl, "Content not a text control element");
319 // Bind the frame to its text control
320 nsresult rv = txtCtrl->BindToFrame(this);
321 NS_ENSURE_SUCCESS(rv, rv);
323 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
324 NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
326 if (!aElements.AppendElement(rootNode))
327 return NS_ERROR_OUT_OF_MEMORY;
329 // Do we need a placeholder node?
330 nsAutoString placeholderTxt;
331 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
332 placeholderTxt);
333 nsContentUtils::RemoveNewlines(placeholderTxt);
334 mUsePlaceholder = !placeholderTxt.IsEmpty();
336 // Create the placeholder anonymous content if needed.
337 if (mUsePlaceholder) {
338 nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode();
339 NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
341 // Associate ::-moz-placeholder pseudo-element with the placeholder node.
342 nsCSSPseudoElements::Type pseudoType =
343 nsCSSPseudoElements::ePseudo_mozPlaceholder;
345 nsRefPtr<nsStyleContext> placeholderStyleContext =
346 PresContext()->StyleSet()->ResolvePseudoElementStyle(
347 mContent->AsElement(), pseudoType, StyleContext(),
348 placeholderNode->AsElement());
350 if (!aElements.AppendElement(ContentInfo(placeholderNode,
351 placeholderStyleContext))) {
352 return NS_ERROR_OUT_OF_MEMORY;
355 if (!IsSingleLineTextControl()) {
356 // For textareas, UpdateValueDisplay doesn't initialize the visibility
357 // status of the placeholder because it returns early, so we have to
358 // do that manually here.
359 txtCtrl->UpdatePlaceholderVisibility(true);
363 rv = UpdateValueDisplay(false);
364 NS_ENSURE_SUCCESS(rv, rv);
366 // textareas are eagerly initialized
367 bool initEagerly = !IsSingleLineTextControl();
368 if (!initEagerly) {
369 // Also, input elements which have a cached selection should get eager
370 // editor initialization.
371 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
372 NS_ASSERTION(txtCtrl, "Content not a text control element");
373 initEagerly = txtCtrl->HasCachedSelection();
375 if (!initEagerly) {
376 nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(txtCtrl);
377 if (element) {
378 // so are input text controls with spellcheck=true
379 element->GetSpellcheck(&initEagerly);
383 if (initEagerly) {
384 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
385 "Someone forgot a script blocker?");
386 EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
387 if (initializer) {
388 initializer->Revoke();
390 initializer = new EditorInitializer(this);
391 Properties().Set(TextControlInitializer(),initializer);
392 if (!nsContentUtils::AddScriptRunner(initializer)) {
393 initializer->Revoke(); // paranoia
394 Properties().Delete(TextControlInitializer());
395 delete initializer;
396 return NS_ERROR_OUT_OF_MEMORY;
400 return NS_OK;
403 void
404 nsTextControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
405 uint32_t aFilter)
407 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
408 NS_ASSERTION(txtCtrl, "Content not a text control element");
410 nsIContent* root = txtCtrl->GetRootEditorNode();
411 if (root) {
412 aElements.AppendElement(root);
415 nsIContent* placeholder = txtCtrl->GetPlaceholderNode();
416 if (placeholder && !(aFilter & nsIContent::eSkipPlaceholderContent))
417 aElements.AppendElement(placeholder);
421 nscoord
422 nsTextControlFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
424 DebugOnly<nscoord> result = 0;
425 DISPLAY_PREF_WIDTH(this, result);
427 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
428 WritingMode wm = GetWritingMode();
429 LogicalSize autoSize(wm);
430 CalcIntrinsicSize(aRenderingContext, wm, autoSize, inflation);
432 return autoSize.ISize(wm);
435 nscoord
436 nsTextControlFrame::GetMinISize(nsRenderingContext* aRenderingContext)
438 // Our min width is just our preferred width if we have auto width.
439 nscoord result;
440 DISPLAY_MIN_WIDTH(this, result);
442 result = GetPrefISize(aRenderingContext);
444 return result;
447 LogicalSize
448 nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
449 WritingMode aWM,
450 const LogicalSize& aCBSize,
451 nscoord aAvailableISize,
452 const LogicalSize& aMargin,
453 const LogicalSize& aBorder,
454 const LogicalSize& aPadding,
455 bool aShrinkWrap)
457 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
458 LogicalSize autoSize(aWM);
459 nsresult rv = CalcIntrinsicSize(aRenderingContext, aWM, autoSize, inflation);
460 if (NS_FAILED(rv)) {
461 // What now?
462 autoSize.SizeTo(aWM, 0, 0);
464 #ifdef DEBUG
465 // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width
466 else {
467 const nsStyleCoord& inlineStyleCoord =
468 aWM.IsVertical() ? StylePosition()->mHeight : StylePosition()->mWidth;
469 if (inlineStyleCoord.GetUnit() == eStyleUnit_Auto) {
470 LogicalSize ancestorAutoSize =
471 nsContainerFrame::ComputeAutoSize(aRenderingContext, aWM,
472 aCBSize, aAvailableISize,
473 aMargin, aBorder,
474 aPadding, aShrinkWrap);
475 // Disabled when there's inflation; see comment in GetPrefSize.
476 MOZ_ASSERT(inflation != 1.0f ||
477 ancestorAutoSize.ISize(aWM) == autoSize.ISize(aWM),
478 "Incorrect size computed by ComputeAutoSize?");
481 #endif
483 return autoSize;
486 void
487 nsTextControlFrame::Reflow(nsPresContext* aPresContext,
488 nsHTMLReflowMetrics& aDesiredSize,
489 const nsHTMLReflowState& aReflowState,
490 nsReflowStatus& aStatus)
492 DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
493 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
495 // make sure that the form registers itself on the initial/first reflow
496 if (mState & NS_FRAME_FIRST_REFLOW) {
497 nsFormControlFrame::RegUnRegAccessKey(this, true);
500 // set values of reflow's out parameters
501 WritingMode wm = aReflowState.GetWritingMode();
502 LogicalSize
503 finalSize(wm,
504 aReflowState.ComputedISize() +
505 aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm),
506 aReflowState.ComputedBSize() +
507 aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm));
508 aDesiredSize.SetSize(wm, finalSize);
510 // computation of the ascent wrt the input height
511 nscoord lineHeight = aReflowState.ComputedBSize();
512 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
513 if (!IsSingleLineTextControl()) {
514 lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
515 NS_AUTOHEIGHT, inflation);
517 nsRefPtr<nsFontMetrics> fontMet;
518 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
519 inflation);
520 // now adjust for our borders and padding
521 aDesiredSize.SetBlockStartAscent(
522 nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight,
523 wm.IsLineInverted()) +
524 aReflowState.ComputedLogicalBorderPadding().BStart(wm));
526 // overflow handling
527 aDesiredSize.SetOverflowAreasToDesiredBounds();
528 // perform reflow on all kids
529 nsIFrame* kid = mFrames.FirstChild();
530 while (kid) {
531 ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize);
532 kid = kid->GetNextSibling();
535 // take into account css properties that affect overflow handling
536 FinishAndStoreOverflow(&aDesiredSize);
538 aStatus = NS_FRAME_COMPLETE;
539 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
542 void
543 nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid,
544 nsPresContext* aPresContext,
545 const nsHTMLReflowState& aReflowState,
546 nsReflowStatus& aStatus,
547 nsHTMLReflowMetrics& aParentDesiredSize)
549 // compute available size and frame offsets for child
550 WritingMode wm = aKid->GetWritingMode();
551 LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm);
552 availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
554 nsHTMLReflowState kidReflowState(aPresContext, aReflowState,
555 aKid, availSize, -1, -1,
556 nsHTMLReflowState::CALLER_WILL_INIT);
557 // Override padding with our computed padding in case we got it from theming or percentage
558 kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding());
560 // Set computed width and computed height for the child
561 kidReflowState.SetComputedWidth(aReflowState.ComputedWidth());
562 kidReflowState.SetComputedHeight(aReflowState.ComputedHeight());
564 // Offset the frame by the size of the parent's border
565 nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left -
566 aReflowState.ComputedPhysicalPadding().left;
567 nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top -
568 aReflowState.ComputedPhysicalPadding().top;
570 // reflow the child
571 nsHTMLReflowMetrics desiredSize(aReflowState);
572 ReflowChild(aKid, aPresContext, desiredSize, kidReflowState,
573 xOffset, yOffset, 0, aStatus);
575 // place the child
576 FinishReflowChild(aKid, aPresContext, desiredSize,
577 &kidReflowState, xOffset, yOffset, 0);
579 // consider the overflow
580 aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas);
583 nsSize
584 nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState)
586 // XXXbz why? Why not the nsBoxFrame sizes?
587 return nsBox::GetMinSize(aState);
590 bool
591 nsTextControlFrame::IsCollapsed()
593 // We're never collapsed in the box sense.
594 return false;
597 bool
598 nsTextControlFrame::IsLeaf() const
600 return true;
603 NS_IMETHODIMP
604 nsTextControlFrame::ScrollOnFocusEvent::Run()
606 if (mFrame) {
607 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mFrame->GetContent());
608 NS_ASSERTION(txtCtrl, "Content not a text control element");
609 nsISelectionController* selCon = txtCtrl->GetSelectionController();
610 if (selCon) {
611 mFrame->mScrollEvent.Forget();
612 selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
613 nsISelectionController::SELECTION_FOCUS_REGION,
614 nsISelectionController::SCROLL_SYNCHRONOUS);
617 return NS_OK;
620 //IMPLEMENTING NS_IFORMCONTROLFRAME
621 void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint)
623 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
624 NS_ASSERTION(txtCtrl, "Content not a text control element");
626 // Revoke the previous scroll event if one exists
627 mScrollEvent.Revoke();
629 // If 'dom.placeholeder.show_on_focus' preference is 'false', focusing or
630 // blurring the frame can have an impact on the placeholder visibility.
631 if (mUsePlaceholder) {
632 txtCtrl->UpdatePlaceholderVisibility(true);
635 if (!aOn) {
636 return;
639 nsISelectionController* selCon = txtCtrl->GetSelectionController();
640 if (!selCon)
641 return;
643 nsCOMPtr<nsISelection> ourSel;
644 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
645 getter_AddRefs(ourSel));
646 if (!ourSel) return;
648 nsIPresShell* presShell = PresContext()->GetPresShell();
649 nsRefPtr<nsCaret> caret = presShell->GetCaret();
650 if (!caret) return;
652 // Scroll the current selection into view
653 nsISelection *caretSelection = caret->GetSelection();
654 const bool isFocusedRightNow = ourSel == caretSelection;
655 if (!isFocusedRightNow) {
656 // Don't scroll the current selection if we've been focused using the mouse.
657 uint32_t lastFocusMethod = 0;
658 nsIDocument* doc = GetContent()->GetComposedDoc();
659 if (doc) {
660 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
661 if (fm) {
662 fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod);
665 if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) {
666 nsRefPtr<ScrollOnFocusEvent> event = new ScrollOnFocusEvent(this);
667 nsresult rv = NS_DispatchToCurrentThread(event);
668 if (NS_SUCCEEDED(rv)) {
669 mScrollEvent = event;
674 // tell the caret to use our selection
675 caret->SetSelection(ourSel);
677 // mutual-exclusion: the selection is either controlled by the
678 // document or by the text input/area. Clear any selection in the
679 // document since the focus is now on our independent selection.
681 nsCOMPtr<nsISelectionController> selcon = do_QueryInterface(presShell);
682 nsCOMPtr<nsISelection> docSel;
683 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
684 getter_AddRefs(docSel));
685 if (!docSel) return;
687 bool isCollapsed = false;
688 docSel->GetIsCollapsed(&isCollapsed);
689 if (!isCollapsed)
690 docSel->RemoveAllRanges();
693 nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
695 if (!mIsProcessing)//some kind of lock.
697 mIsProcessing = true;
698 if (nsGkAtoms::select == aName)
700 // Select all the text.
702 // XXX: This is lame, we can't call editor's SelectAll method
703 // because that triggers AutoCopies in unix builds.
704 // Instead, we have to call our own homegrown version
705 // of select all which merely builds a range that selects
706 // all of the content and adds that to the selection.
708 nsWeakFrame weakThis = this;
709 SelectAllOrCollapseToEndOfText(true); // NOTE: can destroy the world
710 if (!weakThis.IsAlive()) {
711 return NS_OK;
714 mIsProcessing = false;
716 return NS_OK;
719 NS_IMETHODIMP
720 nsTextControlFrame::GetEditor(nsIEditor **aEditor)
722 NS_ENSURE_ARG_POINTER(aEditor);
724 nsresult rv = EnsureEditorInitialized();
725 NS_ENSURE_SUCCESS(rv, rv);
727 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
728 NS_ASSERTION(txtCtrl, "Content not a text control element");
729 *aEditor = txtCtrl->GetTextEditor();
730 NS_IF_ADDREF(*aEditor);
731 return NS_OK;
734 nsresult
735 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
736 int32_t aStartOffset,
737 nsIDOMNode *aEndNode,
738 int32_t aEndOffset,
739 nsITextControlFrame::SelectionDirection aDirection)
741 // Create a new range to represent the new selection.
742 // Note that we use a new range to avoid having to do
743 // isIncreasing checks to avoid possible errors.
745 nsRefPtr<nsRange> range = new nsRange(mContent);
746 nsresult rv = range->SetStart(aStartNode, aStartOffset);
747 NS_ENSURE_SUCCESS(rv, rv);
749 rv = range->SetEnd(aEndNode, aEndOffset);
750 NS_ENSURE_SUCCESS(rv, rv);
752 // Get the selection, clear it and add the new range to it!
753 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
754 NS_ASSERTION(txtCtrl, "Content not a text control element");
755 nsISelectionController* selCon = txtCtrl->GetSelectionController();
756 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
758 nsCOMPtr<nsISelection> selection;
759 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
760 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
762 nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection, &rv);
763 NS_ENSURE_SUCCESS(rv, rv);
765 nsDirection direction;
766 if (aDirection == eNone) {
767 // Preserve the direction
768 direction = selPriv->GetSelectionDirection();
769 } else {
770 direction = (aDirection == eBackward) ? eDirPrevious : eDirNext;
773 rv = selection->RemoveAllRanges();
774 NS_ENSURE_SUCCESS(rv, rv);
776 rv = selection->AddRange(range); // NOTE: can destroy the world
777 NS_ENSURE_SUCCESS(rv, rv);
779 selPriv->SetSelectionDirection(direction);
780 return rv;
783 nsresult
784 nsTextControlFrame::ScrollSelectionIntoView()
786 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
787 NS_ASSERTION(txtCtrl, "Content not a text control element");
788 nsISelectionController* selCon = txtCtrl->GetSelectionController();
789 if (selCon) {
790 // Scroll the selection into view (see bug 231389).
791 return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
792 nsISelectionController::SELECTION_FOCUS_REGION,
793 nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
796 return NS_ERROR_FAILURE;
799 mozilla::dom::Element*
800 nsTextControlFrame::GetRootNodeAndInitializeEditor()
802 nsCOMPtr<nsIDOMElement> root;
803 GetRootNodeAndInitializeEditor(getter_AddRefs(root));
804 nsCOMPtr<mozilla::dom::Element> rootElem = do_QueryInterface(root);
805 return rootElem;
808 nsresult
809 nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
811 NS_ENSURE_ARG_POINTER(aRootElement);
813 nsCOMPtr<nsIEditor> editor;
814 GetEditor(getter_AddRefs(editor));
815 if (!editor)
816 return NS_OK;
818 return editor->GetRootElement(aRootElement);
821 nsresult
822 nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect)
824 nsCOMPtr<nsIDOMElement> rootElement;
825 nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
826 NS_ENSURE_SUCCESS(rv, rv);
828 nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
829 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
831 NS_ENSURE_TRUE(rootNode && rootContent, NS_ERROR_FAILURE);
833 int32_t numChildren = rootContent->GetChildCount();
835 if (numChildren > 0) {
836 // We never want to place the selection after the last
837 // br under the root node!
838 nsIContent *child = rootContent->GetChildAt(numChildren - 1);
839 if (child) {
840 if (child->Tag() == nsGkAtoms::br)
841 --numChildren;
843 if (!aSelect && numChildren) {
844 child = rootContent->GetChildAt(numChildren - 1);
845 if (child && child->IsNodeOfType(nsINode::eTEXT)) {
846 rootNode = do_QueryInterface(child);
847 const nsTextFragment* fragment = child->GetText();
848 numChildren = fragment ? fragment->GetLength() : 0;
853 rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren,
854 rootNode, numChildren);
855 NS_ENSURE_SUCCESS(rv, rv);
857 return ScrollSelectionIntoView();
860 nsresult
861 nsTextControlFrame::SetSelectionEndPoints(int32_t aSelStart, int32_t aSelEnd,
862 nsITextControlFrame::SelectionDirection aDirection)
864 NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
866 if (aSelStart > aSelEnd)
867 return NS_ERROR_FAILURE;
869 nsCOMPtr<nsIDOMNode> startNode, endNode;
870 int32_t startOffset, endOffset;
872 // Calculate the selection start point.
874 nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
876 NS_ENSURE_SUCCESS(rv, rv);
878 if (aSelStart == aSelEnd) {
879 // Collapsed selection, so start and end are the same!
880 endNode = startNode;
881 endOffset = startOffset;
883 else {
884 // Selection isn't collapsed so we have to calculate
885 // the end point too.
887 rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
889 NS_ENSURE_SUCCESS(rv, rv);
892 return SetSelectionInternal(startNode, startOffset, endNode, endOffset, aDirection);
895 NS_IMETHODIMP
896 nsTextControlFrame::SetSelectionRange(int32_t aSelStart, int32_t aSelEnd,
897 nsITextControlFrame::SelectionDirection aDirection)
899 nsresult rv = EnsureEditorInitialized();
900 NS_ENSURE_SUCCESS(rv, rv);
902 if (aSelStart > aSelEnd) {
903 // Simulate what we'd see SetSelectionStart() was called, followed
904 // by a SetSelectionEnd().
906 aSelStart = aSelEnd;
909 return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection);
913 NS_IMETHODIMP
914 nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart)
916 nsresult rv = EnsureEditorInitialized();
917 NS_ENSURE_SUCCESS(rv, rv);
919 int32_t selStart = 0, selEnd = 0;
921 rv = GetSelectionRange(&selStart, &selEnd);
922 NS_ENSURE_SUCCESS(rv, rv);
924 if (aSelectionStart > selEnd) {
925 // Collapse to the new start point.
926 selEnd = aSelectionStart;
929 selStart = aSelectionStart;
931 return SetSelectionEndPoints(selStart, selEnd);
934 NS_IMETHODIMP
935 nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
937 nsresult rv = EnsureEditorInitialized();
938 NS_ENSURE_SUCCESS(rv, rv);
940 int32_t selStart = 0, selEnd = 0;
942 rv = GetSelectionRange(&selStart, &selEnd);
943 NS_ENSURE_SUCCESS(rv, rv);
945 if (aSelectionEnd < selStart) {
946 // Collapse to the new end point.
947 selStart = aSelectionEnd;
950 selEnd = aSelectionEnd;
952 return SetSelectionEndPoints(selStart, selEnd);
955 nsresult
956 nsTextControlFrame::OffsetToDOMPoint(int32_t aOffset,
957 nsIDOMNode** aResult,
958 int32_t* aPosition)
960 NS_ENSURE_ARG_POINTER(aResult && aPosition);
962 *aResult = nullptr;
963 *aPosition = 0;
965 nsCOMPtr<nsIDOMElement> rootElement;
966 nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
967 NS_ENSURE_SUCCESS(rv, rv);
968 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
970 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
972 nsCOMPtr<nsIDOMNodeList> nodeList;
974 rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
975 NS_ENSURE_SUCCESS(rv, rv);
976 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
978 uint32_t length = 0;
980 rv = nodeList->GetLength(&length);
981 NS_ENSURE_SUCCESS(rv, rv);
983 NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most");
985 nsCOMPtr<nsIDOMNode> firstNode;
986 rv = nodeList->Item(0, getter_AddRefs(firstNode));
987 NS_ENSURE_SUCCESS(rv, rv);
988 nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(firstNode);
990 if (length == 0 || aOffset < 0) {
991 NS_IF_ADDREF(*aResult = rootNode);
992 *aPosition = 0;
993 } else if (textNode) {
994 uint32_t textLength = 0;
995 textNode->GetLength(&textLength);
996 if (length == 2 && uint32_t(aOffset) == textLength) {
997 // If we're at the end of the text node and we have a trailing BR node,
998 // set the selection on the BR node.
999 NS_IF_ADDREF(*aResult = rootNode);
1000 *aPosition = 1;
1001 } else {
1002 // Otherwise, set the selection on the textnode itself.
1003 NS_IF_ADDREF(*aResult = firstNode);
1004 *aPosition = std::min(aOffset, int32_t(textLength));
1006 } else {
1007 NS_IF_ADDREF(*aResult = rootNode);
1008 *aPosition = 0;
1011 return NS_OK;
1014 NS_IMETHODIMP
1015 nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart,
1016 int32_t* aSelectionEnd,
1017 SelectionDirection* aDirection)
1019 // make sure we have an editor
1020 nsresult rv = EnsureEditorInitialized();
1021 NS_ENSURE_SUCCESS(rv, rv);
1023 if (aSelectionStart) {
1024 *aSelectionStart = 0;
1026 if (aSelectionEnd) {
1027 *aSelectionEnd = 0;
1029 if (aDirection) {
1030 *aDirection = eNone;
1033 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1034 NS_ASSERTION(txtCtrl, "Content not a text control element");
1035 nsISelectionController* selCon = txtCtrl->GetSelectionController();
1036 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
1037 nsCOMPtr<nsISelection> selection;
1038 rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1039 NS_ENSURE_SUCCESS(rv, rv);
1040 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1042 dom::Selection* sel = static_cast<dom::Selection*>(selection.get());
1043 if (aDirection) {
1044 nsDirection direction = sel->GetSelectionDirection();
1045 if (direction == eDirNext) {
1046 *aDirection = eForward;
1047 } else if (direction == eDirPrevious) {
1048 *aDirection = eBackward;
1049 } else {
1050 NS_NOTREACHED("Invalid nsDirection enum value");
1054 if (!aSelectionStart || !aSelectionEnd) {
1055 return NS_OK;
1058 mozilla::dom::Element* root = GetRootNodeAndInitializeEditor();
1059 NS_ENSURE_STATE(root);
1060 nsContentUtils::GetSelectionInTextControl(sel, root,
1061 *aSelectionStart, *aSelectionEnd);
1063 return NS_OK;
1066 /////END INTERFACE IMPLEMENTATIONS
1068 ////NSIFRAME
1069 nsresult
1070 nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
1071 nsIAtom* aAttribute,
1072 int32_t aModType)
1074 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1075 NS_ASSERTION(txtCtrl, "Content not a text control element");
1076 nsISelectionController* selCon = txtCtrl->GetSelectionController();
1077 const bool needEditor = nsGkAtoms::maxlength == aAttribute ||
1078 nsGkAtoms::readonly == aAttribute ||
1079 nsGkAtoms::disabled == aAttribute ||
1080 nsGkAtoms::spellcheck == aAttribute;
1081 nsCOMPtr<nsIEditor> editor;
1082 if (needEditor) {
1083 GetEditor(getter_AddRefs(editor));
1085 if ((needEditor && !editor) || !selCon) {
1086 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1089 if (nsGkAtoms::maxlength == aAttribute) {
1090 int32_t maxLength;
1091 bool maxDefined = GetMaxLength(&maxLength);
1092 nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor);
1093 if (textEditor) {
1094 if (maxDefined) { // set the maxLength attribute
1095 textEditor->SetMaxTextLength(maxLength);
1096 // if maxLength>docLength, we need to truncate the doc content
1097 } else { // unset the maxLength attribute
1098 textEditor->SetMaxTextLength(-1);
1101 return NS_OK;
1104 if (nsGkAtoms::readonly == aAttribute) {
1105 uint32_t flags;
1106 editor->GetFlags(&flags);
1107 if (AttributeExists(nsGkAtoms::readonly)) { // set readonly
1108 flags |= nsIPlaintextEditor::eEditorReadonlyMask;
1109 if (nsContentUtils::IsFocusedContent(mContent)) {
1110 selCon->SetCaretEnabled(false);
1112 } else { // unset readonly
1113 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
1114 if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
1115 nsContentUtils::IsFocusedContent(mContent)) {
1116 selCon->SetCaretEnabled(true);
1119 editor->SetFlags(flags);
1120 return NS_OK;
1123 if (nsGkAtoms::disabled == aAttribute) {
1124 uint32_t flags;
1125 editor->GetFlags(&flags);
1126 int16_t displaySelection = nsISelectionController::SELECTION_OFF;
1127 const bool focused = nsContentUtils::IsFocusedContent(mContent);
1128 const bool hasAttr = AttributeExists(nsGkAtoms::disabled);
1129 if (hasAttr) { // set disabled
1130 flags |= nsIPlaintextEditor::eEditorDisabledMask;
1131 } else { // unset disabled
1132 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
1133 displaySelection = focused ? nsISelectionController::SELECTION_ON
1134 : nsISelectionController::SELECTION_HIDDEN;
1136 selCon->SetDisplaySelection(displaySelection);
1137 if (focused) {
1138 selCon->SetCaretEnabled(!hasAttr);
1140 editor->SetFlags(flags);
1141 return NS_OK;
1144 if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) {
1145 UpdateValueDisplay(true);
1146 return NS_OK;
1149 // Allow the base class to handle common attributes supported by all form
1150 // elements...
1151 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1155 nsresult
1156 nsTextControlFrame::GetText(nsString& aText)
1158 nsresult rv = NS_OK;
1159 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1160 NS_ASSERTION(txtCtrl, "Content not a text control element");
1161 if (IsSingleLineTextControl()) {
1162 // There will be no line breaks so we can ignore the wrap property.
1163 txtCtrl->GetTextEditorValue(aText, true);
1164 } else {
1165 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
1166 if (textArea) {
1167 rv = textArea->GetValue(aText);
1170 return rv;
1174 nsresult
1175 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
1177 aPhonetic.Truncate(0);
1179 nsCOMPtr<nsIEditor> editor;
1180 nsresult rv = GetEditor(getter_AddRefs(editor));
1181 NS_ENSURE_SUCCESS(rv, rv);
1183 nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor);
1184 if (imeSupport) {
1185 nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
1186 if (phonetic)
1187 phonetic->GetPhonetic(aPhonetic);
1189 return NS_OK;
1192 ///END NSIFRAME OVERLOADS
1193 /////BEGIN PROTECTED METHODS
1195 bool
1196 nsTextControlFrame::GetMaxLength(int32_t* aSize)
1198 *aSize = -1;
1200 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
1201 if (content) {
1202 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength);
1203 if (attr && attr->Type() == nsAttrValue::eInteger) {
1204 *aSize = attr->GetIntegerValue();
1206 return true;
1209 return false;
1212 // END IMPLEMENTING NS_IFORMCONTROLFRAME
1214 void
1215 nsTextControlFrame::SetInitialChildList(ChildListID aListID,
1216 nsFrameList& aChildList)
1218 nsContainerFrame::SetInitialChildList(aListID, aChildList);
1220 nsIFrame* first = GetFirstPrincipalChild();
1222 // Mark the scroll frame as being a reflow root. This will allow
1223 // incremental reflows to be initiated at the scroll frame, rather
1224 // than descending from the root frame of the frame hierarchy.
1225 if (first) {
1226 first->AddStateBits(NS_FRAME_REFLOW_ROOT);
1228 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1229 NS_ASSERTION(txtCtrl, "Content not a text control element");
1230 txtCtrl->InitializeKeyboardEventListeners();
1232 nsPoint* contentScrollPos = static_cast<nsPoint*>
1233 (Properties().Get(ContentScrollPos()));
1234 if (contentScrollPos) {
1235 // If we have a scroll pos stored to be passed to our anonymous
1236 // div, do it here!
1237 nsIStatefulFrame* statefulFrame = do_QueryFrame(first);
1238 NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div");
1239 nsPresState fakePresState;
1240 fakePresState.SetScrollState(*contentScrollPos);
1241 statefulFrame->RestoreState(&fakePresState);
1242 Properties().Remove(ContentScrollPos());
1243 delete contentScrollPos;
1248 void
1249 nsTextControlFrame::SetValueChanged(bool aValueChanged)
1251 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1252 NS_ASSERTION(txtCtrl, "Content not a text control element");
1254 if (mUsePlaceholder) {
1255 nsWeakFrame weakFrame(this);
1256 txtCtrl->UpdatePlaceholderVisibility(true);
1257 if (!weakFrame.IsAlive()) {
1258 return;
1262 txtCtrl->SetValueChanged(aValueChanged);
1266 nsresult
1267 nsTextControlFrame::UpdateValueDisplay(bool aNotify,
1268 bool aBeforeEditorInit,
1269 const nsAString *aValue)
1271 if (!IsSingleLineTextControl()) // textareas don't use this
1272 return NS_OK;
1274 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1275 NS_ASSERTION(txtCtrl, "Content not a text control element");
1276 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1278 NS_PRECONDITION(rootNode, "Must have a div content\n");
1279 NS_PRECONDITION(!mEditorHasBeenInitialized,
1280 "Do not call this after editor has been initialized");
1281 NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(),
1282 "A placeholder div must exist");
1284 nsIContent *textContent = rootNode->GetChildAt(0);
1285 if (!textContent) {
1286 // Set up a textnode with our value
1287 nsRefPtr<nsTextNode> textNode =
1288 new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
1290 NS_ASSERTION(textNode, "Must have textcontent!\n");
1292 rootNode->AppendChildTo(textNode, aNotify);
1293 textContent = textNode;
1296 NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
1298 // Get the current value of the textfield from the content.
1299 nsAutoString value;
1300 if (aValue) {
1301 value = *aValue;
1302 } else {
1303 txtCtrl->GetTextEditorValue(value, true);
1306 // Update the display of the placeholder value if needed.
1307 // We don't need to do this if we're about to initialize the
1308 // editor, since EnsureEditorInitialized takes care of this.
1309 if (mUsePlaceholder && !aBeforeEditorInit)
1311 nsWeakFrame weakFrame(this);
1312 txtCtrl->UpdatePlaceholderVisibility(aNotify);
1313 NS_ENSURE_STATE(weakFrame.IsAlive());
1316 if (aBeforeEditorInit && value.IsEmpty()) {
1317 rootNode->RemoveChildAt(0, true);
1318 return NS_OK;
1321 if (!value.IsEmpty() && IsPasswordTextControl()) {
1322 nsTextEditRules::FillBufWithPWChars(&value, value.Length());
1324 return textContent->SetText(value, aNotify);
1327 NS_IMETHODIMP
1328 nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon)
1330 NS_ENSURE_ARG_POINTER(aSelCon);
1332 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1333 NS_ASSERTION(txtCtrl, "Content not a text control element");
1335 *aSelCon = txtCtrl->GetSelectionController();
1336 NS_IF_ADDREF(*aSelCon);
1338 return NS_OK;
1341 nsFrameSelection*
1342 nsTextControlFrame::GetOwnedFrameSelection()
1344 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1345 NS_ASSERTION(txtCtrl, "Content not a text control element");
1347 return txtCtrl->GetConstFrameSelection();
1350 NS_IMETHODIMP
1351 nsTextControlFrame::SaveState(nsPresState** aState)
1353 NS_ENSURE_ARG_POINTER(aState);
1355 *aState = nullptr;
1357 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1358 NS_ASSERTION(txtCtrl, "Content not a text control element");
1360 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1361 if (rootNode) {
1362 // Query the nsIStatefulFrame from the HTMLScrollFrame
1363 nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1364 if (scrollStateFrame) {
1365 return scrollStateFrame->SaveState(aState);
1369 return NS_OK;
1372 NS_IMETHODIMP
1373 nsTextControlFrame::RestoreState(nsPresState* aState)
1375 NS_ENSURE_ARG_POINTER(aState);
1377 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1378 NS_ASSERTION(txtCtrl, "Content not a text control element");
1380 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1381 if (rootNode) {
1382 // Query the nsIStatefulFrame from the HTMLScrollFrame
1383 nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1384 if (scrollStateFrame) {
1385 return scrollStateFrame->RestoreState(aState);
1389 // Most likely, we don't have our anonymous content constructed yet, which
1390 // would cause us to end up here. In this case, we'll just store the scroll
1391 // pos ourselves, and forward it to the scroll frame later when it's created.
1392 Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollState()));
1393 return NS_OK;
1396 nsresult
1397 nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos)
1399 return NS_ERROR_FAILURE;
1402 void
1403 nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1404 const nsRect& aDirtyRect,
1405 const nsDisplayListSet& aLists)
1408 * The implementation of this method is equivalent as:
1409 * nsContainerFrame::BuildDisplayList()
1410 * with the difference that we filter-out the placeholder frame when it
1411 * should not be visible.
1413 DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame");
1415 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1416 NS_ASSERTION(txtCtrl, "Content not a text control element!");
1418 DisplayBorderBackgroundOutline(aBuilder, aLists);
1420 nsIFrame* kid = mFrames.FirstChild();
1421 // Redirect all lists to the Content list so that nothing can escape, ie
1422 // opacity creating stacking contexts that then get sorted with stacking
1423 // contexts external to us.
1424 nsDisplayList* content = aLists.Content();
1425 nsDisplayListSet set(content, content, content, content, content, content);
1427 while (kid) {
1428 // If the frame is the placeholder frame, we should only show it if the
1429 // placeholder has to be visible.
1430 if (kid->GetContent() != txtCtrl->GetPlaceholderNode() ||
1431 txtCtrl->GetPlaceholderVisibility()) {
1432 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, 0);
1434 kid = kid->GetNextSibling();
1438 mozilla::dom::Element*
1439 nsTextControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
1441 if (aType == nsCSSPseudoElements::ePseudo_mozPlaceholder) {
1442 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1443 return txtCtrl->GetPlaceholderNode();
1446 return nsContainerFrame::GetPseudoElement(aType);
1449 NS_IMETHODIMP
1450 nsTextControlFrame::EditorInitializer::Run()
1452 if (!mFrame) {
1453 return NS_OK;
1456 // Need to block script to avoid bug 669767.
1457 nsAutoScriptBlocker scriptBlocker;
1459 nsCOMPtr<nsIPresShell> shell =
1460 mFrame->PresContext()->GetPresShell();
1461 bool observes = shell->ObservesNativeAnonMutationsForPrint();
1462 shell->ObserveNativeAnonMutationsForPrint(true);
1463 // This can cause the frame to be destroyed (and call Revoke()).
1464 mFrame->EnsureEditorInitialized();
1465 shell->ObserveNativeAnonMutationsForPrint(observes);
1467 // The frame can *still* be destroyed even though we have a scriptblocker,
1468 // bug 682684.
1469 if (!mFrame) {
1470 return NS_ERROR_FAILURE;
1473 mFrame->FinishedInitializer();
1474 return NS_OK;