Line data Source code
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/. */
6 :
7 : #include "mozilla/dom/HTMLTextAreaElement.h"
8 :
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/GenericSpecifiedValuesInlines.h"
17 : #include "mozilla/MouseEvents.h"
18 : #include "nsAttrValueInlines.h"
19 : #include "nsContentCID.h"
20 : #include "nsContentCreatorFunctions.h"
21 : #include "nsError.h"
22 : #include "nsFocusManager.h"
23 : #include "nsIComponentManager.h"
24 : #include "nsIConstraintValidation.h"
25 : #include "nsIControllers.h"
26 : #include "nsIDocument.h"
27 : #include "nsIDOMHTMLFormElement.h"
28 : #include "nsIFormControlFrame.h"
29 : #include "nsIFormControl.h"
30 : #include "nsIForm.h"
31 : #include "nsIFrame.h"
32 : #include "nsISupportsPrimitives.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 "nsPresState.h"
40 : #include "nsReadableUtils.h"
41 : #include "nsStyleConsts.h"
42 : #include "nsTextEditorState.h"
43 : #include "nsIController.h"
44 :
45 : static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
46 :
47 : #define NS_NO_CONTENT_DISPATCH (1 << 0)
48 :
49 2 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
50 :
51 : namespace mozilla {
52 : namespace dom {
53 :
54 1 : HTMLTextAreaElement::HTMLTextAreaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
55 1 : FromParser aFromParser)
56 : : nsGenericHTMLFormElementWithState(aNodeInfo, NS_FORM_TEXTAREA),
57 : mValueChanged(false),
58 : mLastValueChangeWasInteractive(false),
59 : mHandlingSelect(false),
60 1 : mDoneAddingChildren(!aFromParser),
61 1 : mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
62 : mDisabledChanged(false),
63 : mCanShowInvalidUI(true),
64 : mCanShowValidUI(true),
65 : mIsPreviewEnabled(false),
66 3 : mState(this)
67 : {
68 1 : AddMutationObserver(this);
69 :
70 : // Set up our default state. By default we're enabled (since we're
71 : // a control type that can be disabled but not actually disabled
72 : // right now), optional, and valid. We are NOT readwrite by default
73 : // until someone calls UpdateEditableState on us, apparently! Also
74 : // by default we don't have to show validity UI and so forth.
75 2 : AddStatesSilently(NS_EVENT_STATE_ENABLED |
76 2 : NS_EVENT_STATE_OPTIONAL |
77 3 : NS_EVENT_STATE_VALID);
78 1 : }
79 :
80 :
81 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement,
82 : nsGenericHTMLFormElementWithState,
83 : mValidity,
84 : mControllers,
85 : mState)
86 :
87 7 : NS_IMPL_ADDREF_INHERITED(HTMLTextAreaElement, Element)
88 5 : NS_IMPL_RELEASE_INHERITED(HTMLTextAreaElement, Element)
89 :
90 :
91 : // QueryInterface implementation for HTMLTextAreaElement
92 4 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement)
93 3 : NS_INTERFACE_TABLE_INHERITED(HTMLTextAreaElement,
94 : nsIDOMHTMLTextAreaElement,
95 : nsITextControlElement,
96 : nsIDOMNSEditableElement,
97 : nsIMutationObserver,
98 : nsIConstraintValidation)
99 3 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
100 :
101 :
102 : // nsIDOMHTMLTextAreaElement
103 :
104 : nsresult
105 0 : HTMLTextAreaElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
106 : bool aPreallocateChildren) const
107 : {
108 0 : *aResult = nullptr;
109 : already_AddRefed<mozilla::dom::NodeInfo> ni =
110 0 : RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
111 0 : RefPtr<HTMLTextAreaElement> it = new HTMLTextAreaElement(ni);
112 :
113 0 : nsresult rv = const_cast<HTMLTextAreaElement*>(this)->CopyInnerTo(it, aPreallocateChildren);
114 0 : NS_ENSURE_SUCCESS(rv, rv);
115 :
116 0 : if (mValueChanged) {
117 : // Set our value on the clone.
118 0 : nsAutoString value;
119 0 : GetValueInternal(value, true);
120 :
121 : // SetValueInternal handles setting mValueChanged for us
122 0 : rv = it->SetValueInternal(value, nsTextEditorState::eSetValue_Notify);
123 0 : NS_ENSURE_SUCCESS(rv, rv);
124 : }
125 :
126 0 : it->mLastValueChangeWasInteractive = mLastValueChangeWasInteractive;
127 0 : it.forget(aResult);
128 0 : return NS_OK;
129 : }
130 :
131 : // nsIConstraintValidation
132 0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLTextAreaElement)
133 :
134 :
135 : NS_IMETHODIMP
136 0 : HTMLTextAreaElement::GetForm(nsIDOMHTMLFormElement** aForm)
137 : {
138 0 : return nsGenericHTMLFormElementWithState::GetForm(aForm);
139 : }
140 :
141 :
142 : // nsIContent
143 :
144 : NS_IMETHODIMP
145 0 : HTMLTextAreaElement::Select()
146 : {
147 : // XXX Bug? We have to give the input focus before contents can be
148 : // selected
149 :
150 0 : FocusTristate state = FocusState();
151 0 : if (state == eUnfocusable) {
152 0 : return NS_OK;
153 : }
154 :
155 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
156 :
157 0 : RefPtr<nsPresContext> presContext = GetPresContext(eForComposedDoc);
158 0 : if (state == eInactiveWindow) {
159 0 : if (fm)
160 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
161 0 : SelectAll(presContext);
162 0 : return NS_OK;
163 : }
164 :
165 0 : nsEventStatus status = nsEventStatus_eIgnore;
166 0 : WidgetGUIEvent event(true, eFormSelect, nullptr);
167 : // XXXbz HTMLInputElement guards against this reentering; shouldn't we?
168 0 : EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
169 0 : &event, nullptr, &status);
170 :
171 : // If the DOM event was not canceled (e.g. by a JS event handler
172 : // returning false)
173 0 : if (status == nsEventStatus_eIgnore) {
174 0 : if (fm) {
175 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
176 :
177 : // ensure that the element is actually focused
178 0 : nsCOMPtr<nsIDOMElement> focusedElement;
179 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
180 0 : if (SameCOMIdentity(static_cast<nsIDOMNode*>(this), focusedElement)) {
181 : // Now Select all the text!
182 0 : SelectAll(presContext);
183 : }
184 : }
185 : }
186 :
187 0 : return NS_OK;
188 : }
189 :
190 : NS_IMETHODIMP
191 0 : HTMLTextAreaElement::SelectAll(nsPresContext* aPresContext)
192 : {
193 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
194 :
195 0 : if (formControlFrame) {
196 0 : formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
197 : }
198 :
199 0 : return NS_OK;
200 : }
201 :
202 : bool
203 0 : HTMLTextAreaElement::IsHTMLFocusable(bool aWithMouse,
204 : bool *aIsFocusable, int32_t *aTabIndex)
205 : {
206 0 : if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable,
207 : aTabIndex))
208 : {
209 0 : return true;
210 : }
211 :
212 : // disabled textareas are not focusable
213 0 : *aIsFocusable = !IsDisabled();
214 0 : return false;
215 : }
216 :
217 0 : NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Autofocus, autofocus)
218 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Cols, cols, DEFAULT_COLS)
219 0 : NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Disabled, disabled)
220 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLTextAreaElement, MaxLength, maxlength)
221 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLTextAreaElement, MinLength, minlength)
222 0 : NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Name, name)
223 0 : NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, ReadOnly, readonly)
224 0 : NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Required, required)
225 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Rows, rows, DEFAULT_ROWS_TEXTAREA)
226 0 : NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Wrap, wrap)
227 0 : NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Placeholder, placeholder)
228 :
229 : int32_t
230 0 : HTMLTextAreaElement::TabIndexDefault()
231 : {
232 0 : return 0;
233 : }
234 :
235 : NS_IMETHODIMP
236 0 : HTMLTextAreaElement::GetType(nsAString& aType)
237 : {
238 0 : aType.AssignLiteral("textarea");
239 :
240 0 : return NS_OK;
241 : }
242 :
243 : NS_IMETHODIMP
244 0 : HTMLTextAreaElement::GetValue(nsAString& aValue)
245 : {
246 0 : nsAutoString value;
247 0 : GetValueInternal(value, true);
248 :
249 : // Normalize CRLF and CR to LF
250 0 : nsContentUtils::PlatformToDOMLineBreaks(value);
251 :
252 0 : aValue = value;
253 0 : return NS_OK;
254 : }
255 :
256 : void
257 0 : HTMLTextAreaElement::GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const
258 : {
259 0 : mState.GetValue(aValue, aIgnoreWrap);
260 0 : }
261 :
262 : NS_IMETHODIMP_(TextEditor*)
263 0 : HTMLTextAreaElement::GetTextEditor()
264 : {
265 0 : return mState.GetTextEditor();
266 : }
267 :
268 : NS_IMETHODIMP_(nsISelectionController*)
269 0 : HTMLTextAreaElement::GetSelectionController()
270 : {
271 0 : return mState.GetSelectionController();
272 : }
273 :
274 : NS_IMETHODIMP_(nsFrameSelection*)
275 0 : HTMLTextAreaElement::GetConstFrameSelection()
276 : {
277 0 : return mState.GetConstFrameSelection();
278 : }
279 :
280 : NS_IMETHODIMP
281 0 : HTMLTextAreaElement::BindToFrame(nsTextControlFrame* aFrame)
282 : {
283 0 : return mState.BindToFrame(aFrame);
284 : }
285 :
286 : NS_IMETHODIMP_(void)
287 0 : HTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame* aFrame)
288 : {
289 0 : if (aFrame) {
290 0 : mState.UnbindFromFrame(aFrame);
291 : }
292 0 : }
293 :
294 : NS_IMETHODIMP
295 0 : HTMLTextAreaElement::CreateEditor()
296 : {
297 0 : return mState.PrepareEditor();
298 : }
299 :
300 : NS_IMETHODIMP_(Element*)
301 0 : HTMLTextAreaElement::GetRootEditorNode()
302 : {
303 0 : return mState.GetRootNode();
304 : }
305 :
306 : NS_IMETHODIMP_(Element*)
307 0 : HTMLTextAreaElement::CreatePlaceholderNode()
308 : {
309 0 : NS_ENSURE_SUCCESS(mState.CreatePlaceholderNode(), nullptr);
310 0 : return mState.GetPlaceholderNode();
311 : }
312 :
313 : NS_IMETHODIMP_(Element*)
314 0 : HTMLTextAreaElement::GetPlaceholderNode()
315 : {
316 0 : return mState.GetPlaceholderNode();
317 : }
318 :
319 : NS_IMETHODIMP_(void)
320 0 : HTMLTextAreaElement::UpdateOverlayTextVisibility(bool aNotify)
321 : {
322 0 : mState.UpdateOverlayTextVisibility(aNotify);
323 0 : }
324 :
325 : NS_IMETHODIMP_(bool)
326 0 : HTMLTextAreaElement::GetPlaceholderVisibility()
327 : {
328 0 : return mState.GetPlaceholderVisibility();
329 : }
330 :
331 : NS_IMETHODIMP_(Element*)
332 0 : HTMLTextAreaElement::CreatePreviewNode()
333 : {
334 0 : NS_ENSURE_SUCCESS(mState.CreatePreviewNode(), nullptr);
335 0 : return mState.GetPreviewNode();
336 : }
337 :
338 : NS_IMETHODIMP_(Element*)
339 0 : HTMLTextAreaElement::GetPreviewNode()
340 : {
341 0 : return mState.GetPreviewNode();
342 : }
343 :
344 : NS_IMETHODIMP_(void)
345 0 : HTMLTextAreaElement::SetPreviewValue(const nsAString& aValue)
346 : {
347 0 : mState.SetPreviewText(aValue, true);
348 0 : }
349 :
350 : NS_IMETHODIMP_(void)
351 0 : HTMLTextAreaElement::GetPreviewValue(nsAString& aValue)
352 : {
353 0 : mState.GetPreviewText(aValue);
354 0 : }
355 :
356 : NS_IMETHODIMP_(void)
357 0 : HTMLTextAreaElement::EnablePreview()
358 : {
359 0 : if (mIsPreviewEnabled) {
360 0 : return;
361 : }
362 :
363 0 : mIsPreviewEnabled = true;
364 : // Reconstruct the frame to append an anonymous preview node
365 0 : nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), nsChangeHint_ReconstructFrame);
366 : }
367 :
368 : NS_IMETHODIMP_(bool)
369 0 : HTMLTextAreaElement::IsPreviewEnabled()
370 : {
371 0 : return mIsPreviewEnabled;
372 : }
373 :
374 : NS_IMETHODIMP_(bool)
375 0 : HTMLTextAreaElement::GetPreviewVisibility()
376 : {
377 0 : return mState.GetPreviewVisibility();
378 : }
379 :
380 : nsresult
381 0 : HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
382 : uint32_t aFlags)
383 : {
384 : // Need to set the value changed flag here if our value has in fact changed
385 : // (i.e. if eSetValue_Notify is in aFlags), so that
386 : // nsTextControlFrame::UpdateValueDisplay retrieves the correct value if
387 : // needed.
388 0 : if (aFlags & nsTextEditorState::eSetValue_Notify) {
389 0 : SetValueChanged(true);
390 : }
391 :
392 0 : if (!mState.SetValue(aValue, aFlags)) {
393 0 : return NS_ERROR_OUT_OF_MEMORY;
394 : }
395 :
396 0 : return NS_OK;
397 : }
398 :
399 : NS_IMETHODIMP
400 0 : HTMLTextAreaElement::SetValue(const nsAString& aValue)
401 : {
402 : // If the value has been set by a script, we basically want to keep the
403 : // current change event state. If the element is ready to fire a change
404 : // event, we should keep it that way. Otherwise, we should make sure the
405 : // element will not fire any event because of the script interaction.
406 : //
407 : // NOTE: this is currently quite expensive work (too much string
408 : // manipulation). We should probably optimize that.
409 0 : nsAutoString currentValue;
410 0 : GetValueInternal(currentValue, true);
411 :
412 : nsresult rv =
413 : SetValueInternal(aValue,
414 : nsTextEditorState::eSetValue_ByContent |
415 : nsTextEditorState::eSetValue_Notify |
416 0 : nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
417 0 : NS_ENSURE_SUCCESS(rv, rv);
418 :
419 0 : if (mFocusedValue.Equals(currentValue)) {
420 0 : GetValueInternal(mFocusedValue, true);
421 : }
422 :
423 0 : return NS_OK;
424 : }
425 :
426 : NS_IMETHODIMP
427 0 : HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
428 : {
429 : return SetValueInternal(aValue,
430 : nsTextEditorState::eSetValue_BySetUserInput |
431 : nsTextEditorState::eSetValue_Notify|
432 0 : nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
433 : }
434 :
435 : NS_IMETHODIMP
436 0 : HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
437 : {
438 0 : bool previousValue = mValueChanged;
439 :
440 0 : mValueChanged = aValueChanged;
441 0 : if (!aValueChanged && !mState.IsEmpty()) {
442 0 : mState.EmptyValue();
443 : }
444 :
445 0 : if (mValueChanged != previousValue) {
446 0 : UpdateState(true);
447 : }
448 :
449 0 : return NS_OK;
450 : }
451 :
452 : NS_IMETHODIMP
453 0 : HTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
454 : {
455 0 : if (!nsContentUtils::GetNodeTextContent(this, false, aDefaultValue, fallible)) {
456 0 : return NS_ERROR_OUT_OF_MEMORY;
457 : }
458 0 : return NS_OK;
459 : }
460 :
461 : NS_IMETHODIMP
462 0 : HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue)
463 : {
464 0 : ErrorResult error;
465 0 : SetDefaultValue(aDefaultValue, error);
466 0 : return error.StealNSResult();
467 : }
468 :
469 : void
470 0 : HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError)
471 : {
472 0 : nsresult rv = nsContentUtils::SetNodeTextContent(this, aDefaultValue, true);
473 0 : if (NS_SUCCEEDED(rv) && !mValueChanged) {
474 0 : Reset();
475 : }
476 0 : if (NS_FAILED(rv)) {
477 0 : aError.Throw(rv);
478 : }
479 0 : }
480 :
481 : bool
482 2 : HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID,
483 : nsIAtom* aAttribute,
484 : const nsAString& aValue,
485 : nsAttrValue& aResult)
486 : {
487 2 : if (aNamespaceID == kNameSpaceID_None) {
488 2 : if (aAttribute == nsGkAtoms::maxlength ||
489 1 : aAttribute == nsGkAtoms::minlength) {
490 0 : return aResult.ParseNonNegativeIntValue(aValue);
491 1 : } else if (aAttribute == nsGkAtoms::cols) {
492 0 : aResult.ParseIntWithFallback(aValue, DEFAULT_COLS);
493 0 : return true;
494 1 : } else if (aAttribute == nsGkAtoms::rows) {
495 0 : aResult.ParseIntWithFallback(aValue, DEFAULT_ROWS_TEXTAREA);
496 0 : return true;
497 : }
498 : }
499 2 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
500 2 : aResult);
501 : }
502 :
503 : void
504 0 : HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
505 : GenericSpecifiedValues* aData)
506 : {
507 0 : if (aData->ShouldComputeStyleStruct(NS_STYLE_INHERIT_BIT(Text))) {
508 : // wrap=off
509 0 : if (!aData->PropertyIsSet(eCSSProperty_white_space)) {
510 0 : const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
511 0 : if (value && value->Type() == nsAttrValue::eString &&
512 0 : value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
513 0 : aData->SetKeywordValue(eCSSProperty_white_space, StyleWhiteSpace::Pre);
514 : }
515 : }
516 : }
517 :
518 0 : nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes, aData);
519 0 : nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
520 0 : }
521 :
522 : nsChangeHint
523 0 : HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
524 : int32_t aModType) const
525 : {
526 : nsChangeHint retval =
527 0 : nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
528 0 : if (aAttribute == nsGkAtoms::rows ||
529 0 : aAttribute == nsGkAtoms::cols) {
530 0 : retval |= NS_STYLE_HINT_REFLOW;
531 0 : } else if (aAttribute == nsGkAtoms::wrap) {
532 0 : retval |= nsChangeHint_ReconstructFrame;
533 0 : } else if (aAttribute == nsGkAtoms::placeholder) {
534 0 : retval |= nsChangeHint_ReconstructFrame;
535 : }
536 0 : return retval;
537 : }
538 :
539 : NS_IMETHODIMP_(bool)
540 2 : HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
541 : {
542 : static const MappedAttributeEntry attributes[] = {
543 : { &nsGkAtoms::wrap },
544 : { nullptr }
545 : };
546 :
547 : static const MappedAttributeEntry* const map[] = {
548 : attributes,
549 : sDivAlignAttributeMap,
550 : sCommonAttributeMap,
551 : };
552 :
553 2 : return FindAttributeDependence(aAttribute, map);
554 : }
555 :
556 : nsMapRuleToAttributesFunc
557 0 : HTMLTextAreaElement::GetAttributeMappingFunction() const
558 : {
559 0 : return &MapAttributesIntoRule;
560 : }
561 :
562 : bool
563 0 : HTMLTextAreaElement::IsDisabledForEvents(EventMessage aMessage)
564 : {
565 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
566 0 : nsIFrame* formFrame = do_QueryFrame(formControlFrame);
567 0 : return IsElementDisabledForEvents(aMessage, formFrame);
568 : }
569 :
570 : nsresult
571 0 : HTMLTextAreaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
572 : {
573 0 : aVisitor.mCanHandle = false;
574 0 : if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
575 0 : return NS_OK;
576 : }
577 :
578 : // Don't dispatch a second select event if we are already handling
579 : // one.
580 0 : if (aVisitor.mEvent->mMessage == eFormSelect) {
581 0 : if (mHandlingSelect) {
582 0 : return NS_OK;
583 : }
584 0 : mHandlingSelect = true;
585 : }
586 :
587 : // If noContentDispatch is true we will not allow content to handle
588 : // this event. But to allow middle mouse button paste to work we must allow
589 : // middle clicks to go to text fields anyway.
590 0 : if (aVisitor.mEvent->mFlags.mNoContentDispatch) {
591 0 : aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
592 : }
593 0 : if (aVisitor.mEvent->mMessage == eMouseClick &&
594 0 : aVisitor.mEvent->AsMouseEvent()->button ==
595 : WidgetMouseEvent::eMiddleButton) {
596 0 : aVisitor.mEvent->mFlags.mNoContentDispatch = false;
597 : }
598 :
599 0 : if (aVisitor.mEvent->mMessage == eBlur) {
600 : // Set mWantsPreHandleEvent and fire change event in PreHandleEvent to
601 : // prevent it breaks event target chain creation.
602 0 : aVisitor.mWantsPreHandleEvent = true;
603 : }
604 :
605 0 : return nsGenericHTMLFormElementWithState::GetEventTargetParent(aVisitor);
606 : }
607 :
608 : nsresult
609 0 : HTMLTextAreaElement::PreHandleEvent(EventChainVisitor& aVisitor)
610 : {
611 0 : if (aVisitor.mEvent->mMessage == eBlur) {
612 : // Fire onchange (if necessary), before we do the blur, bug 370521.
613 0 : FireChangeEventIfNeeded();
614 : }
615 0 : return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor);
616 : }
617 :
618 : void
619 0 : HTMLTextAreaElement::FireChangeEventIfNeeded()
620 : {
621 0 : nsString value;
622 0 : GetValueInternal(value, true);
623 :
624 0 : if (mFocusedValue.Equals(value)) {
625 0 : return;
626 : }
627 :
628 : // Dispatch the change event.
629 0 : mFocusedValue = value;
630 0 : nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
631 : static_cast<nsIContent*>(this),
632 0 : NS_LITERAL_STRING("change"), true,
633 0 : false);
634 : }
635 :
636 : nsresult
637 0 : HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
638 : {
639 0 : if (aVisitor.mEvent->mMessage == eFormSelect) {
640 0 : mHandlingSelect = false;
641 : }
642 :
643 0 : if (aVisitor.mEvent->mMessage == eFocus ||
644 0 : aVisitor.mEvent->mMessage == eBlur) {
645 0 : if (aVisitor.mEvent->mMessage == eFocus) {
646 : // If the invalid UI is shown, we should show it while focusing (and
647 : // update). Otherwise, we should not.
648 0 : GetValueInternal(mFocusedValue, true);
649 0 : mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
650 :
651 : // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
652 : // UI while typing.
653 0 : mCanShowValidUI = ShouldShowValidityUI();
654 : } else { // eBlur
655 0 : mCanShowInvalidUI = true;
656 0 : mCanShowValidUI = true;
657 : }
658 :
659 0 : UpdateState(true);
660 : }
661 :
662 : // Reset the flag for other content besides this text field
663 0 : aVisitor.mEvent->mFlags.mNoContentDispatch =
664 0 : ((aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH) != 0);
665 :
666 0 : return NS_OK;
667 : }
668 :
669 : void
670 0 : HTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified)
671 : {
672 0 : if (!mValueChanged) {
673 0 : if (!mDoneAddingChildren) {
674 : // Reset now that we're done adding children if the content sink tried to
675 : // sneak some text in without calling AppendChildTo.
676 0 : Reset();
677 : }
678 :
679 0 : if (!mInhibitStateRestoration) {
680 0 : nsresult rv = GenerateStateKey();
681 0 : if (NS_SUCCEEDED(rv)) {
682 0 : RestoreFormControlState();
683 : }
684 : }
685 : }
686 :
687 0 : mDoneAddingChildren = true;
688 0 : }
689 :
690 : bool
691 0 : HTMLTextAreaElement::IsDoneAddingChildren()
692 : {
693 0 : return mDoneAddingChildren;
694 : }
695 :
696 : // Controllers Methods
697 :
698 : nsIControllers*
699 0 : HTMLTextAreaElement::GetControllers(ErrorResult& aError)
700 : {
701 0 : if (!mControllers)
702 : {
703 : nsresult rv;
704 0 : mControllers = do_CreateInstance(kXULControllersCID, &rv);
705 0 : if (NS_FAILED(rv)) {
706 0 : aError.Throw(rv);
707 0 : return nullptr;
708 : }
709 :
710 0 : nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/editor/editorcontroller;1", &rv);
711 0 : if (NS_FAILED(rv)) {
712 0 : aError.Throw(rv);
713 0 : return nullptr;
714 : }
715 :
716 0 : mControllers->AppendController(controller);
717 :
718 0 : controller = do_CreateInstance("@mozilla.org/editor/editingcontroller;1", &rv);
719 0 : if (NS_FAILED(rv)) {
720 0 : aError.Throw(rv);
721 0 : return nullptr;
722 : }
723 :
724 0 : mControllers->AppendController(controller);
725 : }
726 :
727 0 : return mControllers;
728 : }
729 :
730 : NS_IMETHODIMP
731 0 : HTMLTextAreaElement::GetControllers(nsIControllers** aResult)
732 : {
733 0 : NS_ENSURE_ARG_POINTER(aResult);
734 :
735 0 : ErrorResult error;
736 0 : *aResult = GetControllers(error);
737 0 : NS_IF_ADDREF(*aResult);
738 :
739 0 : return error.StealNSResult();
740 : }
741 :
742 : uint32_t
743 0 : HTMLTextAreaElement::GetTextLength()
744 : {
745 0 : nsAutoString val;
746 0 : GetValue(val);
747 0 : return val.Length();
748 : }
749 :
750 : NS_IMETHODIMP
751 0 : HTMLTextAreaElement::GetTextLength(int32_t *aTextLength)
752 : {
753 0 : NS_ENSURE_ARG_POINTER(aTextLength);
754 0 : *aTextLength = GetTextLength();
755 :
756 0 : return NS_OK;
757 : }
758 :
759 : Nullable<uint32_t>
760 0 : HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
761 : {
762 : uint32_t selStart, selEnd;
763 0 : GetSelectionRange(&selStart, &selEnd, aError);
764 0 : return Nullable<uint32_t>(selStart);
765 : }
766 :
767 : void
768 0 : HTMLTextAreaElement::SetSelectionStart(const Nullable<uint32_t>& aSelectionStart,
769 : ErrorResult& aError)
770 : {
771 0 : mState.SetSelectionStart(aSelectionStart, aError);
772 0 : }
773 :
774 : Nullable<uint32_t>
775 0 : HTMLTextAreaElement::GetSelectionEnd(ErrorResult& aError)
776 : {
777 : uint32_t selStart, selEnd;
778 0 : GetSelectionRange(&selStart, &selEnd, aError);
779 0 : return Nullable<uint32_t>(selEnd);
780 : }
781 :
782 : void
783 0 : HTMLTextAreaElement::SetSelectionEnd(const Nullable<uint32_t>& aSelectionEnd,
784 : ErrorResult& aError)
785 : {
786 0 : mState.SetSelectionEnd(aSelectionEnd, aError);
787 0 : }
788 :
789 : void
790 0 : HTMLTextAreaElement::GetSelectionRange(uint32_t* aSelectionStart,
791 : uint32_t* aSelectionEnd,
792 : ErrorResult& aRv)
793 : {
794 0 : return mState.GetSelectionRange(aSelectionStart, aSelectionEnd, aRv);
795 : }
796 :
797 : void
798 0 : HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aError)
799 : {
800 0 : mState.GetSelectionDirectionString(aDirection, aError);
801 0 : }
802 :
803 : void
804 0 : HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection,
805 : ErrorResult& aError)
806 : {
807 0 : mState.SetSelectionDirection(aDirection, aError);
808 0 : }
809 :
810 : void
811 0 : HTMLTextAreaElement::SetSelectionRange(uint32_t aSelectionStart,
812 : uint32_t aSelectionEnd,
813 : const Optional<nsAString>& aDirection,
814 : ErrorResult& aError)
815 : {
816 0 : mState.SetSelectionRange(aSelectionStart, aSelectionEnd,
817 0 : aDirection, aError);
818 0 : }
819 :
820 : void
821 0 : HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
822 : ErrorResult& aRv)
823 : {
824 0 : mState.SetRangeText(aReplacement, aRv);
825 0 : }
826 :
827 : void
828 0 : HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
829 : uint32_t aStart, uint32_t aEnd,
830 : SelectionMode aSelectMode,
831 : ErrorResult& aRv)
832 : {
833 0 : mState.SetRangeText(aReplacement, aStart, aEnd, aSelectMode, aRv);
834 0 : }
835 :
836 : void
837 0 : HTMLTextAreaElement::GetValueFromSetRangeText(nsAString& aValue)
838 : {
839 0 : GetValueInternal(aValue, false);
840 0 : }
841 :
842 : nsresult
843 0 : HTMLTextAreaElement::SetValueFromSetRangeText(const nsAString& aValue)
844 : {
845 : return SetValueInternal(aValue,
846 : nsTextEditorState::eSetValue_ByContent |
847 0 : nsTextEditorState::eSetValue_Notify);
848 : }
849 :
850 : nsresult
851 0 : HTMLTextAreaElement::Reset()
852 : {
853 0 : nsAutoString resetVal;
854 0 : GetDefaultValue(resetVal);
855 0 : SetValueChanged(false);
856 :
857 : nsresult rv = SetValueInternal(resetVal,
858 0 : nsTextEditorState::eSetValue_Internal);
859 0 : NS_ENSURE_SUCCESS(rv, rv);
860 :
861 0 : return NS_OK;
862 : }
863 :
864 : NS_IMETHODIMP
865 0 : HTMLTextAreaElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
866 : {
867 : // Disabled elements don't submit
868 0 : if (IsDisabled()) {
869 0 : return NS_OK;
870 : }
871 :
872 : //
873 : // Get the name (if no name, no submit)
874 : //
875 0 : nsAutoString name;
876 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
877 0 : if (name.IsEmpty()) {
878 0 : return NS_OK;
879 : }
880 :
881 : //
882 : // Get the value
883 : //
884 0 : nsAutoString value;
885 0 : GetValueInternal(value, false);
886 :
887 : //
888 : // Submit
889 : //
890 0 : return aFormSubmission->AddNameValuePair(name, value);
891 : }
892 :
893 : NS_IMETHODIMP
894 0 : HTMLTextAreaElement::SaveState()
895 : {
896 0 : nsresult rv = NS_OK;
897 :
898 : // Only save if value != defaultValue (bug 62713)
899 0 : nsPresState *state = nullptr;
900 0 : if (mValueChanged) {
901 0 : state = GetPrimaryPresState();
902 0 : if (state) {
903 0 : nsAutoString value;
904 0 : GetValueInternal(value, true);
905 :
906 : rv = nsLinebreakConverter::ConvertStringLineBreaks(
907 : value,
908 : nsLinebreakConverter::eLinebreakPlatform,
909 0 : nsLinebreakConverter::eLinebreakContent);
910 :
911 0 : if (NS_FAILED(rv)) {
912 0 : NS_ERROR("Converting linebreaks failed!");
913 0 : return rv;
914 : }
915 :
916 : nsCOMPtr<nsISupportsString> pState =
917 0 : do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
918 0 : if (!pState) {
919 0 : return NS_ERROR_OUT_OF_MEMORY;
920 : }
921 0 : pState->SetData(value);
922 0 : state->SetStateProperty(pState);
923 : }
924 : }
925 :
926 0 : if (mDisabledChanged) {
927 0 : if (!state) {
928 0 : state = GetPrimaryPresState();
929 0 : rv = NS_OK;
930 : }
931 0 : if (state) {
932 : // We do not want to save the real disabled state but the disabled
933 : // attribute.
934 0 : state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
935 : }
936 : }
937 0 : return rv;
938 : }
939 :
940 : bool
941 0 : HTMLTextAreaElement::RestoreState(nsPresState* aState)
942 : {
943 : nsCOMPtr<nsISupportsString> state
944 0 : (do_QueryInterface(aState->GetStateProperty()));
945 :
946 0 : if (state) {
947 0 : nsAutoString data;
948 0 : state->GetData(data);
949 0 : nsresult rv = SetValue(data);
950 0 : NS_ENSURE_SUCCESS(rv, false);
951 : }
952 :
953 0 : if (aState->IsDisabledSet() && !aState->GetDisabled()) {
954 0 : SetDisabled(false);
955 : }
956 :
957 0 : return false;
958 : }
959 :
960 : EventStates
961 6 : HTMLTextAreaElement::IntrinsicState() const
962 : {
963 6 : EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
964 :
965 6 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
966 0 : state |= NS_EVENT_STATE_REQUIRED;
967 : } else {
968 6 : state |= NS_EVENT_STATE_OPTIONAL;
969 : }
970 :
971 6 : if (IsCandidateForConstraintValidation()) {
972 6 : if (IsValid()) {
973 6 : state |= NS_EVENT_STATE_VALID;
974 : } else {
975 0 : state |= NS_EVENT_STATE_INVALID;
976 : // :-moz-ui-invalid always apply if the element suffers from a custom
977 : // error and never applies if novalidate is set on the form owner.
978 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
979 0 : (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
980 0 : (mCanShowInvalidUI && ShouldShowValidityUI()))) {
981 0 : state |= NS_EVENT_STATE_MOZ_UI_INVALID;
982 : }
983 : }
984 :
985 : // :-moz-ui-valid applies if all the following are true:
986 : // 1. The element is not focused, or had either :-moz-ui-valid or
987 : // :-moz-ui-invalid applying before it was focused ;
988 : // 2. The element is either valid or isn't allowed to have
989 : // :-moz-ui-invalid applying ;
990 : // 3. The element has no form owner or its form owner doesn't have the
991 : // novalidate attribute set ;
992 : // 4. The element has already been modified or the user tried to submit the
993 : // form owner while invalid.
994 24 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
995 12 : (mCanShowValidUI && ShouldShowValidityUI() &&
996 6 : (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
997 0 : !mCanShowInvalidUI)))) {
998 0 : state |= NS_EVENT_STATE_MOZ_UI_VALID;
999 : }
1000 : }
1001 :
1002 6 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
1003 0 : IsValueEmpty()) {
1004 0 : state |= NS_EVENT_STATE_PLACEHOLDERSHOWN;
1005 : }
1006 :
1007 6 : return state;
1008 : }
1009 :
1010 : nsresult
1011 3 : HTMLTextAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1012 : nsIContent* aBindingParent,
1013 : bool aCompileEventHandlers)
1014 : {
1015 3 : nsresult rv = nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent,
1016 : aBindingParent,
1017 3 : aCompileEventHandlers);
1018 3 : NS_ENSURE_SUCCESS(rv, rv);
1019 :
1020 : // If there is a disabled fieldset in the parent chain, the element is now
1021 : // barred from constraint validation and can't suffer from value missing.
1022 3 : UpdateValueMissingValidityState();
1023 3 : UpdateBarredFromConstraintValidation();
1024 :
1025 : // And now make sure our state is up to date
1026 3 : UpdateState(false);
1027 :
1028 3 : return rv;
1029 : }
1030 :
1031 : void
1032 0 : HTMLTextAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
1033 : {
1034 0 : nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
1035 :
1036 : // We might be no longer disabled because of parent chain changed.
1037 0 : UpdateValueMissingValidityState();
1038 0 : UpdateBarredFromConstraintValidation();
1039 :
1040 : // And now make sure our state is up to date
1041 0 : UpdateState(false);
1042 0 : }
1043 :
1044 : nsresult
1045 3 : HTMLTextAreaElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1046 : const nsAttrValueOrString* aValue,
1047 : bool aNotify)
1048 : {
1049 3 : if (aNotify && aName == nsGkAtoms::disabled &&
1050 : aNameSpaceID == kNameSpaceID_None) {
1051 0 : mDisabledChanged = true;
1052 : }
1053 :
1054 3 : return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
1055 3 : aValue, aNotify);
1056 : }
1057 :
1058 : void
1059 0 : HTMLTextAreaElement::CharacterDataChanged(nsIDocument* aDocument,
1060 : nsIContent* aContent,
1061 : CharacterDataChangeInfo* aInfo)
1062 : {
1063 0 : ContentChanged(aContent);
1064 0 : }
1065 :
1066 : void
1067 0 : HTMLTextAreaElement::ContentAppended(nsIDocument* aDocument,
1068 : nsIContent* aContainer,
1069 : nsIContent* aFirstNewContent,
1070 : int32_t /* unused */)
1071 : {
1072 0 : ContentChanged(aFirstNewContent);
1073 0 : }
1074 :
1075 : void
1076 0 : HTMLTextAreaElement::ContentInserted(nsIDocument* aDocument,
1077 : nsIContent* aContainer,
1078 : nsIContent* aChild,
1079 : int32_t /* unused */)
1080 : {
1081 0 : ContentChanged(aChild);
1082 0 : }
1083 :
1084 : void
1085 0 : HTMLTextAreaElement::ContentRemoved(nsIDocument* aDocument,
1086 : nsIContent* aContainer,
1087 : nsIContent* aChild,
1088 : int32_t aIndexInContainer,
1089 : nsIContent* aPreviousSibling)
1090 : {
1091 0 : ContentChanged(aChild);
1092 0 : }
1093 :
1094 : void
1095 0 : HTMLTextAreaElement::ContentChanged(nsIContent* aContent)
1096 : {
1097 0 : if (!mValueChanged && mDoneAddingChildren &&
1098 0 : nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
1099 : // Hard to say what the reset can trigger, so be safe pending
1100 : // further auditing.
1101 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1102 0 : Reset();
1103 : }
1104 0 : }
1105 :
1106 : nsresult
1107 3 : HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
1108 : const nsAttrValue* aValue,
1109 : const nsAttrValue* aOldValue, bool aNotify)
1110 : {
1111 3 : if (aNameSpaceID == kNameSpaceID_None) {
1112 4 : if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
1113 2 : aName == nsGkAtoms::readonly) {
1114 0 : UpdateValueMissingValidityState();
1115 :
1116 : // This *has* to be called *after* validity has changed.
1117 0 : if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
1118 0 : UpdateBarredFromConstraintValidation();
1119 : }
1120 2 : } else if (aName == nsGkAtoms::maxlength) {
1121 0 : UpdateTooLongValidityState();
1122 2 : } else if (aName == nsGkAtoms::minlength) {
1123 0 : UpdateTooShortValidityState();
1124 : }
1125 : }
1126 :
1127 3 : return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, aValue,
1128 3 : aOldValue, aNotify);
1129 : }
1130 :
1131 : nsresult
1132 0 : HTMLTextAreaElement::CopyInnerTo(Element* aDest, bool aPreallocateChildren)
1133 : {
1134 0 : nsresult rv = nsGenericHTMLFormElementWithState::CopyInnerTo(aDest,
1135 0 : aPreallocateChildren);
1136 0 : NS_ENSURE_SUCCESS(rv, rv);
1137 :
1138 0 : if (aDest->OwnerDoc()->IsStaticDocument()) {
1139 0 : nsAutoString value;
1140 0 : GetValueInternal(value, true);
1141 0 : return static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
1142 : }
1143 0 : return NS_OK;
1144 : }
1145 :
1146 : bool
1147 0 : HTMLTextAreaElement::IsMutable() const
1148 : {
1149 0 : return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
1150 : }
1151 :
1152 : bool
1153 0 : HTMLTextAreaElement::IsValueEmpty() const
1154 : {
1155 0 : nsAutoString value;
1156 0 : GetValueInternal(value, true);
1157 :
1158 0 : return value.IsEmpty();
1159 : }
1160 :
1161 : // nsIConstraintValidation
1162 :
1163 : NS_IMETHODIMP
1164 0 : HTMLTextAreaElement::SetCustomValidity(const nsAString& aError)
1165 : {
1166 0 : nsIConstraintValidation::SetCustomValidity(aError);
1167 :
1168 0 : UpdateState(true);
1169 :
1170 0 : return NS_OK;
1171 : }
1172 :
1173 : bool
1174 0 : HTMLTextAreaElement::IsTooLong()
1175 : {
1176 0 : if (!mValueChanged ||
1177 0 : !mLastValueChangeWasInteractive ||
1178 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength)) {
1179 0 : return false;
1180 : }
1181 :
1182 0 : int32_t maxLength = -1;
1183 0 : GetMaxLength(&maxLength);
1184 :
1185 : // Maxlength of -1 means parsing error.
1186 0 : if (maxLength == -1) {
1187 0 : return false;
1188 : }
1189 :
1190 0 : int32_t textLength = -1;
1191 0 : GetTextLength(&textLength);
1192 :
1193 0 : return textLength > maxLength;
1194 : }
1195 :
1196 : bool
1197 0 : HTMLTextAreaElement::IsTooShort()
1198 : {
1199 0 : if (!mValueChanged ||
1200 0 : !mLastValueChangeWasInteractive ||
1201 0 : !HasAttr(kNameSpaceID_None, nsGkAtoms::minlength)) {
1202 0 : return false;
1203 : }
1204 :
1205 0 : int32_t minLength = -1;
1206 0 : GetMinLength(&minLength);
1207 :
1208 : // Minlength of -1 means parsing error.
1209 0 : if (minLength == -1) {
1210 0 : return false;
1211 : }
1212 :
1213 0 : int32_t textLength = -1;
1214 0 : GetTextLength(&textLength);
1215 :
1216 0 : return textLength && textLength < minLength;
1217 : }
1218 :
1219 : bool
1220 3 : HTMLTextAreaElement::IsValueMissing() const
1221 : {
1222 3 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
1223 3 : return false;
1224 : }
1225 :
1226 0 : return IsValueEmpty();
1227 : }
1228 :
1229 : void
1230 0 : HTMLTextAreaElement::UpdateTooLongValidityState()
1231 : {
1232 0 : SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
1233 0 : }
1234 :
1235 : void
1236 0 : HTMLTextAreaElement::UpdateTooShortValidityState()
1237 : {
1238 0 : SetValidityState(VALIDITY_STATE_TOO_SHORT, IsTooShort());
1239 0 : }
1240 :
1241 : void
1242 3 : HTMLTextAreaElement::UpdateValueMissingValidityState()
1243 : {
1244 3 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
1245 3 : }
1246 :
1247 : void
1248 3 : HTMLTextAreaElement::UpdateBarredFromConstraintValidation()
1249 : {
1250 6 : SetBarredFromConstraintValidation(HasAttr(kNameSpaceID_None,
1251 6 : nsGkAtoms::readonly) ||
1252 6 : IsDisabled());
1253 3 : }
1254 :
1255 : nsresult
1256 0 : HTMLTextAreaElement::GetValidationMessage(nsAString& aValidationMessage,
1257 : ValidityStateType aType)
1258 : {
1259 0 : nsresult rv = NS_OK;
1260 :
1261 0 : switch (aType)
1262 : {
1263 : case VALIDITY_STATE_TOO_LONG:
1264 : {
1265 0 : nsXPIDLString message;
1266 0 : int32_t maxLength = -1;
1267 0 : int32_t textLength = -1;
1268 0 : nsAutoString strMaxLength;
1269 0 : nsAutoString strTextLength;
1270 :
1271 0 : GetMaxLength(&maxLength);
1272 0 : GetTextLength(&textLength);
1273 :
1274 0 : strMaxLength.AppendInt(maxLength);
1275 0 : strTextLength.AppendInt(textLength);
1276 :
1277 0 : const char16_t* params[] = { strMaxLength.get(), strTextLength.get() };
1278 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1279 : "FormValidationTextTooLong",
1280 0 : params, message);
1281 0 : aValidationMessage = message;
1282 : }
1283 0 : break;
1284 : case VALIDITY_STATE_TOO_SHORT:
1285 : {
1286 0 : nsXPIDLString message;
1287 0 : int32_t minLength = -1;
1288 0 : int32_t textLength = -1;
1289 0 : nsAutoString strMinLength;
1290 0 : nsAutoString strTextLength;
1291 :
1292 0 : GetMinLength(&minLength);
1293 0 : GetTextLength(&textLength);
1294 :
1295 0 : strMinLength.AppendInt(minLength);
1296 0 : strTextLength.AppendInt(textLength);
1297 :
1298 0 : const char16_t* params[] = { strMinLength.get(), strTextLength.get() };
1299 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1300 : "FormValidationTextTooShort",
1301 0 : params, message);
1302 0 : aValidationMessage = message;
1303 : }
1304 0 : break;
1305 : case VALIDITY_STATE_VALUE_MISSING:
1306 : {
1307 0 : nsXPIDLString message;
1308 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1309 : "FormValidationValueMissing",
1310 0 : message);
1311 0 : aValidationMessage = message;
1312 : }
1313 0 : break;
1314 : default:
1315 0 : rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
1316 : }
1317 :
1318 0 : return rv;
1319 : }
1320 :
1321 : NS_IMETHODIMP_(bool)
1322 0 : HTMLTextAreaElement::IsSingleLineTextControl() const
1323 : {
1324 0 : return false;
1325 : }
1326 :
1327 : NS_IMETHODIMP_(bool)
1328 0 : HTMLTextAreaElement::IsTextArea() const
1329 : {
1330 0 : return true;
1331 : }
1332 :
1333 : NS_IMETHODIMP_(bool)
1334 0 : HTMLTextAreaElement::IsPlainTextControl() const
1335 : {
1336 : // need to check our HTML attribute and/or CSS.
1337 0 : return true;
1338 : }
1339 :
1340 : NS_IMETHODIMP_(bool)
1341 0 : HTMLTextAreaElement::IsPasswordTextControl() const
1342 : {
1343 0 : return false;
1344 : }
1345 :
1346 : NS_IMETHODIMP_(int32_t)
1347 0 : HTMLTextAreaElement::GetCols()
1348 : {
1349 0 : return Cols();
1350 : }
1351 :
1352 : NS_IMETHODIMP_(int32_t)
1353 0 : HTMLTextAreaElement::GetWrapCols()
1354 : {
1355 : // wrap=off means -1 for wrap width no matter what cols is
1356 : nsHTMLTextWrap wrapProp;
1357 0 : nsITextControlElement::GetWrapPropertyEnum(this, wrapProp);
1358 0 : if (wrapProp == nsITextControlElement::eHTMLTextWrap_Off) {
1359 : // do not wrap when wrap=off
1360 0 : return 0;
1361 : }
1362 :
1363 : // Otherwise we just wrap at the given number of columns
1364 0 : return GetCols();
1365 : }
1366 :
1367 :
1368 : NS_IMETHODIMP_(int32_t)
1369 0 : HTMLTextAreaElement::GetRows()
1370 : {
1371 0 : const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::rows);
1372 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
1373 0 : int32_t rows = attr->GetIntegerValue();
1374 0 : return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
1375 : }
1376 :
1377 0 : return DEFAULT_ROWS_TEXTAREA;
1378 : }
1379 :
1380 : NS_IMETHODIMP_(void)
1381 0 : HTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
1382 : {
1383 0 : GetDefaultValue(aValue);
1384 0 : }
1385 :
1386 : NS_IMETHODIMP_(bool)
1387 0 : HTMLTextAreaElement::ValueChanged() const
1388 : {
1389 0 : return mValueChanged;
1390 : }
1391 :
1392 : NS_IMETHODIMP_(void)
1393 0 : HTMLTextAreaElement::GetTextEditorValue(nsAString& aValue,
1394 : bool aIgnoreWrap) const
1395 : {
1396 0 : mState.GetValue(aValue, aIgnoreWrap);
1397 0 : }
1398 :
1399 : NS_IMETHODIMP_(void)
1400 0 : HTMLTextAreaElement::InitializeKeyboardEventListeners()
1401 : {
1402 0 : mState.InitializeKeyboardEventListeners();
1403 0 : }
1404 :
1405 : NS_IMETHODIMP_(void)
1406 0 : HTMLTextAreaElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
1407 : {
1408 0 : mLastValueChangeWasInteractive = aWasInteractiveUserChange;
1409 :
1410 : // Update the validity state
1411 0 : bool validBefore = IsValid();
1412 0 : UpdateTooLongValidityState();
1413 0 : UpdateTooShortValidityState();
1414 0 : UpdateValueMissingValidityState();
1415 :
1416 0 : if (validBefore != IsValid() ||
1417 0 : HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
1418 0 : UpdateState(aNotify);
1419 : }
1420 0 : }
1421 :
1422 : NS_IMETHODIMP_(bool)
1423 0 : HTMLTextAreaElement::HasCachedSelection()
1424 : {
1425 0 : return mState.IsSelectionCached();
1426 : }
1427 :
1428 : void
1429 0 : HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
1430 : {
1431 0 : UpdateValueMissingValidityState();
1432 0 : UpdateBarredFromConstraintValidation();
1433 :
1434 0 : nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
1435 0 : }
1436 :
1437 : JSObject*
1438 0 : HTMLTextAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
1439 : {
1440 0 : return HTMLTextAreaElementBinding::Wrap(aCx, this, aGivenProto);
1441 : }
1442 :
1443 : } // namespace dom
1444 : } // namespace mozilla
|