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