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