Bug 1655413 [wpt PR 24763] - Make CSP default-src without 'unsafe-eval' block eval...
[gecko.git] / dom / html / HTMLTextAreaElement.cpp
blob9932e8c1a9c45d01f3c50a7686bc153d8a4dacef
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"
24 #include "nsError.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"
31 #include "nsIForm.h"
32 #include "nsIFrame.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)
46 namespace mozilla {
47 namespace dom {
49 HTMLTextAreaElement::HTMLTextAreaElement(
50 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
51 FromParser aFromParser)
52 : TextControlElement(std::move(aNodeInfo), aFromParser, NS_FORM_TEXTAREA),
53 mValueChanged(false),
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() {
76 mState->Destroy();
77 mState = nullptr;
80 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTextAreaElement)
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTextAreaElement,
83 TextControlElement)
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
85 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
86 if (tmp->mState) {
87 tmp->mState->Traverse(cb);
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
91 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTextAreaElement,
92 TextControlElement)
93 NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
95 if (tmp->mState) {
96 tmp->mState->Unlink();
98 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
100 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement,
101 TextControlElement,
102 nsIMutationObserver,
103 nsIConstraintValidation)
105 // nsIDOMHTMLTextAreaElement
107 nsresult HTMLTextAreaElement::Clone(dom::NodeInfo* aNodeInfo,
108 nsINode** aResult) const {
109 *aResult = nullptr;
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);
116 if (mValueChanged) {
117 // Set our value on the clone.
118 nsAutoString value;
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;
127 it.forget(aResult);
128 return NS_OK;
131 // nsIContent
133 void HTMLTextAreaElement::Select() {
134 // XXX Bug? We have to give the input focus before contents can be
135 // selected
137 FocusTristate state = FocusState();
138 if (state == eUnfocusable) {
139 return;
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);
148 return;
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,
155 nullptr, &status);
157 // If the DOM event was not canceled (e.g. by a JS event handler
158 // returning false)
159 if (status == nsEventStatus_eIgnore) {
160 if (fm) {
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);
172 NS_IMETHODIMP
173 HTMLTextAreaElement::SelectAll(nsPresContext* aPresContext) {
174 nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
176 if (formControlFrame) {
177 formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
180 return NS_OK;
183 bool HTMLTextAreaElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
184 int32_t* aTabIndex) {
185 if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(
186 aWithMouse, aIsFocusable, aTabIndex)) {
187 return true;
190 // disabled textareas are not focusable
191 *aIsFocusable = !IsDisabled();
192 return false;
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 {
208 MOZ_ASSERT(mState);
209 mState->GetValue(aValue, aIgnoreWrap);
212 bool HTMLTextAreaElement::ValueEquals(const nsAString& aValue) const {
213 MOZ_ASSERT(mState);
214 return mState->ValueEquals(aValue);
217 nsIEditor* HTMLTextAreaElement::GetEditorForBindings() {
218 if (!GetPrimaryFrame()) {
219 GetPrimaryFrame(FlushType::Frames);
221 return GetTextEditor();
224 TextEditor* HTMLTextAreaElement::GetTextEditor() {
225 MOZ_ASSERT(mState);
226 return mState->GetTextEditor();
229 TextEditor* HTMLTextAreaElement::GetTextEditorWithoutCreation() {
230 MOZ_ASSERT(mState);
231 return mState->GetTextEditorWithoutCreation();
234 nsISelectionController* HTMLTextAreaElement::GetSelectionController() {
235 MOZ_ASSERT(mState);
236 return mState->GetSelectionController();
239 nsFrameSelection* HTMLTextAreaElement::GetConstFrameSelection() {
240 MOZ_ASSERT(mState);
241 return mState->GetConstFrameSelection();
244 nsresult HTMLTextAreaElement::BindToFrame(nsTextControlFrame* aFrame) {
245 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
246 MOZ_ASSERT(mState);
247 return mState->BindToFrame(aFrame);
250 void HTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame* aFrame) {
251 MOZ_ASSERT(mState);
252 if (aFrame) {
253 mState->UnbindFromFrame(aFrame);
257 nsresult HTMLTextAreaElement::CreateEditor() {
258 MOZ_ASSERT(mState);
259 return mState->PrepareEditor();
262 void HTMLTextAreaElement::UpdateOverlayTextVisibility(bool aNotify) {
263 MOZ_ASSERT(mState);
264 mState->UpdateOverlayTextVisibility(aNotify);
267 bool HTMLTextAreaElement::GetPlaceholderVisibility() {
268 MOZ_ASSERT(mState);
269 return mState->GetPlaceholderVisibility();
272 void HTMLTextAreaElement::SetPreviewValue(const nsAString& aValue) {
273 MOZ_ASSERT(mState);
274 mState->SetPreviewText(aValue, true);
277 void HTMLTextAreaElement::GetPreviewValue(nsAString& aValue) {
278 MOZ_ASSERT(mState);
279 mState->GetPreviewText(aValue);
282 void HTMLTextAreaElement::EnablePreview() {
283 if (mIsPreviewEnabled) {
284 return;
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() {
296 MOZ_ASSERT(mState);
297 return mState->GetPreviewVisibility();
300 nsresult HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
301 uint32_t aFlags) {
302 MOZ_ASSERT(mState);
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
307 // needed.
308 if (aFlags & TextControlState::eSetValue_Notify) {
309 SetValueChanged(true);
312 if (!mState->SetValue(aValue, aFlags)) {
313 return NS_ERROR_OUT_OF_MEMORY;
316 return NS_OK;
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))) {
336 aError.Throw(rv);
337 return;
340 if (mFocusedValue.Equals(currentValue)) {
341 GetValueInternal(mFocusedValue, true);
345 void HTMLTextAreaElement::SetUserInput(const nsAString& aValue,
346 nsIPrincipal& aSubjectPrincipal) {
347 SetValueInternal(
348 aValue, TextControlState::eSetValue_BySetUserInput |
349 TextControlState::eSetValue_Notify |
350 TextControlState::eSetValue_MoveCursorToEndIfValueChanged);
353 nsresult HTMLTextAreaElement::SetValueChanged(bool aValueChanged) {
354 MOZ_ASSERT(mState);
356 bool previousValue = mValueChanged;
358 mValueChanged = aValueChanged;
359 if (!aValueChanged && !mState->IsEmpty()) {
360 mState->EmptyValue();
363 if (mValueChanged != previousValue) {
364 UpdateState(true);
367 return NS_OK;
370 void HTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue,
371 ErrorResult& aError) {
372 if (!nsContentUtils::GetNodeTextContent(this, false, aDefaultValue,
373 fallible)) {
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) {
382 Reset();
384 if (NS_FAILED(rv)) {
385 aError.Throw(rv);
389 bool HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID,
390 nsAtom* aAttribute,
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);
400 return true;
401 } else if (aAttribute == nsGkAtoms::rows) {
402 aResult.ParseIntWithFallback(aValue, DEFAULT_ROWS_TEXTAREA);
403 return true;
404 } else if (aAttribute == nsGkAtoms::autocomplete) {
405 aResult.ParseAtomArray(aValue);
406 return true;
409 return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
410 aMaybeScriptedPrincipal, aResult);
413 void HTMLTextAreaElement::MapAttributesIntoRule(
414 const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls) {
415 // wrap=off
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,
425 aDecls);
426 nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes,
427 aDecls);
430 nsChangeHint HTMLTextAreaElement::GetAttributeChangeHint(
431 const nsAtom* aAttribute, int32_t aModType) const {
432 nsChangeHint retval =
433 nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute,
434 aModType);
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;
442 return retval;
445 NS_IMETHODIMP_(bool)
446 HTMLTextAreaElement::IsAttributeMapped(const nsAtom* aAttribute) const {
447 static const MappedAttributeEntry attributes[] = {{nsGkAtoms::wrap},
448 {nullptr}};
450 static const MappedAttributeEntry* const map[] = {
451 attributes,
452 sDivAlignAttributeMap,
453 sCommonAttributeMap,
456 return FindAttributeDependence(aAttribute, map);
459 nsMapRuleToAttributesFunc HTMLTextAreaElement::GetAttributeMappingFunction()
460 const {
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)) {
473 return;
476 // Don't dispatch a second select event if we are already handling
477 // one.
478 if (aVisitor.mEvent->mMessage == eFormSelect) {
479 if (mHandlingSelect) {
480 return;
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() {
503 nsString value;
504 GetValueInternal(value, true);
506 if (mFocusedValue.Equals(value)) {
507 return;
510 // Dispatch the change event.
511 mFocusedValue = value;
512 nsContentUtils::DispatchTrustedEvent(
513 OwnerDoc(), static_cast<nsIContent*>(this), u"change"_ns, CanBubble::eYes,
514 Cancelable::eNo);
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();
533 } else { // eBlur
534 mCanShowInvalidUI = true;
535 mCanShowValidUI = true;
538 UpdateState(true);
541 return NS_OK;
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.
549 Reset();
552 if (!mInhibitStateRestoration) {
553 GenerateStateKey();
554 RestoreFormControlState();
558 mDoneAddingChildren = true;
561 bool HTMLTextAreaElement::IsDoneAddingChildren() { return mDoneAddingChildren; }
563 // Controllers Methods
565 nsIControllers* HTMLTextAreaElement::GetControllers(ErrorResult& aError) {
566 if (!mControllers) {
567 mControllers = new nsXULControllers();
568 if (!mControllers) {
569 aError.Throw(NS_ERROR_FAILURE);
570 return nullptr;
573 RefPtr<nsBaseCommandController> commandController =
574 nsBaseCommandController::CreateEditorController();
575 if (!commandController) {
576 aError.Throw(NS_ERROR_FAILURE);
577 return nullptr;
580 mControllers->AppendController(commandController);
582 commandController = nsBaseCommandController::CreateEditingController();
583 if (!commandController) {
584 aError.Throw(NS_ERROR_FAILURE);
585 return nullptr;
588 mControllers->AppendController(commandController);
591 return mControllers;
594 nsresult HTMLTextAreaElement::GetControllers(nsIControllers** aResult) {
595 NS_ENSURE_ARG_POINTER(aResult);
597 ErrorResult error;
598 *aResult = GetControllers(error);
599 NS_IF_ADDREF(*aResult);
601 return error.StealNSResult();
604 uint32_t HTMLTextAreaElement::GetTextLength() {
605 nsAutoString val;
606 GetValue(val);
607 return val.Length();
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) {
618 MOZ_ASSERT(mState);
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) {
630 MOZ_ASSERT(mState);
631 mState->SetSelectionEnd(aSelectionEnd, aError);
634 void HTMLTextAreaElement::GetSelectionRange(uint32_t* aSelectionStart,
635 uint32_t* aSelectionEnd,
636 ErrorResult& aRv) {
637 MOZ_ASSERT(mState);
638 return mState->GetSelectionRange(aSelectionStart, aSelectionEnd, aRv);
641 void HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection,
642 ErrorResult& aError) {
643 MOZ_ASSERT(mState);
644 mState->GetSelectionDirectionString(aDirection, aError);
647 void HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection,
648 ErrorResult& aError) {
649 MOZ_ASSERT(mState);
650 mState->SetSelectionDirection(aDirection, aError);
653 void HTMLTextAreaElement::SetSelectionRange(
654 uint32_t aSelectionStart, uint32_t aSelectionEnd,
655 const Optional<nsAString>& aDirection, ErrorResult& aError) {
656 MOZ_ASSERT(mState);
657 mState->SetSelectionRange(aSelectionStart, aSelectionEnd, aDirection, aError);
660 void HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
661 ErrorResult& aRv) {
662 MOZ_ASSERT(mState);
663 mState->SetRangeText(aReplacement, aRv);
666 void HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
667 uint32_t aStart, uint32_t aEnd,
668 SelectionMode aSelectMode,
669 ErrorResult& aRv) {
670 MOZ_ASSERT(mState);
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);
689 nsresult rv =
690 SetValueInternal(resetVal, TextControlState::eSetValue_Internal);
691 NS_ENSURE_SUCCESS(rv, rv);
693 return NS_OK;
696 NS_IMETHODIMP
697 HTMLTextAreaElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission) {
698 // Disabled elements don't submit
699 if (IsDisabled()) {
700 return NS_OK;
704 // Get the name (if no name, no submit)
706 nsAutoString name;
707 GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
708 if (name.IsEmpty()) {
709 return NS_OK;
713 // Get the value
715 nsAutoString value;
716 GetValueInternal(value, false);
719 // Submit
721 return aFormSubmission->AddNameValuePair(name, value);
724 NS_IMETHODIMP
725 HTMLTextAreaElement::SaveState() {
726 nsresult rv = NS_OK;
728 // Only save if value != defaultValue (bug 62713)
729 PresState* state = nullptr;
730 if (mValueChanged) {
731 state = GetPrimaryPresState();
732 if (state) {
733 nsAutoString value;
734 GetValueInternal(value, true);
736 rv = nsLinebreakConverter::ConvertStringLineBreaks(
737 value, nsLinebreakConverter::eLinebreakPlatform,
738 nsLinebreakConverter::eLinebreakContent);
740 if (NS_FAILED(rv)) {
741 NS_ERROR("Converting linebreaks failed!");
742 return rv;
745 state->contentData() =
746 TextContentData(value, mLastValueChangeWasInteractive);
750 if (mDisabledChanged) {
751 if (!state) {
752 state = GetPrimaryPresState();
753 rv = NS_OK;
755 if (state) {
756 // We do not want to save the real disabled state but the disabled
757 // attribute.
758 state->disabled() = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
759 state->disabledSet() = true;
762 return rv;
765 bool HTMLTextAreaElement::RestoreState(PresState* aState) {
766 const PresContentData& state = aState->contentData();
768 if (state.type() == PresContentData::TTextContentData) {
769 ErrorResult rv;
770 SetValue(state.get_TextContentData().value(), rv);
771 ENSURE_SUCCESS(rv, false);
772 if (state.get_TextContentData().lastValueChangeWasInteractive()) {
773 mLastValueChangeWasInteractive = true;
774 UpdateState(true);
777 if (aState->disabledSet() && !aState->disabled()) {
778 SetDisabled(false, IgnoreErrors());
781 return false;
784 EventStates HTMLTextAreaElement::IntrinsicState() const {
785 EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
787 if (IsCandidateForConstraintValidation()) {
788 if (IsValid()) {
789 state |= NS_EVENT_STATE_VALID;
790 } else {
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.
794 if ((!mForm ||
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;
823 return state;
826 nsresult HTMLTextAreaElement::BindToTree(BindContext& aContext,
827 nsINode& aParent) {
828 nsresult rv =
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
838 UpdateState(false);
840 return rv;
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
851 UpdateState(false);
854 nsresult HTMLTextAreaElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
855 const nsAttrValueOrString* aValue,
856 bool aNotify) {
857 if (aNotify && aName == nsGkAtoms::disabled &&
858 aNameSpaceID == kNameSpaceID_None) {
859 mDisabledChanged = true;
862 return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
863 aValue, aNotify);
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
888 // further auditing.
889 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
890 Reset();
894 nsresult HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
895 const nsAttrValue* aValue,
896 const nsAttrValue* aOldValue,
897 nsIPrincipal* aSubjectPrincipal,
898 bool aNotify) {
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
912 // state.
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()) {
941 nsAutoString value;
942 GetValueInternal(value, true);
943 ErrorResult ret;
944 static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value, ret);
945 return ret.StealNSResult();
947 return NS_OK;
950 bool HTMLTextAreaElement::IsMutable() const {
951 return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
954 bool HTMLTextAreaElement::IsValueEmpty() const {
955 nsAutoString value;
956 GetValueInternal(value, true);
958 return value.IsEmpty();
961 void HTMLTextAreaElement::SetCustomValidity(const nsAString& aError) {
962 nsIConstraintValidation::SetCustomValidity(aError);
964 UpdateState(true);
967 bool HTMLTextAreaElement::IsTooLong() {
968 if (!mValueChanged || !mLastValueChangeWasInteractive ||
969 !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength)) {
970 return false;
973 int32_t maxLength = MaxLength();
975 // Maxlength of -1 means parsing error.
976 if (maxLength == -1) {
977 return false;
980 int32_t textLength = GetTextLength();
982 return textLength > maxLength;
985 bool HTMLTextAreaElement::IsTooShort() {
986 if (!mValueChanged || !mLastValueChangeWasInteractive ||
987 !HasAttr(kNameSpaceID_None, nsGkAtoms::minlength)) {
988 return false;
991 int32_t minLength = MinLength();
993 // Minlength of -1 means parsing error.
994 if (minLength == -1) {
995 return false;
998 int32_t textLength = GetTextLength();
1000 return textLength && textLength < minLength;
1003 bool HTMLTextAreaElement::IsValueMissing() const {
1004 if (!Required() || !IsMutable()) {
1005 return false;
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;
1032 switch (aType) {
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;
1047 } break;
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,
1061 strTextLength);
1062 aValidationMessage = message;
1063 } break;
1064 case VALIDITY_STATE_VALUE_MISSING: {
1065 nsAutoString message;
1066 rv = nsContentUtils::GetMaybeLocalizedString(
1067 nsContentUtils::eDOM_PROPERTIES, "FormValidationValueMissing",
1068 OwnerDoc(), message);
1069 aValidationMessage = message;
1070 } break;
1071 default:
1072 rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage,
1073 aType);
1076 return rv;
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
1092 return 0;
1095 // Otherwise we just wrap at the given number of columns
1096 return GetCols();
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 {
1117 MOZ_ASSERT(mState);
1118 mState->GetValue(aValue, aIgnoreWrap);
1121 void HTMLTextAreaElement::InitializeKeyboardEventListeners() {
1122 MOZ_ASSERT(mState);
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)) {
1138 UpdateState(true);
1142 bool HTMLTextAreaElement::HasCachedSelection() {
1143 MOZ_ASSERT(mState);
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
1150 // disabled state.
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);
1170 } // namespace dom
1171 } // namespace mozilla