1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/HTMLTextAreaElement.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/AsyncEventDispatcher.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/dom/HTMLFormSubmission.h"
13 #include "mozilla/dom/HTMLTextAreaElementBinding.h"
14 #include "mozilla/EventDispatcher.h"
15 #include "mozilla/EventStates.h"
16 #include "mozilla/MappedDeclarations.h"
17 #include "mozilla/MouseEvents.h"
18 #include "mozilla/PresState.h"
19 #include "mozilla/TextControlState.h"
20 #include "nsAttrValueInlines.h"
21 #include "nsBaseCommandController.h"
22 #include "nsContentCID.h"
23 #include "nsContentCreatorFunctions.h"
25 #include "nsFocusManager.h"
26 #include "nsIConstraintValidation.h"
27 #include "nsIControllers.h"
28 #include "mozilla/dom/Document.h"
29 #include "nsIFormControlFrame.h"
30 #include "nsIFormControl.h"
33 #include "nsITextControlFrame.h"
34 #include "nsLayoutUtils.h"
35 #include "nsLinebreakConverter.h"
36 #include "nsMappedAttributes.h"
37 #include "nsPIDOMWindow.h"
38 #include "nsPresContext.h"
39 #include "nsReadableUtils.h"
40 #include "nsStyleConsts.h"
41 #include "nsBaseCommandController.h"
42 #include "nsXULControllers.h"
44 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea
)
49 HTMLTextAreaElement::HTMLTextAreaElement(
50 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
,
51 FromParser aFromParser
)
52 : TextControlElement(std::move(aNodeInfo
), aFromParser
, NS_FORM_TEXTAREA
),
54 mLastValueChangeWasInteractive(false),
55 mHandlingSelect(false),
56 mDoneAddingChildren(!aFromParser
),
57 mInhibitStateRestoration(!!(aFromParser
& FROM_PARSER_FRAGMENT
)),
58 mDisabledChanged(false),
59 mCanShowInvalidUI(true),
60 mCanShowValidUI(true),
61 mIsPreviewEnabled(false),
62 mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown
),
63 mState(TextControlState::Construct(this)) {
64 AddMutationObserver(this);
66 // Set up our default state. By default we're enabled (since we're
67 // a control type that can be disabled but not actually disabled
68 // right now), optional, and valid. We are NOT readwrite by default
69 // until someone calls UpdateEditableState on us, apparently! Also
70 // by default we don't have to show validity UI and so forth.
71 AddStatesSilently(NS_EVENT_STATE_ENABLED
| NS_EVENT_STATE_OPTIONAL
|
72 NS_EVENT_STATE_VALID
);
75 HTMLTextAreaElement::~HTMLTextAreaElement() {
80 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTextAreaElement
)
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTextAreaElement
,
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity
)
85 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers
)
87 tmp
->mState
->Traverse(cb
);
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
91 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTextAreaElement
,
93 NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity
)
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers
)
96 tmp
->mState
->Unlink();
98 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
100 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement
,
103 nsIConstraintValidation
)
105 // nsIDOMHTMLTextAreaElement
107 nsresult
HTMLTextAreaElement::Clone(dom::NodeInfo
* aNodeInfo
,
108 nsINode
** aResult
) const {
110 RefPtr
<HTMLTextAreaElement
> it
= new (aNodeInfo
->NodeInfoManager())
111 HTMLTextAreaElement(do_AddRef(aNodeInfo
));
113 nsresult rv
= const_cast<HTMLTextAreaElement
*>(this)->CopyInnerTo(it
);
114 NS_ENSURE_SUCCESS(rv
, rv
);
117 // Set our value on the clone.
119 GetValueInternal(value
, true);
121 // SetValueInternal handles setting mValueChanged for us
122 rv
= it
->SetValueInternal(value
, TextControlState::eSetValue_Notify
);
123 NS_ENSURE_SUCCESS(rv
, rv
);
126 it
->mLastValueChangeWasInteractive
= mLastValueChangeWasInteractive
;
133 void HTMLTextAreaElement::Select() {
134 // XXX Bug? We have to give the input focus before contents can be
137 FocusTristate state
= FocusState();
138 if (state
== eUnfocusable
) {
142 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
144 RefPtr
<nsPresContext
> presContext
= GetPresContext(eForComposedDoc
);
145 if (state
== eInactiveWindow
) {
146 if (fm
) fm
->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL
);
147 SelectAll(presContext
);
151 nsEventStatus status
= nsEventStatus_eIgnore
;
152 WidgetGUIEvent
event(true, eFormSelect
, nullptr);
153 // XXXbz HTMLInputElement guards against this reentering; shouldn't we?
154 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), presContext
, &event
,
157 // If the DOM event was not canceled (e.g. by a JS event handler
159 if (status
== nsEventStatus_eIgnore
) {
161 fm
->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL
);
163 // ensure that the element is actually focused
164 if (this == fm
->GetFocusedElement()) {
165 // Now Select all the text!
166 SelectAll(presContext
);
173 HTMLTextAreaElement::SelectAll(nsPresContext
* aPresContext
) {
174 nsIFormControlFrame
* formControlFrame
= GetFormControlFrame(true);
176 if (formControlFrame
) {
177 formControlFrame
->SetFormProperty(nsGkAtoms::select
, EmptyString());
183 bool HTMLTextAreaElement::IsHTMLFocusable(bool aWithMouse
, bool* aIsFocusable
,
184 int32_t* aTabIndex
) {
185 if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(
186 aWithMouse
, aIsFocusable
, aTabIndex
)) {
190 // disabled textareas are not focusable
191 *aIsFocusable
= !IsDisabled();
195 int32_t HTMLTextAreaElement::TabIndexDefault() { return 0; }
197 void HTMLTextAreaElement::GetType(nsAString
& aType
) {
198 aType
.AssignLiteral("textarea");
201 void HTMLTextAreaElement::GetValue(nsAString
& aValue
) {
202 GetValueInternal(aValue
, true);
203 MOZ_ASSERT(aValue
.FindChar(static_cast<char16_t
>('\r')) == -1);
206 void HTMLTextAreaElement::GetValueInternal(nsAString
& aValue
,
207 bool aIgnoreWrap
) const {
209 mState
->GetValue(aValue
, aIgnoreWrap
);
212 bool HTMLTextAreaElement::ValueEquals(const nsAString
& aValue
) const {
214 return mState
->ValueEquals(aValue
);
217 nsIEditor
* HTMLTextAreaElement::GetEditorForBindings() {
218 if (!GetPrimaryFrame()) {
219 GetPrimaryFrame(FlushType::Frames
);
221 return GetTextEditor();
224 TextEditor
* HTMLTextAreaElement::GetTextEditor() {
226 return mState
->GetTextEditor();
229 TextEditor
* HTMLTextAreaElement::GetTextEditorWithoutCreation() {
231 return mState
->GetTextEditorWithoutCreation();
234 nsISelectionController
* HTMLTextAreaElement::GetSelectionController() {
236 return mState
->GetSelectionController();
239 nsFrameSelection
* HTMLTextAreaElement::GetConstFrameSelection() {
241 return mState
->GetConstFrameSelection();
244 nsresult
HTMLTextAreaElement::BindToFrame(nsTextControlFrame
* aFrame
) {
245 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
247 return mState
->BindToFrame(aFrame
);
250 void HTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame
* aFrame
) {
253 mState
->UnbindFromFrame(aFrame
);
257 nsresult
HTMLTextAreaElement::CreateEditor() {
259 return mState
->PrepareEditor();
262 void HTMLTextAreaElement::UpdateOverlayTextVisibility(bool aNotify
) {
264 mState
->UpdateOverlayTextVisibility(aNotify
);
267 bool HTMLTextAreaElement::GetPlaceholderVisibility() {
269 return mState
->GetPlaceholderVisibility();
272 void HTMLTextAreaElement::SetPreviewValue(const nsAString
& aValue
) {
274 mState
->SetPreviewText(aValue
, true);
277 void HTMLTextAreaElement::GetPreviewValue(nsAString
& aValue
) {
279 mState
->GetPreviewText(aValue
);
282 void HTMLTextAreaElement::EnablePreview() {
283 if (mIsPreviewEnabled
) {
287 mIsPreviewEnabled
= true;
288 // Reconstruct the frame to append an anonymous preview node
289 nsLayoutUtils::PostRestyleEvent(this, RestyleHint
{0},
290 nsChangeHint_ReconstructFrame
);
293 bool HTMLTextAreaElement::IsPreviewEnabled() { return mIsPreviewEnabled
; }
295 bool HTMLTextAreaElement::GetPreviewVisibility() {
297 return mState
->GetPreviewVisibility();
300 nsresult
HTMLTextAreaElement::SetValueInternal(const nsAString
& aValue
,
304 // Need to set the value changed flag here if our value has in fact changed
305 // (i.e. if eSetValue_Notify is in aFlags), so that
306 // nsTextControlFrame::UpdateValueDisplay retrieves the correct value if
308 if (aFlags
& TextControlState::eSetValue_Notify
) {
309 SetValueChanged(true);
312 if (!mState
->SetValue(aValue
, aFlags
)) {
313 return NS_ERROR_OUT_OF_MEMORY
;
319 void HTMLTextAreaElement::SetValue(const nsAString
& aValue
,
320 ErrorResult
& aError
) {
321 // If the value has been set by a script, we basically want to keep the
322 // current change event state. If the element is ready to fire a change
323 // event, we should keep it that way. Otherwise, we should make sure the
324 // element will not fire any event because of the script interaction.
326 // NOTE: this is currently quite expensive work (too much string
327 // manipulation). We should probably optimize that.
328 nsAutoString currentValue
;
329 GetValueInternal(currentValue
, true);
331 nsresult rv
= SetValueInternal(
332 aValue
, TextControlState::eSetValue_ByContent
|
333 TextControlState::eSetValue_Notify
|
334 TextControlState::eSetValue_MoveCursorToEndIfValueChanged
);
335 if (NS_WARN_IF(NS_FAILED(rv
))) {
340 if (mFocusedValue
.Equals(currentValue
)) {
341 GetValueInternal(mFocusedValue
, true);
345 void HTMLTextAreaElement::SetUserInput(const nsAString
& aValue
,
346 nsIPrincipal
& aSubjectPrincipal
) {
348 aValue
, TextControlState::eSetValue_BySetUserInput
|
349 TextControlState::eSetValue_Notify
|
350 TextControlState::eSetValue_MoveCursorToEndIfValueChanged
);
353 nsresult
HTMLTextAreaElement::SetValueChanged(bool aValueChanged
) {
356 bool previousValue
= mValueChanged
;
358 mValueChanged
= aValueChanged
;
359 if (!aValueChanged
&& !mState
->IsEmpty()) {
360 mState
->EmptyValue();
363 if (mValueChanged
!= previousValue
) {
370 void HTMLTextAreaElement::GetDefaultValue(nsAString
& aDefaultValue
,
371 ErrorResult
& aError
) {
372 if (!nsContentUtils::GetNodeTextContent(this, false, aDefaultValue
,
374 aError
.Throw(NS_ERROR_OUT_OF_MEMORY
);
378 void HTMLTextAreaElement::SetDefaultValue(const nsAString
& aDefaultValue
,
379 ErrorResult
& aError
) {
380 nsresult rv
= nsContentUtils::SetNodeTextContent(this, aDefaultValue
, true);
381 if (NS_SUCCEEDED(rv
) && !mValueChanged
) {
389 bool HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID
,
391 const nsAString
& aValue
,
392 nsIPrincipal
* aMaybeScriptedPrincipal
,
393 nsAttrValue
& aResult
) {
394 if (aNamespaceID
== kNameSpaceID_None
) {
395 if (aAttribute
== nsGkAtoms::maxlength
||
396 aAttribute
== nsGkAtoms::minlength
) {
397 return aResult
.ParseNonNegativeIntValue(aValue
);
398 } else if (aAttribute
== nsGkAtoms::cols
) {
399 aResult
.ParseIntWithFallback(aValue
, DEFAULT_COLS
);
401 } else if (aAttribute
== nsGkAtoms::rows
) {
402 aResult
.ParseIntWithFallback(aValue
, DEFAULT_ROWS_TEXTAREA
);
404 } else if (aAttribute
== nsGkAtoms::autocomplete
) {
405 aResult
.ParseAtomArray(aValue
);
409 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
410 aMaybeScriptedPrincipal
, aResult
);
413 void HTMLTextAreaElement::MapAttributesIntoRule(
414 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
416 if (!aDecls
.PropertyIsSet(eCSSProperty_white_space
)) {
417 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::wrap
);
418 if (value
&& value
->Type() == nsAttrValue::eString
&&
419 value
->Equals(nsGkAtoms::OFF
, eIgnoreCase
)) {
420 aDecls
.SetKeywordValue(eCSSProperty_white_space
, StyleWhiteSpace::Pre
);
424 nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes
,
426 nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes
,
430 nsChangeHint
HTMLTextAreaElement::GetAttributeChangeHint(
431 const nsAtom
* aAttribute
, int32_t aModType
) const {
432 nsChangeHint retval
=
433 nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute
,
435 if (aAttribute
== nsGkAtoms::rows
|| aAttribute
== nsGkAtoms::cols
) {
436 retval
|= NS_STYLE_HINT_REFLOW
;
437 } else if (aAttribute
== nsGkAtoms::wrap
) {
438 retval
|= nsChangeHint_ReconstructFrame
;
439 } else if (aAttribute
== nsGkAtoms::placeholder
) {
440 retval
|= nsChangeHint_ReconstructFrame
;
446 HTMLTextAreaElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
447 static const MappedAttributeEntry attributes
[] = {{nsGkAtoms::wrap
},
450 static const MappedAttributeEntry
* const map
[] = {
452 sDivAlignAttributeMap
,
456 return FindAttributeDependence(aAttribute
, map
);
459 nsMapRuleToAttributesFunc
HTMLTextAreaElement::GetAttributeMappingFunction()
461 return &MapAttributesIntoRule
;
464 bool HTMLTextAreaElement::IsDisabledForEvents(WidgetEvent
* aEvent
) {
465 nsIFormControlFrame
* formControlFrame
= GetFormControlFrame(false);
466 nsIFrame
* formFrame
= do_QueryFrame(formControlFrame
);
467 return IsElementDisabledForEvents(aEvent
, formFrame
);
470 void HTMLTextAreaElement::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
471 aVisitor
.mCanHandle
= false;
472 if (IsDisabledForEvents(aVisitor
.mEvent
)) {
476 // Don't dispatch a second select event if we are already handling
478 if (aVisitor
.mEvent
->mMessage
== eFormSelect
) {
479 if (mHandlingSelect
) {
482 mHandlingSelect
= true;
485 if (aVisitor
.mEvent
->mMessage
== eBlur
) {
486 // Set mWantsPreHandleEvent and fire change event in PreHandleEvent to
487 // prevent it breaks event target chain creation.
488 aVisitor
.mWantsPreHandleEvent
= true;
491 nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor
);
494 nsresult
HTMLTextAreaElement::PreHandleEvent(EventChainVisitor
& aVisitor
) {
495 if (aVisitor
.mEvent
->mMessage
== eBlur
) {
496 // Fire onchange (if necessary), before we do the blur, bug 370521.
497 FireChangeEventIfNeeded();
499 return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor
);
502 void HTMLTextAreaElement::FireChangeEventIfNeeded() {
504 GetValueInternal(value
, true);
506 if (mFocusedValue
.Equals(value
)) {
510 // Dispatch the change event.
511 mFocusedValue
= value
;
512 nsContentUtils::DispatchTrustedEvent(
513 OwnerDoc(), static_cast<nsIContent
*>(this), u
"change"_ns
, CanBubble::eYes
,
517 nsresult
HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor
& aVisitor
) {
518 if (aVisitor
.mEvent
->mMessage
== eFormSelect
) {
519 mHandlingSelect
= false;
522 if (aVisitor
.mEvent
->mMessage
== eFocus
||
523 aVisitor
.mEvent
->mMessage
== eBlur
) {
524 if (aVisitor
.mEvent
->mMessage
== eFocus
) {
525 // If the invalid UI is shown, we should show it while focusing (and
526 // update). Otherwise, we should not.
527 GetValueInternal(mFocusedValue
, true);
528 mCanShowInvalidUI
= !IsValid() && ShouldShowValidityUI();
530 // If neither invalid UI nor valid UI is shown, we shouldn't show the
531 // valid UI while typing.
532 mCanShowValidUI
= ShouldShowValidityUI();
534 mCanShowInvalidUI
= true;
535 mCanShowValidUI
= true;
544 void HTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified
) {
545 if (!mValueChanged
) {
546 if (!mDoneAddingChildren
) {
547 // Reset now that we're done adding children if the content sink tried to
548 // sneak some text in without calling AppendChildTo.
552 if (!mInhibitStateRestoration
) {
554 RestoreFormControlState();
558 mDoneAddingChildren
= true;
561 bool HTMLTextAreaElement::IsDoneAddingChildren() { return mDoneAddingChildren
; }
563 // Controllers Methods
565 nsIControllers
* HTMLTextAreaElement::GetControllers(ErrorResult
& aError
) {
567 mControllers
= new nsXULControllers();
569 aError
.Throw(NS_ERROR_FAILURE
);
573 RefPtr
<nsBaseCommandController
> commandController
=
574 nsBaseCommandController::CreateEditorController();
575 if (!commandController
) {
576 aError
.Throw(NS_ERROR_FAILURE
);
580 mControllers
->AppendController(commandController
);
582 commandController
= nsBaseCommandController::CreateEditingController();
583 if (!commandController
) {
584 aError
.Throw(NS_ERROR_FAILURE
);
588 mControllers
->AppendController(commandController
);
594 nsresult
HTMLTextAreaElement::GetControllers(nsIControllers
** aResult
) {
595 NS_ENSURE_ARG_POINTER(aResult
);
598 *aResult
= GetControllers(error
);
599 NS_IF_ADDREF(*aResult
);
601 return error
.StealNSResult();
604 uint32_t HTMLTextAreaElement::GetTextLength() {
610 Nullable
<uint32_t> HTMLTextAreaElement::GetSelectionStart(ErrorResult
& aError
) {
611 uint32_t selStart
, selEnd
;
612 GetSelectionRange(&selStart
, &selEnd
, aError
);
613 return Nullable
<uint32_t>(selStart
);
616 void HTMLTextAreaElement::SetSelectionStart(
617 const Nullable
<uint32_t>& aSelectionStart
, ErrorResult
& aError
) {
619 mState
->SetSelectionStart(aSelectionStart
, aError
);
622 Nullable
<uint32_t> HTMLTextAreaElement::GetSelectionEnd(ErrorResult
& aError
) {
623 uint32_t selStart
, selEnd
;
624 GetSelectionRange(&selStart
, &selEnd
, aError
);
625 return Nullable
<uint32_t>(selEnd
);
628 void HTMLTextAreaElement::SetSelectionEnd(
629 const Nullable
<uint32_t>& aSelectionEnd
, ErrorResult
& aError
) {
631 mState
->SetSelectionEnd(aSelectionEnd
, aError
);
634 void HTMLTextAreaElement::GetSelectionRange(uint32_t* aSelectionStart
,
635 uint32_t* aSelectionEnd
,
638 return mState
->GetSelectionRange(aSelectionStart
, aSelectionEnd
, aRv
);
641 void HTMLTextAreaElement::GetSelectionDirection(nsAString
& aDirection
,
642 ErrorResult
& aError
) {
644 mState
->GetSelectionDirectionString(aDirection
, aError
);
647 void HTMLTextAreaElement::SetSelectionDirection(const nsAString
& aDirection
,
648 ErrorResult
& aError
) {
650 mState
->SetSelectionDirection(aDirection
, aError
);
653 void HTMLTextAreaElement::SetSelectionRange(
654 uint32_t aSelectionStart
, uint32_t aSelectionEnd
,
655 const Optional
<nsAString
>& aDirection
, ErrorResult
& aError
) {
657 mState
->SetSelectionRange(aSelectionStart
, aSelectionEnd
, aDirection
, aError
);
660 void HTMLTextAreaElement::SetRangeText(const nsAString
& aReplacement
,
663 mState
->SetRangeText(aReplacement
, aRv
);
666 void HTMLTextAreaElement::SetRangeText(const nsAString
& aReplacement
,
667 uint32_t aStart
, uint32_t aEnd
,
668 SelectionMode aSelectMode
,
671 mState
->SetRangeText(aReplacement
, aStart
, aEnd
, aSelectMode
, aRv
);
674 void HTMLTextAreaElement::GetValueFromSetRangeText(nsAString
& aValue
) {
675 GetValueInternal(aValue
, false);
678 nsresult
HTMLTextAreaElement::SetValueFromSetRangeText(
679 const nsAString
& aValue
) {
680 return SetValueInternal(aValue
, TextControlState::eSetValue_ByContent
|
681 TextControlState::eSetValue_Notify
);
684 nsresult
HTMLTextAreaElement::Reset() {
685 nsAutoString resetVal
;
686 GetDefaultValue(resetVal
, IgnoreErrors());
687 SetValueChanged(false);
690 SetValueInternal(resetVal
, TextControlState::eSetValue_Internal
);
691 NS_ENSURE_SUCCESS(rv
, rv
);
697 HTMLTextAreaElement::SubmitNamesValues(HTMLFormSubmission
* aFormSubmission
) {
698 // Disabled elements don't submit
704 // Get the name (if no name, no submit)
707 GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, name
);
708 if (name
.IsEmpty()) {
716 GetValueInternal(value
, false);
721 return aFormSubmission
->AddNameValuePair(name
, value
);
725 HTMLTextAreaElement::SaveState() {
728 // Only save if value != defaultValue (bug 62713)
729 PresState
* state
= nullptr;
731 state
= GetPrimaryPresState();
734 GetValueInternal(value
, true);
736 rv
= nsLinebreakConverter::ConvertStringLineBreaks(
737 value
, nsLinebreakConverter::eLinebreakPlatform
,
738 nsLinebreakConverter::eLinebreakContent
);
741 NS_ERROR("Converting linebreaks failed!");
745 state
->contentData() =
746 TextContentData(value
, mLastValueChangeWasInteractive
);
750 if (mDisabledChanged
) {
752 state
= GetPrimaryPresState();
756 // We do not want to save the real disabled state but the disabled
758 state
->disabled() = HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
);
759 state
->disabledSet() = true;
765 bool HTMLTextAreaElement::RestoreState(PresState
* aState
) {
766 const PresContentData
& state
= aState
->contentData();
768 if (state
.type() == PresContentData::TTextContentData
) {
770 SetValue(state
.get_TextContentData().value(), rv
);
771 ENSURE_SUCCESS(rv
, false);
772 if (state
.get_TextContentData().lastValueChangeWasInteractive()) {
773 mLastValueChangeWasInteractive
= true;
777 if (aState
->disabledSet() && !aState
->disabled()) {
778 SetDisabled(false, IgnoreErrors());
784 EventStates
HTMLTextAreaElement::IntrinsicState() const {
785 EventStates state
= nsGenericHTMLFormElementWithState::IntrinsicState();
787 if (IsCandidateForConstraintValidation()) {
789 state
|= NS_EVENT_STATE_VALID
;
791 state
|= NS_EVENT_STATE_INVALID
;
792 // :-moz-ui-invalid always apply if the element suffers from a custom
793 // error and never applies if novalidate is set on the form owner.
795 !mForm
->HasAttr(kNameSpaceID_None
, nsGkAtoms::novalidate
)) &&
796 (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR
) ||
797 (mCanShowInvalidUI
&& ShouldShowValidityUI()))) {
798 state
|= NS_EVENT_STATE_MOZ_UI_INVALID
;
802 // :-moz-ui-valid applies if all the following are true:
803 // 1. The element is not focused, or had either :-moz-ui-valid or
804 // :-moz-ui-invalid applying before it was focused ;
805 // 2. The element is either valid or isn't allowed to have
806 // :-moz-ui-invalid applying ;
807 // 3. The element has no form owner or its form owner doesn't have the
808 // novalidate attribute set ;
809 // 4. The element has already been modified or the user tried to submit the
810 // form owner while invalid.
811 if ((!mForm
|| !mForm
->HasAttr(kNameSpaceID_None
, nsGkAtoms::novalidate
)) &&
812 (mCanShowValidUI
&& ShouldShowValidityUI() &&
813 (IsValid() || (state
.HasState(NS_EVENT_STATE_MOZ_UI_INVALID
) &&
814 !mCanShowInvalidUI
)))) {
815 state
|= NS_EVENT_STATE_MOZ_UI_VALID
;
819 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::placeholder
) && IsValueEmpty()) {
820 state
|= NS_EVENT_STATE_PLACEHOLDERSHOWN
;
826 nsresult
HTMLTextAreaElement::BindToTree(BindContext
& aContext
,
829 nsGenericHTMLFormElementWithState::BindToTree(aContext
, aParent
);
830 NS_ENSURE_SUCCESS(rv
, rv
);
832 // If there is a disabled fieldset in the parent chain, the element is now
833 // barred from constraint validation and can't suffer from value missing.
834 UpdateValueMissingValidityState();
835 UpdateBarredFromConstraintValidation();
837 // And now make sure our state is up to date
843 void HTMLTextAreaElement::UnbindFromTree(bool aNullParent
) {
844 nsGenericHTMLFormElementWithState::UnbindFromTree(aNullParent
);
846 // We might be no longer disabled because of parent chain changed.
847 UpdateValueMissingValidityState();
848 UpdateBarredFromConstraintValidation();
850 // And now make sure our state is up to date
854 nsresult
HTMLTextAreaElement::BeforeSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
855 const nsAttrValueOrString
* aValue
,
857 if (aNotify
&& aName
== nsGkAtoms::disabled
&&
858 aNameSpaceID
== kNameSpaceID_None
) {
859 mDisabledChanged
= true;
862 return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID
, aName
,
866 void HTMLTextAreaElement::CharacterDataChanged(nsIContent
* aContent
,
867 const CharacterDataChangeInfo
&) {
868 ContentChanged(aContent
);
871 void HTMLTextAreaElement::ContentAppended(nsIContent
* aFirstNewContent
) {
872 ContentChanged(aFirstNewContent
);
875 void HTMLTextAreaElement::ContentInserted(nsIContent
* aChild
) {
876 ContentChanged(aChild
);
879 void HTMLTextAreaElement::ContentRemoved(nsIContent
* aChild
,
880 nsIContent
* aPreviousSibling
) {
881 ContentChanged(aChild
);
884 void HTMLTextAreaElement::ContentChanged(nsIContent
* aContent
) {
885 if (!mValueChanged
&& mDoneAddingChildren
&&
886 nsContentUtils::IsInSameAnonymousTree(this, aContent
)) {
887 // Hard to say what the reset can trigger, so be safe pending
889 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
894 nsresult
HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
895 const nsAttrValue
* aValue
,
896 const nsAttrValue
* aOldValue
,
897 nsIPrincipal
* aSubjectPrincipal
,
899 if (aNameSpaceID
== kNameSpaceID_None
) {
900 if (aName
== nsGkAtoms::required
|| aName
== nsGkAtoms::disabled
||
901 aName
== nsGkAtoms::readonly
) {
902 if (aName
== nsGkAtoms::disabled
) {
903 // This *has* to be called *before* validity state check because
904 // UpdateBarredFromConstraintValidation and
905 // UpdateValueMissingValidityState depend on our disabled state.
906 UpdateDisabledState(aNotify
);
909 if (aName
== nsGkAtoms::required
) {
910 // This *has* to be called *before* UpdateValueMissingValidityState
911 // because UpdateValueMissingValidityState depends on our required
913 UpdateRequiredState(!!aValue
, aNotify
);
916 UpdateValueMissingValidityState();
918 // This *has* to be called *after* validity has changed.
919 if (aName
== nsGkAtoms::readonly
|| aName
== nsGkAtoms::disabled
) {
920 UpdateBarredFromConstraintValidation();
922 } else if (aName
== nsGkAtoms::autocomplete
) {
923 // Clear the cached @autocomplete attribute state.
924 mAutocompleteAttrState
= nsContentUtils::eAutocompleteAttrState_Unknown
;
925 } else if (aName
== nsGkAtoms::maxlength
) {
926 UpdateTooLongValidityState();
927 } else if (aName
== nsGkAtoms::minlength
) {
928 UpdateTooShortValidityState();
932 return nsGenericHTMLFormElementWithState::AfterSetAttr(
933 aNameSpaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
936 nsresult
HTMLTextAreaElement::CopyInnerTo(Element
* aDest
) {
937 nsresult rv
= nsGenericHTMLFormElementWithState::CopyInnerTo(aDest
);
938 NS_ENSURE_SUCCESS(rv
, rv
);
940 if (aDest
->OwnerDoc()->IsStaticDocument()) {
942 GetValueInternal(value
, true);
944 static_cast<HTMLTextAreaElement
*>(aDest
)->SetValue(value
, ret
);
945 return ret
.StealNSResult();
950 bool HTMLTextAreaElement::IsMutable() const {
951 return (!HasAttr(kNameSpaceID_None
, nsGkAtoms::readonly
) && !IsDisabled());
954 bool HTMLTextAreaElement::IsValueEmpty() const {
956 GetValueInternal(value
, true);
958 return value
.IsEmpty();
961 void HTMLTextAreaElement::SetCustomValidity(const nsAString
& aError
) {
962 nsIConstraintValidation::SetCustomValidity(aError
);
967 bool HTMLTextAreaElement::IsTooLong() {
968 if (!mValueChanged
|| !mLastValueChangeWasInteractive
||
969 !HasAttr(kNameSpaceID_None
, nsGkAtoms::maxlength
)) {
973 int32_t maxLength
= MaxLength();
975 // Maxlength of -1 means parsing error.
976 if (maxLength
== -1) {
980 int32_t textLength
= GetTextLength();
982 return textLength
> maxLength
;
985 bool HTMLTextAreaElement::IsTooShort() {
986 if (!mValueChanged
|| !mLastValueChangeWasInteractive
||
987 !HasAttr(kNameSpaceID_None
, nsGkAtoms::minlength
)) {
991 int32_t minLength
= MinLength();
993 // Minlength of -1 means parsing error.
994 if (minLength
== -1) {
998 int32_t textLength
= GetTextLength();
1000 return textLength
&& textLength
< minLength
;
1003 bool HTMLTextAreaElement::IsValueMissing() const {
1004 if (!Required() || !IsMutable()) {
1008 return IsValueEmpty();
1011 void HTMLTextAreaElement::UpdateTooLongValidityState() {
1012 SetValidityState(VALIDITY_STATE_TOO_LONG
, IsTooLong());
1015 void HTMLTextAreaElement::UpdateTooShortValidityState() {
1016 SetValidityState(VALIDITY_STATE_TOO_SHORT
, IsTooShort());
1019 void HTMLTextAreaElement::UpdateValueMissingValidityState() {
1020 SetValidityState(VALIDITY_STATE_VALUE_MISSING
, IsValueMissing());
1023 void HTMLTextAreaElement::UpdateBarredFromConstraintValidation() {
1024 SetBarredFromConstraintValidation(
1025 HasAttr(kNameSpaceID_None
, nsGkAtoms::readonly
) || IsDisabled());
1028 nsresult
HTMLTextAreaElement::GetValidationMessage(
1029 nsAString
& aValidationMessage
, ValidityStateType aType
) {
1030 nsresult rv
= NS_OK
;
1033 case VALIDITY_STATE_TOO_LONG
: {
1034 nsAutoString message
;
1035 int32_t maxLength
= MaxLength();
1036 int32_t textLength
= GetTextLength();
1037 nsAutoString strMaxLength
;
1038 nsAutoString strTextLength
;
1040 strMaxLength
.AppendInt(maxLength
);
1041 strTextLength
.AppendInt(textLength
);
1043 rv
= nsContentUtils::FormatMaybeLocalizedString(
1044 message
, nsContentUtils::eDOM_PROPERTIES
, "FormValidationTextTooLong",
1045 OwnerDoc(), strMaxLength
, strTextLength
);
1046 aValidationMessage
= message
;
1048 case VALIDITY_STATE_TOO_SHORT
: {
1049 nsAutoString message
;
1050 int32_t minLength
= MinLength();
1051 int32_t textLength
= GetTextLength();
1052 nsAutoString strMinLength
;
1053 nsAutoString strTextLength
;
1055 strMinLength
.AppendInt(minLength
);
1056 strTextLength
.AppendInt(textLength
);
1058 rv
= nsContentUtils::FormatMaybeLocalizedString(
1059 message
, nsContentUtils::eDOM_PROPERTIES
,
1060 "FormValidationTextTooShort", OwnerDoc(), strMinLength
,
1062 aValidationMessage
= message
;
1064 case VALIDITY_STATE_VALUE_MISSING
: {
1065 nsAutoString message
;
1066 rv
= nsContentUtils::GetMaybeLocalizedString(
1067 nsContentUtils::eDOM_PROPERTIES
, "FormValidationValueMissing",
1068 OwnerDoc(), message
);
1069 aValidationMessage
= message
;
1072 rv
= nsIConstraintValidation::GetValidationMessage(aValidationMessage
,
1079 bool HTMLTextAreaElement::IsSingleLineTextControl() const { return false; }
1081 bool HTMLTextAreaElement::IsTextArea() const { return true; }
1083 bool HTMLTextAreaElement::IsPasswordTextControl() const { return false; }
1085 int32_t HTMLTextAreaElement::GetCols() { return Cols(); }
1087 int32_t HTMLTextAreaElement::GetWrapCols() {
1088 nsHTMLTextWrap wrapProp
;
1089 TextControlElement::GetWrapPropertyEnum(this, wrapProp
);
1090 if (wrapProp
== TextControlElement::eHTMLTextWrap_Off
) {
1091 // do not wrap when wrap=off
1095 // Otherwise we just wrap at the given number of columns
1099 int32_t HTMLTextAreaElement::GetRows() {
1100 const nsAttrValue
* attr
= GetParsedAttr(nsGkAtoms::rows
);
1101 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
1102 int32_t rows
= attr
->GetIntegerValue();
1103 return (rows
<= 0) ? DEFAULT_ROWS_TEXTAREA
: rows
;
1106 return DEFAULT_ROWS_TEXTAREA
;
1109 void HTMLTextAreaElement::GetDefaultValueFromContent(nsAString
& aValue
) {
1110 GetDefaultValue(aValue
, IgnoreErrors());
1113 bool HTMLTextAreaElement::ValueChanged() const { return mValueChanged
; }
1115 void HTMLTextAreaElement::GetTextEditorValue(nsAString
& aValue
,
1116 bool aIgnoreWrap
) const {
1118 mState
->GetValue(aValue
, aIgnoreWrap
);
1121 void HTMLTextAreaElement::InitializeKeyboardEventListeners() {
1123 mState
->InitializeKeyboardEventListeners();
1126 void HTMLTextAreaElement::OnValueChanged(ValueChangeKind aKind
) {
1127 if (aKind
!= ValueChangeKind::Internal
) {
1128 mLastValueChangeWasInteractive
= aKind
== ValueChangeKind::UserInteraction
;
1131 // Update the validity state
1132 bool validBefore
= IsValid();
1133 UpdateTooLongValidityState();
1134 UpdateTooShortValidityState();
1135 UpdateValueMissingValidityState();
1137 if (validBefore
!= IsValid() || HasAttr(nsGkAtoms::placeholder
)) {
1142 bool HTMLTextAreaElement::HasCachedSelection() {
1144 return mState
->IsSelectionCached();
1147 void HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify
) {
1148 // This *has* to be called before UpdateBarredFromConstraintValidation and
1149 // UpdateValueMissingValidityState because these two functions depend on our
1151 nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify
);
1153 UpdateValueMissingValidityState();
1154 UpdateBarredFromConstraintValidation();
1155 UpdateState(aNotify
);
1158 JSObject
* HTMLTextAreaElement::WrapNode(JSContext
* aCx
,
1159 JS::Handle
<JSObject
*> aGivenProto
) {
1160 return HTMLTextAreaElement_Binding::Wrap(aCx
, this, aGivenProto
);
1163 void HTMLTextAreaElement::GetAutocomplete(DOMString
& aValue
) {
1164 const nsAttrValue
* attributeVal
= GetParsedAttr(nsGkAtoms::autocomplete
);
1166 mAutocompleteAttrState
= nsContentUtils::SerializeAutocompleteAttribute(
1167 attributeVal
, aValue
, mAutocompleteAttrState
);
1171 } // namespace mozilla