Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsTextControlFrame.h"
10 #include "nsIPlaintextEditor.h"
11 #include "nsCaret.h"
12 #include "nsGenericHTMLElement.h"
13 #include "nsIEditor.h"
14 #include "nsIEditorIMESupport.h"
15 #include "nsIPhonetic.h"
16 #include "nsTextFragment.h"
17 #include "nsIDOMHTMLTextAreaElement.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsINodeInfo.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)
56 {
57 return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
58 }
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()
72 {
73 return a11y::eHTMLTextFieldType;
74 }
75 #endif
77 #ifdef DEBUG
78 class EditorInitializerEntryTracker {
79 public:
80 explicit EditorInitializerEntryTracker(nsTextControlFrame &frame)
81 : mFrame(frame)
82 , mFirstEntry(false)
83 {
84 if (!mFrame.mInEditorInitialization) {
85 mFrame.mInEditorInitialization = true;
86 mFirstEntry = true;
87 }
88 }
89 ~EditorInitializerEntryTracker()
90 {
91 if (mFirstEntry) {
92 mFrame.mInEditorInitialization = false;
93 }
94 }
95 bool EnteredMoreThanOnce() const { return !mFirstEntry; }
96 private:
97 nsTextControlFrame &mFrame;
98 bool mFirstEntry;
99 };
100 #endif
102 nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
103 : nsContainerFrame(aContext)
104 , mEditorHasBeenInitialized(false)
105 , mIsProcessing(false)
106 #ifdef DEBUG
107 , mInEditorInitialization(false)
108 #endif
109 {
110 }
112 nsTextControlFrame::~nsTextControlFrame()
113 {
114 }
116 void
117 nsTextControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
118 {
119 mScrollEvent.Revoke();
121 EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
122 if (initializer) {
123 initializer->Revoke();
124 Properties().Delete(TextControlInitializer());
125 }
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);
136 }
138 nsIAtom*
139 nsTextControlFrame::GetType() const
140 {
141 return nsGkAtoms::textInputFrame;
142 }
144 nsresult
145 nsTextControlFrame::CalcIntrinsicSize(nsRenderingContext* aRenderingContext,
146 nsSize& aIntrinsicSize,
147 float aFontSizeInflation)
148 {
149 // Get leading and the Average/MaxAdvance char width
150 nscoord lineHeight = 0;
151 nscoord charWidth = 0;
152 nscoord charMaxAdvance = 0;
154 nsRefPtr<nsFontMetrics> fontMet;
155 nsresult rv =
156 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet),
157 aFontSizeInflation);
158 NS_ENSURE_SUCCESS(rv, rv);
159 aRenderingContext->SetFont(fontMet);
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.width = 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;
184 }
185 // Now add the extra padding on (so that small input sizes work well)
186 aIntrinsicSize.width += 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.width += 1;
192 }
193 }
195 // Increment width with cols * letter-spacing.
196 {
197 const nsStyleCoord& lsCoord = StyleText()->mLetterSpacing;
198 if (eStyleUnit_Coord == lsCoord.GetUnit()) {
199 nscoord letterSpacing = lsCoord.GetCoordValue();
200 if (letterSpacing != 0) {
201 aIntrinsicSize.width += cols * letterSpacing;
202 }
203 }
204 }
206 // Set the height equal to total number of rows (times the height of each
207 // line, of course)
208 aIntrinsicSize.height = 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 += scrollbarSizes.LeftRight();
223 aIntrinsicSize.height += scrollbarSizes.TopBottom();;
224 }
225 }
227 return NS_OK;
228 }
230 nsresult
231 nsTextControlFrame::EnsureEditorInitialized()
232 {
233 // This method initializes our editor, if needed.
235 // This code used to be called from CreateAnonymousContent(), but
236 // when the editor set the initial string, it would trigger a
237 // PresShell listener which called FlushPendingNotifications()
238 // during frame construction. This was causing other form controls
239 // to display wrong values. Additionally, calling this every time
240 // a text frame control is instantiated means that we're effectively
241 // instantiating the editor for all text fields, even if they
242 // never get used. So, now this method is being called lazily only
243 // when we actually need an editor.
245 if (mEditorHasBeenInitialized)
246 return NS_OK;
248 nsIDocument* doc = mContent->GetCurrentDoc();
249 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
251 nsWeakFrame weakFrame(this);
253 // Flush out content on our document. Have to do this, because script
254 // blockers don't prevent the sink flushing out content and notifying in the
255 // process, which can destroy frames.
256 doc->FlushPendingNotifications(Flush_ContentAndNotify);
257 NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
259 // Make sure that editor init doesn't do things that would kill us off
260 // (especially off the script blockers it'll create for its DOM mutations).
261 {
262 nsAutoScriptBlocker scriptBlocker;
264 // Time to mess with our security context... See comments in GetValue()
265 // for why this is needed.
266 mozilla::dom::AutoNoJSAPI nojsapi;
268 // Make sure that we try to focus the content even if the method fails
269 class EnsureSetFocus {
270 public:
271 explicit EnsureSetFocus(nsTextControlFrame* aFrame)
272 : mFrame(aFrame) {}
273 ~EnsureSetFocus() {
274 if (nsContentUtils::IsFocusedContent(mFrame->GetContent()))
275 mFrame->SetFocus(true, false);
276 }
277 private:
278 nsTextControlFrame *mFrame;
279 };
280 EnsureSetFocus makeSureSetFocusHappens(this);
282 #ifdef DEBUG
283 // Make sure we are not being called again until we're finished.
284 // If reentrancy happens, just pretend that we don't have an editor.
285 const EditorInitializerEntryTracker tracker(*this);
286 NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
287 "EnsureEditorInitialized has been called while a previous call was in progress");
288 #endif
290 // Create an editor for the frame, if one doesn't already exist
291 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
292 NS_ASSERTION(txtCtrl, "Content not a text control element");
293 nsresult rv = txtCtrl->CreateEditor();
294 NS_ENSURE_SUCCESS(rv, rv);
295 NS_ENSURE_STATE(weakFrame.IsAlive());
297 // Set mEditorHasBeenInitialized so that subsequent calls will use the
298 // editor.
299 mEditorHasBeenInitialized = true;
301 // Set the selection to the beginning of the text field.
302 if (weakFrame.IsAlive()) {
303 SetSelectionEndPoints(0, 0);
304 }
305 }
306 NS_ENSURE_STATE(weakFrame.IsAlive());
307 return NS_OK;
308 }
310 nsresult
311 nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
312 {
313 NS_ASSERTION(mContent, "We should have a content!");
315 mState |= NS_FRAME_INDEPENDENT_SELECTION;
317 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
318 NS_ASSERTION(txtCtrl, "Content not a text control element");
320 // Bind the frame to its text control
321 nsresult rv = txtCtrl->BindToFrame(this);
322 NS_ENSURE_SUCCESS(rv, rv);
324 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
325 NS_ENSURE_TRUE(rootNode, NS_ERROR_OUT_OF_MEMORY);
327 if (!aElements.AppendElement(rootNode))
328 return NS_ERROR_OUT_OF_MEMORY;
330 // Do we need a placeholder node?
331 nsAutoString placeholderTxt;
332 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
333 placeholderTxt);
334 nsContentUtils::RemoveNewlines(placeholderTxt);
335 mUsePlaceholder = !placeholderTxt.IsEmpty();
337 // Create the placeholder anonymous content if needed.
338 if (mUsePlaceholder) {
339 nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode();
340 NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY);
342 // Associate ::-moz-placeholder pseudo-element with the placeholder node.
343 nsCSSPseudoElements::Type pseudoType =
344 nsCSSPseudoElements::ePseudo_mozPlaceholder;
346 nsRefPtr<nsStyleContext> placeholderStyleContext =
347 PresContext()->StyleSet()->ResolvePseudoElementStyle(
348 mContent->AsElement(), pseudoType, StyleContext(),
349 placeholderNode->AsElement());
351 if (!aElements.AppendElement(ContentInfo(placeholderNode,
352 placeholderStyleContext))) {
353 return NS_ERROR_OUT_OF_MEMORY;
354 }
355 }
357 rv = UpdateValueDisplay(false);
358 NS_ENSURE_SUCCESS(rv, rv);
360 // textareas are eagerly initialized
361 bool initEagerly = !IsSingleLineTextControl();
362 if (!initEagerly) {
363 // Also, input elements which have a cached selection should get eager
364 // editor initialization.
365 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
366 NS_ASSERTION(txtCtrl, "Content not a text control element");
367 initEagerly = txtCtrl->HasCachedSelection();
368 }
369 if (!initEagerly) {
370 nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(txtCtrl);
371 if (element) {
372 // so are input text controls with spellcheck=true
373 element->GetSpellcheck(&initEagerly);
374 }
375 }
377 if (initEagerly) {
378 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
379 "Someone forgot a script blocker?");
380 EditorInitializer* initializer = (EditorInitializer*) Properties().Get(TextControlInitializer());
381 if (initializer) {
382 initializer->Revoke();
383 }
384 initializer = new EditorInitializer(this);
385 Properties().Set(TextControlInitializer(),initializer);
386 if (!nsContentUtils::AddScriptRunner(initializer)) {
387 initializer->Revoke(); // paranoia
388 Properties().Delete(TextControlInitializer());
389 delete initializer;
390 return NS_ERROR_OUT_OF_MEMORY;
391 }
392 }
394 return NS_OK;
395 }
397 void
398 nsTextControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
399 uint32_t aFilter)
400 {
401 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
402 NS_ASSERTION(txtCtrl, "Content not a text control element");
404 aElements.MaybeAppendElement(txtCtrl->GetRootEditorNode());
405 if (!(aFilter & nsIContent::eSkipPlaceholderContent))
406 aElements.MaybeAppendElement(txtCtrl->GetPlaceholderNode());
408 }
410 nscoord
411 nsTextControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
412 {
413 DebugOnly<nscoord> result = 0;
414 DISPLAY_PREF_WIDTH(this, result);
416 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
417 nsSize autoSize;
418 CalcIntrinsicSize(aRenderingContext, autoSize, inflation);
420 return autoSize.width;
421 }
423 nscoord
424 nsTextControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
425 {
426 // Our min width is just our preferred width if we have auto width.
427 nscoord result;
428 DISPLAY_MIN_WIDTH(this, result);
430 result = GetPrefWidth(aRenderingContext);
432 return result;
433 }
435 nsSize
436 nsTextControlFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
437 nsSize aCBSize, nscoord aAvailableWidth,
438 nsSize aMargin, nsSize aBorder,
439 nsSize aPadding, bool aShrinkWrap)
440 {
441 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
442 nsSize autoSize;
443 nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize, inflation);
444 if (NS_FAILED(rv)) {
445 // What now?
446 autoSize.SizeTo(0, 0);
447 }
448 #ifdef DEBUG
449 // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width
450 else if (StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
451 nsSize ancestorAutoSize =
452 nsContainerFrame::ComputeAutoSize(aRenderingContext,
453 aCBSize, aAvailableWidth,
454 aMargin, aBorder,
455 aPadding, aShrinkWrap);
456 // Disabled when there's inflation; see comment in GetPrefSize.
457 NS_ASSERTION(inflation != 1.0f || ancestorAutoSize.width == autoSize.width,
458 "Incorrect size computed by ComputeAutoSize?");
459 }
460 #endif
462 return autoSize;
463 }
465 nsresult
466 nsTextControlFrame::Reflow(nsPresContext* aPresContext,
467 nsHTMLReflowMetrics& aDesiredSize,
468 const nsHTMLReflowState& aReflowState,
469 nsReflowStatus& aStatus)
470 {
471 DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
472 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
474 // make sure that the form registers itself on the initial/first reflow
475 if (mState & NS_FRAME_FIRST_REFLOW) {
476 nsFormControlFrame::RegUnRegAccessKey(this, true);
477 }
479 // set values of reflow's out parameters
480 aDesiredSize.Width() = aReflowState.ComputedWidth() +
481 aReflowState.ComputedPhysicalBorderPadding().LeftRight();
482 aDesiredSize.Height() = aReflowState.ComputedHeight() +
483 aReflowState.ComputedPhysicalBorderPadding().TopBottom();
485 // computation of the ascent wrt the input height
486 nscoord lineHeight = aReflowState.ComputedHeight();
487 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
488 if (!IsSingleLineTextControl()) {
489 lineHeight = nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(),
490 NS_AUTOHEIGHT, inflation);
491 }
492 nsRefPtr<nsFontMetrics> fontMet;
493 nsresult rv = nsLayoutUtils::GetFontMetricsForFrame(this,
494 getter_AddRefs(fontMet),
495 inflation);
496 NS_ENSURE_SUCCESS(rv, rv);
497 // now adjust for our borders and padding
498 aDesiredSize.SetTopAscent(
499 nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight)
500 + aReflowState.ComputedPhysicalBorderPadding().top);
502 // overflow handling
503 aDesiredSize.SetOverflowAreasToDesiredBounds();
504 // perform reflow on all kids
505 nsIFrame* kid = mFrames.FirstChild();
506 while (kid) {
507 ReflowTextControlChild(kid, aPresContext, aReflowState, aStatus, aDesiredSize);
508 kid = kid->GetNextSibling();
509 }
511 // take into account css properties that affect overflow handling
512 FinishAndStoreOverflow(&aDesiredSize);
514 aStatus = NS_FRAME_COMPLETE;
515 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
516 return NS_OK;
517 }
519 void
520 nsTextControlFrame::ReflowTextControlChild(nsIFrame* aKid,
521 nsPresContext* aPresContext,
522 const nsHTMLReflowState& aReflowState,
523 nsReflowStatus& aStatus,
524 nsHTMLReflowMetrics& aParentDesiredSize)
525 {
526 // compute available size and frame offsets for child
527 nsSize availSize(aReflowState.ComputedWidth() +
528 aReflowState.ComputedPhysicalPadding().LeftRight(),
529 NS_UNCONSTRAINEDSIZE);
531 nsHTMLReflowState kidReflowState(aPresContext, aReflowState,
532 aKid, availSize, -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
533 // Override padding with our computed padding in case we got it from theming or percentage
534 kidReflowState.Init(aPresContext, -1, -1, nullptr, &aReflowState.ComputedPhysicalPadding());
536 // Set computed width and computed height for the child
537 kidReflowState.SetComputedWidth(aReflowState.ComputedWidth());
538 kidReflowState.SetComputedHeight(aReflowState.ComputedHeight());
540 // Offset the frame by the size of the parent's border
541 nscoord xOffset = aReflowState.ComputedPhysicalBorderPadding().left -
542 aReflowState.ComputedPhysicalPadding().left;
543 nscoord yOffset = aReflowState.ComputedPhysicalBorderPadding().top -
544 aReflowState.ComputedPhysicalPadding().top;
546 // reflow the child
547 nsHTMLReflowMetrics desiredSize(aReflowState);
548 ReflowChild(aKid, aPresContext, desiredSize, kidReflowState,
549 xOffset, yOffset, 0, aStatus);
551 // place the child
552 FinishReflowChild(aKid, aPresContext, desiredSize,
553 &kidReflowState, xOffset, yOffset, 0);
555 // consider the overflow
556 aParentDesiredSize.mOverflowAreas.UnionWith(desiredSize.mOverflowAreas);
557 }
559 nsSize
560 nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState)
561 {
562 // XXXbz why? Why not the nsBoxFrame sizes?
563 return nsBox::GetMinSize(aState);
564 }
566 bool
567 nsTextControlFrame::IsCollapsed()
568 {
569 // We're never collapsed in the box sense.
570 return false;
571 }
573 bool
574 nsTextControlFrame::IsLeaf() const
575 {
576 return true;
577 }
579 NS_IMETHODIMP
580 nsTextControlFrame::ScrollOnFocusEvent::Run()
581 {
582 if (mFrame) {
583 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(mFrame->GetContent());
584 NS_ASSERTION(txtCtrl, "Content not a text control element");
585 nsISelectionController* selCon = txtCtrl->GetSelectionController();
586 if (selCon) {
587 mFrame->mScrollEvent.Forget();
588 selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
589 nsISelectionController::SELECTION_FOCUS_REGION,
590 nsISelectionController::SCROLL_SYNCHRONOUS);
591 }
592 }
593 return NS_OK;
594 }
596 //IMPLEMENTING NS_IFORMCONTROLFRAME
597 void nsTextControlFrame::SetFocus(bool aOn, bool aRepaint)
598 {
599 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
600 NS_ASSERTION(txtCtrl, "Content not a text control element");
602 // Revoke the previous scroll event if one exists
603 mScrollEvent.Revoke();
605 // If 'dom.placeholeder.show_on_focus' preference is 'false', focusing or
606 // blurring the frame can have an impact on the placeholder visibility.
607 if (mUsePlaceholder) {
608 txtCtrl->UpdatePlaceholderVisibility(true);
609 }
611 if (!aOn) {
612 return;
613 }
615 nsISelectionController* selCon = txtCtrl->GetSelectionController();
616 if (!selCon)
617 return;
619 nsCOMPtr<nsISelection> ourSel;
620 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
621 getter_AddRefs(ourSel));
622 if (!ourSel) return;
624 nsIPresShell* presShell = PresContext()->GetPresShell();
625 nsRefPtr<nsCaret> caret = presShell->GetCaret();
626 if (!caret) return;
628 // Scroll the current selection into view
629 nsISelection *caretSelection = caret->GetCaretDOMSelection();
630 const bool isFocusedRightNow = ourSel == caretSelection;
631 if (!isFocusedRightNow) {
632 // Don't scroll the current selection if we've been focused using the mouse.
633 uint32_t lastFocusMethod = 0;
634 nsIDocument* doc = GetContent()->GetCurrentDoc();
635 if (doc) {
636 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
637 if (fm) {
638 fm->GetLastFocusMethod(doc->GetWindow(), &lastFocusMethod);
639 }
640 }
641 if (!(lastFocusMethod & nsIFocusManager::FLAG_BYMOUSE)) {
642 nsRefPtr<ScrollOnFocusEvent> event = new ScrollOnFocusEvent(this);
643 nsresult rv = NS_DispatchToCurrentThread(event);
644 if (NS_SUCCEEDED(rv)) {
645 mScrollEvent = event;
646 }
647 }
648 }
650 // tell the caret to use our selection
651 caret->SetCaretDOMSelection(ourSel);
653 // mutual-exclusion: the selection is either controlled by the
654 // document or by the text input/area. Clear any selection in the
655 // document since the focus is now on our independent selection.
657 nsCOMPtr<nsISelectionController> selcon = do_QueryInterface(presShell);
658 nsCOMPtr<nsISelection> docSel;
659 selcon->GetSelection(nsISelectionController::SELECTION_NORMAL,
660 getter_AddRefs(docSel));
661 if (!docSel) return;
663 bool isCollapsed = false;
664 docSel->GetIsCollapsed(&isCollapsed);
665 if (!isCollapsed)
666 docSel->RemoveAllRanges();
667 }
669 nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
670 {
671 if (!mIsProcessing)//some kind of lock.
672 {
673 mIsProcessing = true;
674 if (nsGkAtoms::select == aName)
675 {
676 // Select all the text.
677 //
678 // XXX: This is lame, we can't call editor's SelectAll method
679 // because that triggers AutoCopies in unix builds.
680 // Instead, we have to call our own homegrown version
681 // of select all which merely builds a range that selects
682 // all of the content and adds that to the selection.
684 nsWeakFrame weakThis = this;
685 SelectAllOrCollapseToEndOfText(true); // NOTE: can destroy the world
686 if (!weakThis.IsAlive()) {
687 return NS_OK;
688 }
689 }
690 mIsProcessing = false;
691 }
692 return NS_OK;
693 }
695 NS_IMETHODIMP
696 nsTextControlFrame::GetEditor(nsIEditor **aEditor)
697 {
698 NS_ENSURE_ARG_POINTER(aEditor);
700 nsresult rv = EnsureEditorInitialized();
701 NS_ENSURE_SUCCESS(rv, rv);
703 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
704 NS_ASSERTION(txtCtrl, "Content not a text control element");
705 *aEditor = txtCtrl->GetTextEditor();
706 NS_IF_ADDREF(*aEditor);
707 return NS_OK;
708 }
710 nsresult
711 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
712 int32_t aStartOffset,
713 nsIDOMNode *aEndNode,
714 int32_t aEndOffset,
715 nsITextControlFrame::SelectionDirection aDirection)
716 {
717 // Create a new range to represent the new selection.
718 // Note that we use a new range to avoid having to do
719 // isIncreasing checks to avoid possible errors.
721 nsRefPtr<nsRange> range = new nsRange(mContent);
722 nsresult rv = range->SetStart(aStartNode, aStartOffset);
723 NS_ENSURE_SUCCESS(rv, rv);
725 rv = range->SetEnd(aEndNode, aEndOffset);
726 NS_ENSURE_SUCCESS(rv, rv);
728 // Get the selection, clear it and add the new range to it!
729 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
730 NS_ASSERTION(txtCtrl, "Content not a text control element");
731 nsISelectionController* selCon = txtCtrl->GetSelectionController();
732 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
734 nsCOMPtr<nsISelection> selection;
735 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
736 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
738 nsCOMPtr<nsISelectionPrivate> selPriv = do_QueryInterface(selection, &rv);
739 NS_ENSURE_SUCCESS(rv, rv);
741 nsDirection direction;
742 if (aDirection == eNone) {
743 // Preserve the direction
744 direction = selPriv->GetSelectionDirection();
745 } else {
746 direction = (aDirection == eBackward) ? eDirPrevious : eDirNext;
747 }
749 rv = selection->RemoveAllRanges();
750 NS_ENSURE_SUCCESS(rv, rv);
752 rv = selection->AddRange(range); // NOTE: can destroy the world
753 NS_ENSURE_SUCCESS(rv, rv);
755 selPriv->SetSelectionDirection(direction);
756 return rv;
757 }
759 nsresult
760 nsTextControlFrame::ScrollSelectionIntoView()
761 {
762 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
763 NS_ASSERTION(txtCtrl, "Content not a text control element");
764 nsISelectionController* selCon = txtCtrl->GetSelectionController();
765 if (selCon) {
766 // Scroll the selection into view (see bug 231389).
767 return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
768 nsISelectionController::SELECTION_FOCUS_REGION,
769 nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
770 }
772 return NS_ERROR_FAILURE;
773 }
775 mozilla::dom::Element*
776 nsTextControlFrame::GetRootNodeAndInitializeEditor()
777 {
778 nsCOMPtr<nsIDOMElement> root;
779 GetRootNodeAndInitializeEditor(getter_AddRefs(root));
780 nsCOMPtr<mozilla::dom::Element> rootElem = do_QueryInterface(root);
781 return rootElem;
782 }
784 nsresult
785 nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
786 {
787 NS_ENSURE_ARG_POINTER(aRootElement);
789 nsCOMPtr<nsIEditor> editor;
790 GetEditor(getter_AddRefs(editor));
791 if (!editor)
792 return NS_OK;
794 return editor->GetRootElement(aRootElement);
795 }
797 nsresult
798 nsTextControlFrame::SelectAllOrCollapseToEndOfText(bool aSelect)
799 {
800 nsCOMPtr<nsIDOMElement> rootElement;
801 nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
802 NS_ENSURE_SUCCESS(rv, rv);
804 nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
805 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
807 NS_ENSURE_TRUE(rootNode && rootContent, NS_ERROR_FAILURE);
809 int32_t numChildren = rootContent->GetChildCount();
811 if (numChildren > 0) {
812 // We never want to place the selection after the last
813 // br under the root node!
814 nsIContent *child = rootContent->GetChildAt(numChildren - 1);
815 if (child) {
816 if (child->Tag() == nsGkAtoms::br)
817 --numChildren;
818 }
819 if (!aSelect && numChildren) {
820 child = rootContent->GetChildAt(numChildren - 1);
821 if (child && child->IsNodeOfType(nsINode::eTEXT)) {
822 rootNode = do_QueryInterface(child);
823 const nsTextFragment* fragment = child->GetText();
824 numChildren = fragment ? fragment->GetLength() : 0;
825 }
826 }
827 }
829 rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren,
830 rootNode, numChildren);
831 NS_ENSURE_SUCCESS(rv, rv);
833 return ScrollSelectionIntoView();
834 }
836 nsresult
837 nsTextControlFrame::SetSelectionEndPoints(int32_t aSelStart, int32_t aSelEnd,
838 nsITextControlFrame::SelectionDirection aDirection)
839 {
840 NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
842 if (aSelStart > aSelEnd)
843 return NS_ERROR_FAILURE;
845 nsCOMPtr<nsIDOMNode> startNode, endNode;
846 int32_t startOffset, endOffset;
848 // Calculate the selection start point.
850 nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
852 NS_ENSURE_SUCCESS(rv, rv);
854 if (aSelStart == aSelEnd) {
855 // Collapsed selection, so start and end are the same!
856 endNode = startNode;
857 endOffset = startOffset;
858 }
859 else {
860 // Selection isn't collapsed so we have to calculate
861 // the end point too.
863 rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
865 NS_ENSURE_SUCCESS(rv, rv);
866 }
868 return SetSelectionInternal(startNode, startOffset, endNode, endOffset, aDirection);
869 }
871 NS_IMETHODIMP
872 nsTextControlFrame::SetSelectionRange(int32_t aSelStart, int32_t aSelEnd,
873 nsITextControlFrame::SelectionDirection aDirection)
874 {
875 nsresult rv = EnsureEditorInitialized();
876 NS_ENSURE_SUCCESS(rv, rv);
878 if (aSelStart > aSelEnd) {
879 // Simulate what we'd see SetSelectionStart() was called, followed
880 // by a SetSelectionEnd().
882 aSelStart = aSelEnd;
883 }
885 return SetSelectionEndPoints(aSelStart, aSelEnd, aDirection);
886 }
889 NS_IMETHODIMP
890 nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart)
891 {
892 nsresult rv = EnsureEditorInitialized();
893 NS_ENSURE_SUCCESS(rv, rv);
895 int32_t selStart = 0, selEnd = 0;
897 rv = GetSelectionRange(&selStart, &selEnd);
898 NS_ENSURE_SUCCESS(rv, rv);
900 if (aSelectionStart > selEnd) {
901 // Collapse to the new start point.
902 selEnd = aSelectionStart;
903 }
905 selStart = aSelectionStart;
907 return SetSelectionEndPoints(selStart, selEnd);
908 }
910 NS_IMETHODIMP
911 nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
912 {
913 nsresult rv = EnsureEditorInitialized();
914 NS_ENSURE_SUCCESS(rv, rv);
916 int32_t selStart = 0, selEnd = 0;
918 rv = GetSelectionRange(&selStart, &selEnd);
919 NS_ENSURE_SUCCESS(rv, rv);
921 if (aSelectionEnd < selStart) {
922 // Collapse to the new end point.
923 selStart = aSelectionEnd;
924 }
926 selEnd = aSelectionEnd;
928 return SetSelectionEndPoints(selStart, selEnd);
929 }
931 nsresult
932 nsTextControlFrame::OffsetToDOMPoint(int32_t aOffset,
933 nsIDOMNode** aResult,
934 int32_t* aPosition)
935 {
936 NS_ENSURE_ARG_POINTER(aResult && aPosition);
938 *aResult = nullptr;
939 *aPosition = 0;
941 nsCOMPtr<nsIDOMElement> rootElement;
942 nsresult rv = GetRootNodeAndInitializeEditor(getter_AddRefs(rootElement));
943 NS_ENSURE_SUCCESS(rv, rv);
944 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
946 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
948 nsCOMPtr<nsIDOMNodeList> nodeList;
950 rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
951 NS_ENSURE_SUCCESS(rv, rv);
952 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
954 uint32_t length = 0;
956 rv = nodeList->GetLength(&length);
957 NS_ENSURE_SUCCESS(rv, rv);
959 NS_ASSERTION(length <= 2, "We should have one text node and one mozBR at most");
961 nsCOMPtr<nsIDOMNode> firstNode;
962 rv = nodeList->Item(0, getter_AddRefs(firstNode));
963 NS_ENSURE_SUCCESS(rv, rv);
964 nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(firstNode);
966 if (length == 0 || aOffset < 0) {
967 NS_IF_ADDREF(*aResult = rootNode);
968 *aPosition = 0;
969 } else if (textNode) {
970 uint32_t textLength = 0;
971 textNode->GetLength(&textLength);
972 if (length == 2 && uint32_t(aOffset) == textLength) {
973 // If we're at the end of the text node and we have a trailing BR node,
974 // set the selection on the BR node.
975 NS_IF_ADDREF(*aResult = rootNode);
976 *aPosition = 1;
977 } else {
978 // Otherwise, set the selection on the textnode itself.
979 NS_IF_ADDREF(*aResult = firstNode);
980 *aPosition = std::min(aOffset, int32_t(textLength));
981 }
982 } else {
983 NS_IF_ADDREF(*aResult = rootNode);
984 *aPosition = 0;
985 }
987 return NS_OK;
988 }
990 NS_IMETHODIMP
991 nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart,
992 int32_t* aSelectionEnd,
993 SelectionDirection* aDirection)
994 {
995 // make sure we have an editor
996 nsresult rv = EnsureEditorInitialized();
997 NS_ENSURE_SUCCESS(rv, rv);
999 if (aSelectionStart) {
1000 *aSelectionStart = 0;
1001 }
1002 if (aSelectionEnd) {
1003 *aSelectionEnd = 0;
1004 }
1005 if (aDirection) {
1006 *aDirection = eNone;
1007 }
1009 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1010 NS_ASSERTION(txtCtrl, "Content not a text control element");
1011 nsISelectionController* selCon = txtCtrl->GetSelectionController();
1012 NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
1013 nsCOMPtr<nsISelection> selection;
1014 rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1015 NS_ENSURE_SUCCESS(rv, rv);
1016 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1018 dom::Selection* sel = static_cast<dom::Selection*>(selection.get());
1019 if (aDirection) {
1020 nsDirection direction = sel->GetSelectionDirection();
1021 if (direction == eDirNext) {
1022 *aDirection = eForward;
1023 } else if (direction == eDirPrevious) {
1024 *aDirection = eBackward;
1025 } else {
1026 NS_NOTREACHED("Invalid nsDirection enum value");
1027 }
1028 }
1030 if (!aSelectionStart || !aSelectionEnd) {
1031 return NS_OK;
1032 }
1034 mozilla::dom::Element* root = GetRootNodeAndInitializeEditor();
1035 NS_ENSURE_STATE(root);
1036 nsContentUtils::GetSelectionInTextControl(sel, root,
1037 *aSelectionStart, *aSelectionEnd);
1039 return NS_OK;
1040 }
1042 /////END INTERFACE IMPLEMENTATIONS
1044 ////NSIFRAME
1045 nsresult
1046 nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
1047 nsIAtom* aAttribute,
1048 int32_t aModType)
1049 {
1050 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1051 NS_ASSERTION(txtCtrl, "Content not a text control element");
1052 nsISelectionController* selCon = txtCtrl->GetSelectionController();
1053 const bool needEditor = nsGkAtoms::maxlength == aAttribute ||
1054 nsGkAtoms::readonly == aAttribute ||
1055 nsGkAtoms::disabled == aAttribute ||
1056 nsGkAtoms::spellcheck == aAttribute;
1057 nsCOMPtr<nsIEditor> editor;
1058 if (needEditor) {
1059 GetEditor(getter_AddRefs(editor));
1060 }
1061 if ((needEditor && !editor) || !selCon) {
1062 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1063 }
1065 if (nsGkAtoms::maxlength == aAttribute) {
1066 int32_t maxLength;
1067 bool maxDefined = GetMaxLength(&maxLength);
1068 nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(editor);
1069 if (textEditor) {
1070 if (maxDefined) { // set the maxLength attribute
1071 textEditor->SetMaxTextLength(maxLength);
1072 // if maxLength>docLength, we need to truncate the doc content
1073 } else { // unset the maxLength attribute
1074 textEditor->SetMaxTextLength(-1);
1075 }
1076 }
1077 return NS_OK;
1078 }
1080 if (nsGkAtoms::readonly == aAttribute) {
1081 uint32_t flags;
1082 editor->GetFlags(&flags);
1083 if (AttributeExists(nsGkAtoms::readonly)) { // set readonly
1084 flags |= nsIPlaintextEditor::eEditorReadonlyMask;
1085 if (nsContentUtils::IsFocusedContent(mContent)) {
1086 selCon->SetCaretEnabled(false);
1087 }
1088 } else { // unset readonly
1089 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
1090 if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
1091 nsContentUtils::IsFocusedContent(mContent)) {
1092 selCon->SetCaretEnabled(true);
1093 }
1094 }
1095 editor->SetFlags(flags);
1096 return NS_OK;
1097 }
1099 if (nsGkAtoms::disabled == aAttribute) {
1100 uint32_t flags;
1101 editor->GetFlags(&flags);
1102 int16_t displaySelection = nsISelectionController::SELECTION_OFF;
1103 const bool focused = nsContentUtils::IsFocusedContent(mContent);
1104 const bool hasAttr = AttributeExists(nsGkAtoms::disabled);
1105 if (hasAttr) { // set disabled
1106 flags |= nsIPlaintextEditor::eEditorDisabledMask;
1107 } else { // unset disabled
1108 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
1109 displaySelection = focused ? nsISelectionController::SELECTION_ON
1110 : nsISelectionController::SELECTION_HIDDEN;
1111 }
1112 selCon->SetDisplaySelection(displaySelection);
1113 if (focused) {
1114 selCon->SetCaretEnabled(!hasAttr);
1115 }
1116 editor->SetFlags(flags);
1117 return NS_OK;
1118 }
1120 if (!mEditorHasBeenInitialized && nsGkAtoms::value == aAttribute) {
1121 UpdateValueDisplay(true);
1122 return NS_OK;
1123 }
1125 // Allow the base class to handle common attributes supported by all form
1126 // elements...
1127 return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1128 }
1131 nsresult
1132 nsTextControlFrame::GetText(nsString& aText)
1133 {
1134 nsresult rv = NS_OK;
1135 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1136 NS_ASSERTION(txtCtrl, "Content not a text control element");
1137 if (IsSingleLineTextControl()) {
1138 // There will be no line breaks so we can ignore the wrap property.
1139 txtCtrl->GetTextEditorValue(aText, true);
1140 } else {
1141 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
1142 if (textArea) {
1143 rv = textArea->GetValue(aText);
1144 }
1145 }
1146 return rv;
1147 }
1150 nsresult
1151 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
1152 {
1153 aPhonetic.Truncate(0);
1155 nsCOMPtr<nsIEditor> editor;
1156 nsresult rv = GetEditor(getter_AddRefs(editor));
1157 NS_ENSURE_SUCCESS(rv, rv);
1159 nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(editor);
1160 if (imeSupport) {
1161 nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
1162 if (phonetic)
1163 phonetic->GetPhonetic(aPhonetic);
1164 }
1165 return NS_OK;
1166 }
1168 ///END NSIFRAME OVERLOADS
1169 /////BEGIN PROTECTED METHODS
1171 bool
1172 nsTextControlFrame::GetMaxLength(int32_t* aSize)
1173 {
1174 *aSize = -1;
1176 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
1177 if (content) {
1178 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength);
1179 if (attr && attr->Type() == nsAttrValue::eInteger) {
1180 *aSize = attr->GetIntegerValue();
1182 return true;
1183 }
1184 }
1185 return false;
1186 }
1188 // END IMPLEMENTING NS_IFORMCONTROLFRAME
1190 nsresult
1191 nsTextControlFrame::SetInitialChildList(ChildListID aListID,
1192 nsFrameList& aChildList)
1193 {
1194 nsresult rv = nsContainerFrame::SetInitialChildList(aListID, aChildList);
1196 nsIFrame* first = GetFirstPrincipalChild();
1198 // Mark the scroll frame as being a reflow root. This will allow
1199 // incremental reflows to be initiated at the scroll frame, rather
1200 // than descending from the root frame of the frame hierarchy.
1201 if (first) {
1202 first->AddStateBits(NS_FRAME_REFLOW_ROOT);
1204 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1205 NS_ASSERTION(txtCtrl, "Content not a text control element");
1206 txtCtrl->InitializeKeyboardEventListeners();
1208 nsPoint* contentScrollPos = static_cast<nsPoint*>
1209 (Properties().Get(ContentScrollPos()));
1210 if (contentScrollPos) {
1211 // If we have a scroll pos stored to be passed to our anonymous
1212 // div, do it here!
1213 nsIStatefulFrame* statefulFrame = do_QueryFrame(first);
1214 NS_ASSERTION(statefulFrame, "unexpected type of frame for the anonymous div");
1215 nsPresState fakePresState;
1216 fakePresState.SetScrollState(*contentScrollPos);
1217 statefulFrame->RestoreState(&fakePresState);
1218 Properties().Remove(ContentScrollPos());
1219 delete contentScrollPos;
1220 }
1221 }
1222 return rv;
1223 }
1225 void
1226 nsTextControlFrame::SetValueChanged(bool aValueChanged)
1227 {
1228 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1229 NS_ASSERTION(txtCtrl, "Content not a text control element");
1231 if (mUsePlaceholder) {
1232 nsWeakFrame weakFrame(this);
1233 txtCtrl->UpdatePlaceholderVisibility(true);
1234 if (!weakFrame.IsAlive()) {
1235 return;
1236 }
1237 }
1239 txtCtrl->SetValueChanged(aValueChanged);
1240 }
1243 nsresult
1244 nsTextControlFrame::UpdateValueDisplay(bool aNotify,
1245 bool aBeforeEditorInit,
1246 const nsAString *aValue)
1247 {
1248 if (!IsSingleLineTextControl()) // textareas don't use this
1249 return NS_OK;
1251 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1252 NS_ASSERTION(txtCtrl, "Content not a text control element");
1253 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1255 NS_PRECONDITION(rootNode, "Must have a div content\n");
1256 NS_PRECONDITION(!mEditorHasBeenInitialized,
1257 "Do not call this after editor has been initialized");
1258 NS_ASSERTION(!mUsePlaceholder || txtCtrl->GetPlaceholderNode(),
1259 "A placeholder div must exist");
1261 nsIContent *textContent = rootNode->GetChildAt(0);
1262 if (!textContent) {
1263 // Set up a textnode with our value
1264 nsRefPtr<nsTextNode> textNode =
1265 new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
1267 NS_ASSERTION(textNode, "Must have textcontent!\n");
1269 rootNode->AppendChildTo(textNode, aNotify);
1270 textContent = textNode;
1271 }
1273 NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
1275 // Get the current value of the textfield from the content.
1276 nsAutoString value;
1277 if (aValue) {
1278 value = *aValue;
1279 } else {
1280 txtCtrl->GetTextEditorValue(value, true);
1281 }
1283 // Update the display of the placeholder value if needed.
1284 // We don't need to do this if we're about to initialize the
1285 // editor, since EnsureEditorInitialized takes care of this.
1286 if (mUsePlaceholder && !aBeforeEditorInit)
1287 {
1288 nsWeakFrame weakFrame(this);
1289 txtCtrl->UpdatePlaceholderVisibility(aNotify);
1290 NS_ENSURE_STATE(weakFrame.IsAlive());
1291 }
1293 if (aBeforeEditorInit && value.IsEmpty()) {
1294 rootNode->RemoveChildAt(0, true);
1295 return NS_OK;
1296 }
1298 if (!value.IsEmpty() && IsPasswordTextControl()) {
1299 nsTextEditRules::FillBufWithPWChars(&value, value.Length());
1300 }
1301 return textContent->SetText(value, aNotify);
1302 }
1304 NS_IMETHODIMP
1305 nsTextControlFrame::GetOwnedSelectionController(nsISelectionController** aSelCon)
1306 {
1307 NS_ENSURE_ARG_POINTER(aSelCon);
1309 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1310 NS_ASSERTION(txtCtrl, "Content not a text control element");
1312 *aSelCon = txtCtrl->GetSelectionController();
1313 NS_IF_ADDREF(*aSelCon);
1315 return NS_OK;
1316 }
1318 nsFrameSelection*
1319 nsTextControlFrame::GetOwnedFrameSelection()
1320 {
1321 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1322 NS_ASSERTION(txtCtrl, "Content not a text control element");
1324 return txtCtrl->GetConstFrameSelection();
1325 }
1327 NS_IMETHODIMP
1328 nsTextControlFrame::SaveState(nsPresState** aState)
1329 {
1330 NS_ENSURE_ARG_POINTER(aState);
1332 *aState = nullptr;
1334 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1335 NS_ASSERTION(txtCtrl, "Content not a text control element");
1337 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1338 if (rootNode) {
1339 // Query the nsIStatefulFrame from the HTMLScrollFrame
1340 nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1341 if (scrollStateFrame) {
1342 return scrollStateFrame->SaveState(aState);
1343 }
1344 }
1346 return NS_OK;
1347 }
1349 NS_IMETHODIMP
1350 nsTextControlFrame::RestoreState(nsPresState* aState)
1351 {
1352 NS_ENSURE_ARG_POINTER(aState);
1354 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1355 NS_ASSERTION(txtCtrl, "Content not a text control element");
1357 nsIContent* rootNode = txtCtrl->GetRootEditorNode();
1358 if (rootNode) {
1359 // Query the nsIStatefulFrame from the HTMLScrollFrame
1360 nsIStatefulFrame* scrollStateFrame = do_QueryFrame(rootNode->GetPrimaryFrame());
1361 if (scrollStateFrame) {
1362 return scrollStateFrame->RestoreState(aState);
1363 }
1364 }
1366 // Most likely, we don't have our anonymous content constructed yet, which
1367 // would cause us to end up here. In this case, we'll just store the scroll
1368 // pos ourselves, and forward it to the scroll frame later when it's created.
1369 Properties().Set(ContentScrollPos(), new nsPoint(aState->GetScrollState()));
1370 return NS_OK;
1371 }
1373 nsresult
1374 nsTextControlFrame::PeekOffset(nsPeekOffsetStruct *aPos)
1375 {
1376 return NS_ERROR_FAILURE;
1377 }
1379 void
1380 nsTextControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1381 const nsRect& aDirtyRect,
1382 const nsDisplayListSet& aLists)
1383 {
1384 /*
1385 * The implementation of this method is equivalent as:
1386 * nsContainerFrame::BuildDisplayList()
1387 * with the difference that we filter-out the placeholder frame when it
1388 * should not be visible.
1389 */
1390 DO_GLOBAL_REFLOW_COUNT_DSP("nsTextControlFrame");
1392 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1393 NS_ASSERTION(txtCtrl, "Content not a text control element!");
1395 DisplayBorderBackgroundOutline(aBuilder, aLists);
1397 nsIFrame* kid = mFrames.FirstChild();
1398 // Redirect all lists to the Content list so that nothing can escape, ie
1399 // opacity creating stacking contexts that then get sorted with stacking
1400 // contexts external to us.
1401 nsDisplayList* content = aLists.Content();
1402 nsDisplayListSet set(content, content, content, content, content, content);
1404 while (kid) {
1405 // If the frame is the placeholder frame, we should only show it if the
1406 // placeholder has to be visible.
1407 if (kid->GetContent() != txtCtrl->GetPlaceholderNode() ||
1408 txtCtrl->GetPlaceholderVisibility()) {
1409 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, 0);
1410 }
1411 kid = kid->GetNextSibling();
1412 }
1413 }
1415 mozilla::dom::Element*
1416 nsTextControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
1417 {
1418 if (aType == nsCSSPseudoElements::ePseudo_mozPlaceholder) {
1419 nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
1420 return txtCtrl->GetPlaceholderNode();
1421 }
1423 return nsContainerFrame::GetPseudoElement(aType);
1424 }
1426 NS_IMETHODIMP
1427 nsTextControlFrame::EditorInitializer::Run()
1428 {
1429 if (!mFrame) {
1430 return NS_OK;
1431 }
1433 // Need to block script to avoid bug 669767.
1434 nsAutoScriptBlocker scriptBlocker;
1436 nsCOMPtr<nsIPresShell> shell =
1437 mFrame->PresContext()->GetPresShell();
1438 bool observes = shell->ObservesNativeAnonMutationsForPrint();
1439 shell->ObserveNativeAnonMutationsForPrint(true);
1440 // This can cause the frame to be destroyed (and call Revoke()).
1441 mFrame->EnsureEditorInitialized();
1442 shell->ObserveNativeAnonMutationsForPrint(observes);
1444 // The frame can *still* be destroyed even though we have a scriptblocker,
1445 // bug 682684.
1446 if (!mFrame) {
1447 return NS_ERROR_FAILURE;
1448 }
1450 mFrame->FinishedInitializer();
1451 return NS_OK;
1452 }