1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsIDOMHTMLTextAreaElement.h"
43 : #include "nsITextControlElement.h"
44 : #include "nsIDOMNSEditableElement.h"
45 : #include "nsIControllers.h"
46 : #include "nsFocusManager.h"
47 : #include "nsPIDOMWindow.h"
48 : #include "nsContentCID.h"
49 : #include "nsCOMPtr.h"
50 : #include "nsIComponentManager.h"
51 : #include "nsIDOMHTMLFormElement.h"
52 : #include "nsIFormControl.h"
53 : #include "nsIForm.h"
54 : #include "nsFormSubmission.h"
55 : #include "nsIDOMEventTarget.h"
56 : #include "nsGenericHTMLElement.h"
57 : #include "nsGkAtoms.h"
58 : #include "nsStyleConsts.h"
59 : #include "nsPresContext.h"
60 : #include "nsMappedAttributes.h"
61 : #include "nsIFormControlFrame.h"
62 : #include "nsITextControlFrame.h"
63 : #include "nsEventStates.h"
64 : #include "nsLinebreakConverter.h"
65 : #include "nsIDocument.h"
66 : #include "nsIFrame.h"
67 : #include "nsIFormControlFrame.h"
68 : #include "nsIPrivateDOMEvent.h"
69 : #include "nsGUIEvent.h"
70 : #include "nsLinebreakConverter.h"
71 : #include "nsPresState.h"
72 : #include "nsReadableUtils.h"
73 : #include "nsEventDispatcher.h"
74 : #include "nsLayoutUtils.h"
75 : #include "nsLayoutErrors.h"
76 : #include "nsStubMutationObserver.h"
77 : #include "nsDOMError.h"
78 : #include "mozAutoDocUpdate.h"
79 : #include "nsISupportsPrimitives.h"
80 : #include "nsContentCreatorFunctions.h"
81 : #include "nsIConstraintValidation.h"
82 : #include "nsHTMLFormElement.h"
83 :
84 : #include "nsTextEditorState.h"
85 :
86 : using namespace mozilla;
87 : using namespace mozilla::dom;
88 :
89 : static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
90 :
91 : #define NS_NO_CONTENT_DISPATCH (1 << 0)
92 :
93 : class nsHTMLTextAreaElement : public nsGenericHTMLFormElement,
94 : public nsIDOMHTMLTextAreaElement,
95 : public nsITextControlElement,
96 : public nsIDOMNSEditableElement,
97 : public nsStubMutationObserver,
98 : public nsIConstraintValidation
99 0 : {
100 : public:
101 : using nsIConstraintValidation::GetValidationMessage;
102 :
103 : nsHTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
104 : mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
105 :
106 : // nsISupports
107 : NS_DECL_ISUPPORTS_INHERITED
108 :
109 : // nsIDOMNode
110 0 : NS_FORWARD_NSIDOMNODE(nsGenericHTMLFormElement::)
111 :
112 : // nsIDOMElement
113 0 : NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
114 :
115 : // nsIDOMHTMLElement
116 0 : NS_FORWARD_NSIDOMHTMLELEMENT_BASIC(nsGenericHTMLFormElement::)
117 0 : NS_SCRIPTABLE NS_IMETHOD Click() {
118 0 : return nsGenericHTMLFormElement::Click();
119 : }
120 : NS_SCRIPTABLE NS_IMETHOD GetTabIndex(PRInt32* aTabIndex);
121 : NS_SCRIPTABLE NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
122 0 : NS_SCRIPTABLE NS_IMETHOD Focus() {
123 0 : return nsGenericHTMLFormElement::Focus();
124 : }
125 0 : NS_SCRIPTABLE NS_IMETHOD GetDraggable(bool* aDraggable) {
126 0 : return nsGenericHTMLFormElement::GetDraggable(aDraggable);
127 : }
128 0 : NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML) {
129 0 : return nsGenericHTMLFormElement::GetInnerHTML(aInnerHTML);
130 : }
131 0 : NS_SCRIPTABLE NS_IMETHOD SetInnerHTML(const nsAString& aInnerHTML) {
132 0 : return nsGenericHTMLFormElement::SetInnerHTML(aInnerHTML);
133 : }
134 :
135 : // nsIDOMHTMLTextAreaElement
136 : NS_DECL_NSIDOMHTMLTEXTAREAELEMENT
137 :
138 : // nsIDOMNSEditableElement
139 0 : NS_IMETHOD GetEditor(nsIEditor** aEditor)
140 : {
141 0 : return nsGenericHTMLElement::GetEditor(aEditor);
142 : }
143 : NS_IMETHOD SetUserInput(const nsAString& aInput);
144 :
145 : // nsIFormControl
146 0 : NS_IMETHOD_(PRUint32) GetType() const { return NS_FORM_TEXTAREA; }
147 : NS_IMETHOD Reset();
148 : NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission);
149 : NS_IMETHOD SaveState();
150 : virtual bool RestoreState(nsPresState* aState);
151 :
152 : virtual void FieldSetDisabledChanged(bool aNotify);
153 :
154 : virtual nsEventStates IntrinsicState() const;
155 :
156 : // nsITextControlElemet
157 : NS_IMETHOD SetValueChanged(bool aValueChanged);
158 : NS_IMETHOD_(bool) IsSingleLineTextControl() const;
159 : NS_IMETHOD_(bool) IsTextArea() const;
160 : NS_IMETHOD_(bool) IsPlainTextControl() const;
161 : NS_IMETHOD_(bool) IsPasswordTextControl() const;
162 : NS_IMETHOD_(PRInt32) GetCols();
163 : NS_IMETHOD_(PRInt32) GetWrapCols();
164 : NS_IMETHOD_(PRInt32) GetRows();
165 : NS_IMETHOD_(void) GetDefaultValueFromContent(nsAString& aValue);
166 : NS_IMETHOD_(bool) ValueChanged() const;
167 : NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const;
168 : NS_IMETHOD_(void) SetTextEditorValue(const nsAString& aValue, bool aUserInput);
169 : NS_IMETHOD_(nsIEditor*) GetTextEditor();
170 : NS_IMETHOD_(nsISelectionController*) GetSelectionController();
171 : NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection();
172 : NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame);
173 : NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame);
174 : NS_IMETHOD CreateEditor();
175 : NS_IMETHOD_(nsIContent*) GetRootEditorNode();
176 : NS_IMETHOD_(nsIContent*) CreatePlaceholderNode();
177 : NS_IMETHOD_(nsIContent*) GetPlaceholderNode();
178 : NS_IMETHOD_(void) UpdatePlaceholderText(bool aNotify);
179 : NS_IMETHOD_(void) SetPlaceholderClass(bool aVisible, bool aNotify);
180 : NS_IMETHOD_(void) InitializeKeyboardEventListeners();
181 : NS_IMETHOD_(void) OnValueChanged(bool aNotify);
182 : NS_IMETHOD_(bool) HasCachedSelection();
183 :
184 : // nsIContent
185 : virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
186 : nsIContent* aBindingParent,
187 : bool aCompileEventHandlers);
188 : virtual void UnbindFromTree(bool aDeep = true,
189 : bool aNullParent = true);
190 : virtual bool ParseAttribute(PRInt32 aNamespaceID,
191 : nsIAtom* aAttribute,
192 : const nsAString& aValue,
193 : nsAttrValue& aResult);
194 : virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
195 : virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
196 : PRInt32 aModType) const;
197 : NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
198 :
199 : virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
200 : virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
201 :
202 : virtual bool IsHTMLFocusable(bool aWithMouse, bool *aIsFocusable, PRInt32 *aTabIndex);
203 :
204 : virtual void DoneAddingChildren(bool aHaveNotified);
205 : virtual bool IsDoneAddingChildren();
206 :
207 : virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
208 :
209 : nsresult CopyInnerTo(nsGenericElement* aDest) const;
210 :
211 : /**
212 : * Called when an attribute is about to be changed
213 : */
214 : virtual nsresult BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
215 : const nsAttrValueOrString* aValue,
216 : bool aNotify);
217 :
218 : // nsIMutationObserver
219 : NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
220 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
221 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
222 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
223 :
224 0 : virtual void UpdateEditableState(bool aNotify)
225 : {
226 0 : return UpdateEditableFormControlState(aNotify);
227 : }
228 :
229 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLTextAreaElement,
230 : nsGenericHTMLFormElement)
231 :
232 : virtual nsXPCClassInfo* GetClassInfo();
233 :
234 : // nsIConstraintValidation
235 : bool IsTooLong();
236 : bool IsValueMissing() const;
237 : void UpdateTooLongValidityState();
238 : void UpdateValueMissingValidityState();
239 : void UpdateBarredFromConstraintValidation();
240 : nsresult GetValidationMessage(nsAString& aValidationMessage,
241 : ValidityStateType aType);
242 :
243 : protected:
244 : using nsGenericHTMLFormElement::IsSingleLineTextControl; // get rid of the compiler warning
245 :
246 : nsCOMPtr<nsIControllers> mControllers;
247 : /** Whether or not the value has changed since its default value was given. */
248 : bool mValueChanged;
249 : /** Whether or not we are already handling select event. */
250 : bool mHandlingSelect;
251 : /** Whether or not we are done adding children (always true if not
252 : created by a parser */
253 : bool mDoneAddingChildren;
254 : /** Whether state restoration should be inhibited in DoneAddingChildren. */
255 : bool mInhibitStateRestoration;
256 : /** Whether our disabled state has changed from the default **/
257 : bool mDisabledChanged;
258 : /** Whether we should make :-moz-ui-invalid apply on the element. **/
259 : bool mCanShowInvalidUI;
260 : /** Whether we should make :-moz-ui-valid apply on the element. **/
261 : bool mCanShowValidUI;
262 :
263 : /** The state of the text editor (selection controller and the editor) **/
264 : nsRefPtr<nsTextEditorState> mState;
265 :
266 : NS_IMETHOD SelectAll(nsPresContext* aPresContext);
267 : /**
268 : * Get the value, whether it is from the content or the frame.
269 : * @param aValue the value [out]
270 : * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
271 : * value. If this is true, linebreaks will not be inserted even if
272 : * wrap=hard.
273 : */
274 : void GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const;
275 :
276 : nsresult SetValueInternal(const nsAString& aValue,
277 : bool aUserInput);
278 : nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
279 :
280 : /**
281 : * Common method to call from the various mutation observer methods.
282 : * aContent is a content node that's either the one that changed or its
283 : * parent; we should only respond to the change if aContent is non-anonymous.
284 : */
285 : void ContentChanged(nsIContent* aContent);
286 :
287 : virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
288 : const nsAttrValue* aValue, bool aNotify);
289 :
290 : /**
291 : * Return if an element should have a specific validity UI
292 : * (with :-moz-ui-invalid and :-moz-ui-valid pseudo-classes).
293 : *
294 : * @return Whether the elemnet should have a validity UI.
295 : */
296 0 : bool ShouldShowValidityUI() const {
297 : /**
298 : * Always show the validity UI if the form has already tried to be submitted
299 : * but was invalid.
300 : *
301 : * Otherwise, show the validity UI if the element's value has been changed.
302 : */
303 :
304 0 : if (mForm && mForm->HasEverTriedInvalidSubmit()) {
305 0 : return true;
306 : }
307 :
308 0 : return mValueChanged;
309 : }
310 :
311 : /**
312 : * Get the mutable state of the element.
313 : */
314 : bool IsMutable() const;
315 :
316 : /**
317 : * Returns whether the current value is the empty string.
318 : *
319 : * @return whether the current value is the empty string.
320 : */
321 : bool IsValueEmpty() const;
322 : };
323 :
324 :
325 0 : NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
326 :
327 :
328 0 : nsHTMLTextAreaElement::nsHTMLTextAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
329 : FromParser aFromParser)
330 : : nsGenericHTMLFormElement(aNodeInfo),
331 : mValueChanged(false),
332 : mHandlingSelect(false),
333 0 : mDoneAddingChildren(!aFromParser),
334 0 : mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
335 : mDisabledChanged(false),
336 : mCanShowInvalidUI(true),
337 : mCanShowValidUI(true),
338 0 : mState(new nsTextEditorState(this))
339 : {
340 0 : AddMutationObserver(this);
341 :
342 : // Set up our default state. By default we're enabled (since we're
343 : // a control type that can be disabled but not actually disabled
344 : // right now), optional, and valid. We are NOT readwrite by default
345 : // until someone calls UpdateEditableState on us, apparently! Also
346 : // by default we don't have to show validity UI and so forth.
347 : AddStatesSilently(NS_EVENT_STATE_ENABLED |
348 : NS_EVENT_STATE_OPTIONAL |
349 0 : NS_EVENT_STATE_VALID);
350 0 : }
351 :
352 :
353 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLTextAreaElement)
354 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLTextAreaElement,
355 : nsGenericHTMLFormElement)
356 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers)
357 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
358 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLTextAreaElement,
359 : nsGenericHTMLFormElement)
360 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers)
361 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mState, nsTextEditorState)
362 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
363 :
364 0 : NS_IMPL_ADDREF_INHERITED(nsHTMLTextAreaElement, nsGenericElement)
365 0 : NS_IMPL_RELEASE_INHERITED(nsHTMLTextAreaElement, nsGenericElement)
366 :
367 :
368 0 : DOMCI_NODE_DATA(HTMLTextAreaElement, nsHTMLTextAreaElement)
369 :
370 : // QueryInterface implementation for nsHTMLTextAreaElement
371 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLTextAreaElement)
372 0 : NS_HTML_CONTENT_INTERFACE_TABLE5(nsHTMLTextAreaElement,
373 : nsIDOMHTMLTextAreaElement,
374 : nsITextControlElement,
375 : nsIDOMNSEditableElement,
376 : nsIMutationObserver,
377 : nsIConstraintValidation)
378 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLTextAreaElement,
379 : nsGenericHTMLFormElement)
380 0 : NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLTextAreaElement)
381 :
382 :
383 : // nsIDOMHTMLTextAreaElement
384 :
385 :
386 0 : NS_IMPL_ELEMENT_CLONE(nsHTMLTextAreaElement)
387 :
388 : // nsIConstraintValidation
389 0 : NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLTextAreaElement)
390 :
391 :
392 : NS_IMETHODIMP
393 0 : nsHTMLTextAreaElement::GetForm(nsIDOMHTMLFormElement** aForm)
394 : {
395 0 : return nsGenericHTMLFormElement::GetForm(aForm);
396 : }
397 :
398 :
399 : // nsIContent
400 :
401 : NS_IMETHODIMP
402 0 : nsHTMLTextAreaElement::Select()
403 : {
404 : // XXX Bug? We have to give the input focus before contents can be
405 : // selected
406 :
407 0 : FocusTristate state = FocusState();
408 0 : if (state == eUnfocusable) {
409 0 : return NS_OK;
410 : }
411 :
412 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
413 :
414 0 : nsRefPtr<nsPresContext> presContext = GetPresContext();
415 0 : if (state == eInactiveWindow) {
416 0 : if (fm)
417 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
418 0 : SelectAll(presContext);
419 0 : return NS_OK;
420 : }
421 :
422 0 : nsEventStatus status = nsEventStatus_eIgnore;
423 0 : nsGUIEvent event(true, NS_FORM_SELECTED, nsnull);
424 : // XXXbz nsHTMLInputElement guards against this reentering; shouldn't we?
425 : nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
426 0 : &event, nsnull, &status);
427 :
428 : // If the DOM event was not canceled (e.g. by a JS event handler
429 : // returning false)
430 0 : if (status == nsEventStatus_eIgnore) {
431 0 : if (fm) {
432 0 : fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
433 :
434 : // ensure that the element is actually focused
435 0 : nsCOMPtr<nsIDOMElement> focusedElement;
436 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
437 0 : if (SameCOMIdentity(static_cast<nsIDOMNode*>(this), focusedElement)) {
438 : // Now Select all the text!
439 0 : SelectAll(presContext);
440 : }
441 : }
442 : }
443 :
444 0 : return NS_OK;
445 : }
446 :
447 : NS_IMETHODIMP
448 0 : nsHTMLTextAreaElement::SelectAll(nsPresContext* aPresContext)
449 : {
450 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
451 :
452 0 : if (formControlFrame) {
453 0 : formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
454 : }
455 :
456 0 : return NS_OK;
457 : }
458 :
459 : bool
460 0 : nsHTMLTextAreaElement::IsHTMLFocusable(bool aWithMouse,
461 : bool *aIsFocusable, PRInt32 *aTabIndex)
462 : {
463 0 : if (nsGenericHTMLFormElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
464 0 : return true;
465 : }
466 :
467 : // disabled textareas are not focusable
468 0 : *aIsFocusable = !IsDisabled();
469 0 : return false;
470 : }
471 :
472 0 : NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Autofocus, autofocus)
473 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(nsHTMLTextAreaElement, Cols, cols, DEFAULT_COLS)
474 0 : NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Disabled, disabled)
475 0 : NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLTextAreaElement, MaxLength, maxlength)
476 0 : NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Name, name)
477 0 : NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, ReadOnly, readonly)
478 0 : NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Required, required)
479 0 : NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(nsHTMLTextAreaElement, Rows, rows, DEFAULT_ROWS_TEXTAREA)
480 0 : NS_IMPL_INT_ATTR(nsHTMLTextAreaElement, TabIndex, tabindex)
481 0 : NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Wrap, wrap)
482 0 : NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Placeholder, placeholder)
483 :
484 :
485 : NS_IMETHODIMP
486 0 : nsHTMLTextAreaElement::GetType(nsAString& aType)
487 : {
488 0 : aType.AssignLiteral("textarea");
489 :
490 0 : return NS_OK;
491 : }
492 :
493 : NS_IMETHODIMP
494 0 : nsHTMLTextAreaElement::GetValue(nsAString& aValue)
495 : {
496 0 : GetValueInternal(aValue, true);
497 0 : return NS_OK;
498 : }
499 :
500 : void
501 0 : nsHTMLTextAreaElement::GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const
502 : {
503 0 : mState->GetValue(aValue, aIgnoreWrap);
504 0 : }
505 :
506 : NS_IMETHODIMP_(nsIEditor*)
507 0 : nsHTMLTextAreaElement::GetTextEditor()
508 : {
509 0 : return mState->GetEditor();
510 : }
511 :
512 : NS_IMETHODIMP_(nsISelectionController*)
513 0 : nsHTMLTextAreaElement::GetSelectionController()
514 : {
515 0 : return mState->GetSelectionController();
516 : }
517 :
518 : NS_IMETHODIMP_(nsFrameSelection*)
519 0 : nsHTMLTextAreaElement::GetConstFrameSelection()
520 : {
521 0 : return mState->GetConstFrameSelection();
522 : }
523 :
524 : NS_IMETHODIMP
525 0 : nsHTMLTextAreaElement::BindToFrame(nsTextControlFrame* aFrame)
526 : {
527 0 : return mState->BindToFrame(aFrame);
528 : }
529 :
530 : NS_IMETHODIMP_(void)
531 0 : nsHTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame* aFrame)
532 : {
533 0 : if (aFrame) {
534 0 : mState->UnbindFromFrame(aFrame);
535 : }
536 0 : }
537 :
538 : NS_IMETHODIMP
539 0 : nsHTMLTextAreaElement::CreateEditor()
540 : {
541 0 : return mState->PrepareEditor();
542 : }
543 :
544 : NS_IMETHODIMP_(nsIContent*)
545 0 : nsHTMLTextAreaElement::GetRootEditorNode()
546 : {
547 0 : return mState->GetRootNode();
548 : }
549 :
550 : NS_IMETHODIMP_(nsIContent*)
551 0 : nsHTMLTextAreaElement::CreatePlaceholderNode()
552 : {
553 0 : NS_ENSURE_SUCCESS(mState->CreatePlaceholderNode(), nsnull);
554 0 : return mState->GetPlaceholderNode();
555 : }
556 :
557 : NS_IMETHODIMP_(nsIContent*)
558 0 : nsHTMLTextAreaElement::GetPlaceholderNode()
559 : {
560 0 : return mState->GetPlaceholderNode();
561 : }
562 :
563 : NS_IMETHODIMP_(void)
564 0 : nsHTMLTextAreaElement::UpdatePlaceholderText(bool aNotify)
565 : {
566 0 : mState->UpdatePlaceholderText(aNotify);
567 0 : }
568 :
569 : NS_IMETHODIMP_(void)
570 0 : nsHTMLTextAreaElement::SetPlaceholderClass(bool aVisible, bool aNotify)
571 : {
572 0 : mState->SetPlaceholderClass(aVisible, aNotify);
573 0 : }
574 :
575 : nsresult
576 0 : nsHTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
577 : bool aUserInput)
578 : {
579 : // Need to set the value changed flag here, so that
580 : // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
581 : // if needed.
582 0 : SetValueChanged(true);
583 0 : mState->SetValue(aValue, aUserInput);
584 :
585 0 : return NS_OK;
586 : }
587 :
588 : NS_IMETHODIMP
589 0 : nsHTMLTextAreaElement::SetValue(const nsAString& aValue)
590 : {
591 0 : return SetValueInternal(aValue, false);
592 : }
593 :
594 : NS_IMETHODIMP
595 0 : nsHTMLTextAreaElement::SetUserInput(const nsAString& aValue)
596 : {
597 0 : if (!nsContentUtils::IsCallerTrustedForWrite()) {
598 0 : return NS_ERROR_DOM_SECURITY_ERR;
599 : }
600 0 : SetValueInternal(aValue, true);
601 0 : return NS_OK;
602 : }
603 :
604 : NS_IMETHODIMP
605 0 : nsHTMLTextAreaElement::SetValueChanged(bool aValueChanged)
606 : {
607 0 : bool previousValue = mValueChanged;
608 :
609 0 : mValueChanged = aValueChanged;
610 0 : if (!aValueChanged && !mState->IsEmpty()) {
611 0 : mState->EmptyValue();
612 : }
613 :
614 0 : if (mValueChanged != previousValue) {
615 0 : UpdateState(true);
616 : }
617 :
618 0 : return NS_OK;
619 : }
620 :
621 : NS_IMETHODIMP
622 0 : nsHTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
623 : {
624 0 : nsContentUtils::GetNodeTextContent(this, false, aDefaultValue);
625 0 : return NS_OK;
626 : }
627 :
628 : NS_IMETHODIMP
629 0 : nsHTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue)
630 : {
631 0 : nsresult rv = nsContentUtils::SetNodeTextContent(this, aDefaultValue, true);
632 0 : if (NS_SUCCEEDED(rv) && !mValueChanged) {
633 0 : Reset();
634 : }
635 0 : return rv;
636 : }
637 :
638 : bool
639 0 : nsHTMLTextAreaElement::ParseAttribute(PRInt32 aNamespaceID,
640 : nsIAtom* aAttribute,
641 : const nsAString& aValue,
642 : nsAttrValue& aResult)
643 : {
644 0 : if (aNamespaceID == kNameSpaceID_None) {
645 0 : if (aAttribute == nsGkAtoms::maxlength) {
646 0 : return aResult.ParseNonNegativeIntValue(aValue);
647 0 : } else if (aAttribute == nsGkAtoms::cols ||
648 : aAttribute == nsGkAtoms::rows) {
649 0 : return aResult.ParsePositiveIntValue(aValue);
650 : }
651 : }
652 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
653 0 : aResult);
654 : }
655 :
656 : static void
657 0 : MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
658 : nsRuleData* aData)
659 : {
660 0 : nsGenericHTMLFormElement::MapDivAlignAttributeInto(aAttributes, aData);
661 0 : nsGenericHTMLFormElement::MapCommonAttributesInto(aAttributes, aData);
662 0 : }
663 :
664 : nsChangeHint
665 0 : nsHTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
666 : PRInt32 aModType) const
667 : {
668 : nsChangeHint retval =
669 0 : nsGenericHTMLFormElement::GetAttributeChangeHint(aAttribute, aModType);
670 0 : if (aAttribute == nsGkAtoms::rows ||
671 : aAttribute == nsGkAtoms::cols) {
672 0 : NS_UpdateHint(retval, NS_STYLE_HINT_REFLOW);
673 0 : } else if (aAttribute == nsGkAtoms::wrap) {
674 0 : NS_UpdateHint(retval, nsChangeHint_ReconstructFrame);
675 0 : } else if (aAttribute == nsGkAtoms::placeholder) {
676 0 : NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
677 : }
678 0 : return retval;
679 : }
680 :
681 : NS_IMETHODIMP_(bool)
682 0 : nsHTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
683 : {
684 : static const MappedAttributeEntry* const map[] = {
685 : sDivAlignAttributeMap,
686 : sCommonAttributeMap,
687 : };
688 :
689 0 : return FindAttributeDependence(aAttribute, map);
690 : }
691 :
692 : nsMapRuleToAttributesFunc
693 0 : nsHTMLTextAreaElement::GetAttributeMappingFunction() const
694 : {
695 0 : return &MapAttributesIntoRule;
696 : }
697 :
698 : nsresult
699 0 : nsHTMLTextAreaElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
700 : {
701 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
702 0 : nsIFrame* formFrame = NULL;
703 0 : if (formControlFrame) {
704 0 : formFrame = do_QueryFrame(formControlFrame);
705 : }
706 :
707 0 : aVisitor.mCanHandle = false;
708 0 : if (IsElementDisabledForEvents(aVisitor.mEvent->message, formFrame)) {
709 0 : return NS_OK;
710 : }
711 :
712 : // Don't dispatch a second select event if we are already handling
713 : // one.
714 0 : if (aVisitor.mEvent->message == NS_FORM_SELECTED) {
715 0 : if (mHandlingSelect) {
716 0 : return NS_OK;
717 : }
718 0 : mHandlingSelect = true;
719 : }
720 :
721 : // If NS_EVENT_FLAG_NO_CONTENT_DISPATCH is set we will not allow content to handle
722 : // this event. But to allow middle mouse button paste to work we must allow
723 : // middle clicks to go to text fields anyway.
724 0 : if (aVisitor.mEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH)
725 0 : aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
726 0 : if (aVisitor.mEvent->message == NS_MOUSE_CLICK &&
727 : aVisitor.mEvent->eventStructType == NS_MOUSE_EVENT &&
728 : static_cast<nsMouseEvent*>(aVisitor.mEvent)->button ==
729 : nsMouseEvent::eMiddleButton) {
730 0 : aVisitor.mEvent->flags &= ~NS_EVENT_FLAG_NO_CONTENT_DISPATCH;
731 : }
732 :
733 : // Fire onchange (if necessary), before we do the blur, bug 370521.
734 0 : if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
735 0 : nsIFrame* primaryFrame = GetPrimaryFrame();
736 0 : if (primaryFrame) {
737 0 : nsITextControlFrame* textFrame = do_QueryFrame(primaryFrame);
738 0 : if (textFrame) {
739 0 : textFrame->CheckFireOnChange();
740 : }
741 : }
742 : }
743 :
744 0 : return nsGenericHTMLFormElement::PreHandleEvent(aVisitor);
745 : }
746 :
747 : nsresult
748 0 : nsHTMLTextAreaElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
749 : {
750 0 : if (aVisitor.mEvent->message == NS_FORM_SELECTED) {
751 0 : mHandlingSelect = false;
752 : }
753 :
754 0 : if (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
755 : aVisitor.mEvent->message == NS_BLUR_CONTENT) {
756 0 : if (aVisitor.mEvent->message == NS_FOCUS_CONTENT) {
757 : // If the invalid UI is shown, we should show it while focusing (and
758 : // update). Otherwise, we should not.
759 0 : mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
760 :
761 : // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
762 : // UI while typing.
763 0 : mCanShowValidUI = ShouldShowValidityUI();
764 : } else { // NS_BLUR_CONTENT
765 0 : mCanShowInvalidUI = true;
766 0 : mCanShowValidUI = true;
767 : }
768 :
769 0 : UpdateState(true);
770 : }
771 :
772 : // Reset the flag for other content besides this text field
773 : aVisitor.mEvent->flags |= (aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH)
774 0 : ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
775 :
776 0 : return NS_OK;
777 : }
778 :
779 : void
780 0 : nsHTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified)
781 : {
782 0 : if (!mValueChanged) {
783 0 : if (!mDoneAddingChildren) {
784 : // Reset now that we're done adding children if the content sink tried to
785 : // sneak some text in without calling AppendChildTo.
786 0 : Reset();
787 : }
788 0 : if (!mInhibitStateRestoration) {
789 0 : RestoreFormControlState(this, this);
790 : }
791 : }
792 :
793 0 : mDoneAddingChildren = true;
794 0 : }
795 :
796 : bool
797 0 : nsHTMLTextAreaElement::IsDoneAddingChildren()
798 : {
799 0 : return mDoneAddingChildren;
800 : }
801 :
802 : // Controllers Methods
803 :
804 : NS_IMETHODIMP
805 0 : nsHTMLTextAreaElement::GetControllers(nsIControllers** aResult)
806 : {
807 0 : NS_ENSURE_ARG_POINTER(aResult);
808 :
809 0 : if (!mControllers)
810 : {
811 : nsresult rv;
812 0 : mControllers = do_CreateInstance(kXULControllersCID, &rv);
813 0 : NS_ENSURE_SUCCESS(rv, rv);
814 :
815 0 : nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/editor/editorcontroller;1", &rv);
816 0 : if (NS_FAILED(rv))
817 0 : return rv;
818 :
819 0 : mControllers->AppendController(controller);
820 :
821 0 : controller = do_CreateInstance("@mozilla.org/editor/editingcontroller;1", &rv);
822 0 : if (NS_FAILED(rv))
823 0 : return rv;
824 :
825 0 : mControllers->AppendController(controller);
826 : }
827 :
828 0 : *aResult = mControllers;
829 0 : NS_IF_ADDREF(*aResult);
830 :
831 0 : return NS_OK;
832 : }
833 :
834 : NS_IMETHODIMP
835 0 : nsHTMLTextAreaElement::GetTextLength(PRInt32 *aTextLength)
836 : {
837 0 : NS_ENSURE_ARG_POINTER(aTextLength);
838 0 : nsAutoString val;
839 0 : nsresult rv = GetValue(val);
840 0 : *aTextLength = val.Length();
841 :
842 0 : return rv;
843 : }
844 :
845 : NS_IMETHODIMP
846 0 : nsHTMLTextAreaElement::GetSelectionStart(PRInt32 *aSelectionStart)
847 : {
848 0 : NS_ENSURE_ARG_POINTER(aSelectionStart);
849 :
850 : PRInt32 selEnd;
851 0 : nsresult rv = GetSelectionRange(aSelectionStart, &selEnd);
852 :
853 0 : if (NS_FAILED(rv) && mState->IsSelectionCached()) {
854 0 : *aSelectionStart = mState->GetSelectionProperties().mStart;
855 0 : return NS_OK;
856 : }
857 0 : return rv;
858 : }
859 :
860 : NS_IMETHODIMP
861 0 : nsHTMLTextAreaElement::SetSelectionStart(PRInt32 aSelectionStart)
862 : {
863 0 : if (mState->IsSelectionCached()) {
864 0 : mState->GetSelectionProperties().mStart = aSelectionStart;
865 0 : return NS_OK;
866 : }
867 :
868 0 : nsAutoString direction;
869 0 : nsresult rv = GetSelectionDirection(direction);
870 0 : NS_ENSURE_SUCCESS(rv, rv);
871 : PRInt32 start, end;
872 0 : rv = GetSelectionRange(&start, &end);
873 0 : NS_ENSURE_SUCCESS(rv, rv);
874 0 : start = aSelectionStart;
875 0 : if (end < start) {
876 0 : end = start;
877 : }
878 0 : return SetSelectionRange(start, end, direction);
879 : }
880 :
881 : NS_IMETHODIMP
882 0 : nsHTMLTextAreaElement::GetSelectionEnd(PRInt32 *aSelectionEnd)
883 : {
884 0 : NS_ENSURE_ARG_POINTER(aSelectionEnd);
885 :
886 : PRInt32 selStart;
887 0 : nsresult rv = GetSelectionRange(&selStart, aSelectionEnd);
888 :
889 0 : if (NS_FAILED(rv) && mState->IsSelectionCached()) {
890 0 : *aSelectionEnd = mState->GetSelectionProperties().mEnd;
891 0 : return NS_OK;
892 : }
893 0 : return rv;
894 : }
895 :
896 : NS_IMETHODIMP
897 0 : nsHTMLTextAreaElement::SetSelectionEnd(PRInt32 aSelectionEnd)
898 : {
899 0 : if (mState->IsSelectionCached()) {
900 0 : mState->GetSelectionProperties().mEnd = aSelectionEnd;
901 0 : return NS_OK;
902 : }
903 :
904 0 : nsAutoString direction;
905 0 : nsresult rv = GetSelectionDirection(direction);
906 0 : NS_ENSURE_SUCCESS(rv, rv);
907 : PRInt32 start, end;
908 0 : rv = GetSelectionRange(&start, &end);
909 0 : NS_ENSURE_SUCCESS(rv, rv);
910 0 : end = aSelectionEnd;
911 0 : if (start > end) {
912 0 : start = end;
913 : }
914 0 : return SetSelectionRange(start, end, direction);
915 : }
916 :
917 : nsresult
918 0 : nsHTMLTextAreaElement::GetSelectionRange(PRInt32* aSelectionStart,
919 : PRInt32* aSelectionEnd)
920 : {
921 0 : nsresult rv = NS_ERROR_FAILURE;
922 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
923 :
924 0 : if (formControlFrame) {
925 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
926 0 : if (textControlFrame)
927 0 : rv = textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
928 : }
929 :
930 0 : return rv;
931 : }
932 :
933 : static void
934 0 : DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
935 : {
936 0 : if (dir == nsITextControlFrame::eNone) {
937 0 : aDirection.AssignLiteral("none");
938 0 : } else if (dir == nsITextControlFrame::eForward) {
939 0 : aDirection.AssignLiteral("forward");
940 0 : } else if (dir == nsITextControlFrame::eBackward) {
941 0 : aDirection.AssignLiteral("backward");
942 : } else {
943 0 : NS_NOTREACHED("Invalid SelectionDirection value");
944 : }
945 0 : }
946 :
947 : nsresult
948 0 : nsHTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection)
949 : {
950 0 : nsresult rv = NS_ERROR_FAILURE;
951 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
952 :
953 0 : if (formControlFrame) {
954 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
955 0 : if (textControlFrame) {
956 : nsITextControlFrame::SelectionDirection dir;
957 0 : rv = textControlFrame->GetSelectionRange(nsnull, nsnull, &dir);
958 0 : if (NS_SUCCEEDED(rv)) {
959 0 : DirectionToName(dir, aDirection);
960 : }
961 : }
962 : }
963 :
964 0 : if (NS_FAILED(rv)) {
965 0 : if (mState->IsSelectionCached()) {
966 0 : DirectionToName(mState->GetSelectionProperties().mDirection, aDirection);
967 0 : return NS_OK;
968 : }
969 : }
970 :
971 0 : return rv;
972 : }
973 :
974 : NS_IMETHODIMP
975 0 : nsHTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection) {
976 0 : if (mState->IsSelectionCached()) {
977 0 : nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
978 0 : if (aDirection.EqualsLiteral("forward")) {
979 0 : dir = nsITextControlFrame::eForward;
980 0 : } else if (aDirection.EqualsLiteral("backward")) {
981 0 : dir = nsITextControlFrame::eBackward;
982 : }
983 0 : mState->GetSelectionProperties().mDirection = dir;
984 0 : return NS_OK;
985 : }
986 :
987 : PRInt32 start, end;
988 0 : nsresult rv = GetSelectionRange(&start, &end);
989 0 : if (NS_SUCCEEDED(rv)) {
990 0 : rv = SetSelectionRange(start, end, aDirection);
991 : }
992 :
993 0 : return rv;
994 : }
995 :
996 : NS_IMETHODIMP
997 0 : nsHTMLTextAreaElement::SetSelectionRange(PRInt32 aSelectionStart,
998 : PRInt32 aSelectionEnd,
999 : const nsAString& aDirection)
1000 : {
1001 0 : nsresult rv = NS_ERROR_FAILURE;
1002 0 : nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
1003 :
1004 0 : if (formControlFrame) {
1005 0 : nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
1006 0 : if (textControlFrame) {
1007 : // Default to forward, even if not specified.
1008 : // Note that we don't currently support directionless selections, so
1009 : // "none" is treated like "forward".
1010 0 : nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
1011 0 : if (aDirection.EqualsLiteral("backward")) {
1012 0 : dir = nsITextControlFrame::eBackward;
1013 : }
1014 :
1015 0 : rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
1016 0 : if (NS_SUCCEEDED(rv)) {
1017 0 : rv = textControlFrame->ScrollSelectionIntoView();
1018 : }
1019 : }
1020 : }
1021 :
1022 0 : return rv;
1023 : }
1024 :
1025 : nsresult
1026 0 : nsHTMLTextAreaElement::Reset()
1027 : {
1028 : nsresult rv;
1029 :
1030 : // To get the initial spellchecking, reset value to
1031 : // empty string before setting the default value.
1032 0 : SetValue(EmptyString());
1033 0 : nsAutoString resetVal;
1034 0 : GetDefaultValue(resetVal);
1035 0 : rv = SetValue(resetVal);
1036 0 : NS_ENSURE_SUCCESS(rv, rv);
1037 :
1038 0 : SetValueChanged(false);
1039 0 : return NS_OK;
1040 : }
1041 :
1042 : NS_IMETHODIMP
1043 0 : nsHTMLTextAreaElement::SubmitNamesValues(nsFormSubmission* aFormSubmission)
1044 : {
1045 : // Disabled elements don't submit
1046 0 : if (IsDisabled()) {
1047 0 : return NS_OK;
1048 : }
1049 :
1050 : //
1051 : // Get the name (if no name, no submit)
1052 : //
1053 0 : nsAutoString name;
1054 0 : GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
1055 0 : if (name.IsEmpty()) {
1056 0 : return NS_OK;
1057 : }
1058 :
1059 : //
1060 : // Get the value
1061 : //
1062 0 : nsAutoString value;
1063 0 : GetValueInternal(value, false);
1064 :
1065 : //
1066 : // Submit
1067 : //
1068 0 : return aFormSubmission->AddNameValuePair(name, value);
1069 : }
1070 :
1071 : NS_IMETHODIMP
1072 0 : nsHTMLTextAreaElement::SaveState()
1073 : {
1074 0 : nsresult rv = NS_OK;
1075 :
1076 : // Only save if value != defaultValue (bug 62713)
1077 0 : nsPresState *state = nsnull;
1078 0 : if (mValueChanged) {
1079 0 : rv = GetPrimaryPresState(this, &state);
1080 0 : if (state) {
1081 0 : nsAutoString value;
1082 0 : GetValueInternal(value, true);
1083 :
1084 : rv = nsLinebreakConverter::ConvertStringLineBreaks(
1085 : value,
1086 : nsLinebreakConverter::eLinebreakPlatform,
1087 0 : nsLinebreakConverter::eLinebreakContent);
1088 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
1089 :
1090 : nsCOMPtr<nsISupportsString> pState =
1091 0 : do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
1092 0 : if (!pState) {
1093 0 : return NS_ERROR_OUT_OF_MEMORY;
1094 : }
1095 0 : pState->SetData(value);
1096 0 : state->SetStateProperty(pState);
1097 : }
1098 : }
1099 :
1100 0 : if (mDisabledChanged) {
1101 0 : if (!state) {
1102 0 : rv = GetPrimaryPresState(this, &state);
1103 : }
1104 0 : if (state) {
1105 : // We do not want to save the real disabled state but the disabled
1106 : // attribute.
1107 0 : state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
1108 : }
1109 : }
1110 0 : return rv;
1111 : }
1112 :
1113 : bool
1114 0 : nsHTMLTextAreaElement::RestoreState(nsPresState* aState)
1115 : {
1116 : nsCOMPtr<nsISupportsString> state
1117 0 : (do_QueryInterface(aState->GetStateProperty()));
1118 :
1119 0 : if (state) {
1120 0 : nsAutoString data;
1121 0 : state->GetData(data);
1122 0 : SetValue(data);
1123 : }
1124 :
1125 0 : if (aState->IsDisabledSet()) {
1126 0 : SetDisabled(aState->GetDisabled());
1127 : }
1128 :
1129 0 : return false;
1130 : }
1131 :
1132 : nsEventStates
1133 0 : nsHTMLTextAreaElement::IntrinsicState() const
1134 : {
1135 0 : nsEventStates state = nsGenericHTMLFormElement::IntrinsicState();
1136 :
1137 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
1138 0 : state |= NS_EVENT_STATE_REQUIRED;
1139 : } else {
1140 0 : state |= NS_EVENT_STATE_OPTIONAL;
1141 : }
1142 :
1143 0 : if (IsCandidateForConstraintValidation()) {
1144 0 : if (IsValid()) {
1145 0 : state |= NS_EVENT_STATE_VALID;
1146 : } else {
1147 0 : state |= NS_EVENT_STATE_INVALID;
1148 : // :-moz-ui-invalid always apply if the element suffers from a custom
1149 : // error and never applies if novalidate is set on the form owner.
1150 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
1151 0 : (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
1152 0 : (mCanShowInvalidUI && ShouldShowValidityUI()))) {
1153 0 : state |= NS_EVENT_STATE_MOZ_UI_INVALID;
1154 : }
1155 : }
1156 :
1157 : // :-moz-ui-valid applies if all the following are true:
1158 : // 1. The element is not focused, or had either :-moz-ui-valid or
1159 : // :-moz-ui-invalid applying before it was focused ;
1160 : // 2. The element is either valid or isn't allowed to have
1161 : // :-moz-ui-invalid applying ;
1162 : // 3. The element has no form owner or its form owner doesn't have the
1163 : // novalidate attribute set ;
1164 : // 4. The element has already been modified or the user tried to submit the
1165 : // form owner while invalid.
1166 0 : if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
1167 0 : (mCanShowValidUI && ShouldShowValidityUI() &&
1168 0 : (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
1169 0 : !mCanShowInvalidUI)))) {
1170 0 : state |= NS_EVENT_STATE_MOZ_UI_VALID;
1171 : }
1172 : }
1173 :
1174 0 : if (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder) &&
1175 0 : !nsContentUtils::IsFocusedContent((nsIContent*)(this)) &&
1176 0 : IsValueEmpty()) {
1177 0 : state |= NS_EVENT_STATE_MOZ_PLACEHOLDER;
1178 : }
1179 :
1180 : return state;
1181 : }
1182 :
1183 : nsresult
1184 0 : nsHTMLTextAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1185 : nsIContent* aBindingParent,
1186 : bool aCompileEventHandlers)
1187 : {
1188 : nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
1189 : aBindingParent,
1190 0 : aCompileEventHandlers);
1191 0 : NS_ENSURE_SUCCESS(rv, rv);
1192 :
1193 : // If there is a disabled fieldset in the parent chain, the element is now
1194 : // barred from constraint validation and can't suffer from value missing.
1195 0 : UpdateValueMissingValidityState();
1196 0 : UpdateBarredFromConstraintValidation();
1197 :
1198 : // And now make sure our state is up to date
1199 0 : UpdateState(false);
1200 :
1201 0 : return rv;
1202 : }
1203 :
1204 : void
1205 0 : nsHTMLTextAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
1206 : {
1207 0 : nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
1208 :
1209 : // We might be no longer disabled because of parent chain changed.
1210 0 : UpdateValueMissingValidityState();
1211 0 : UpdateBarredFromConstraintValidation();
1212 :
1213 : // And now make sure our state is up to date
1214 0 : UpdateState(false);
1215 0 : }
1216 :
1217 : nsresult
1218 0 : nsHTMLTextAreaElement::BeforeSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
1219 : const nsAttrValueOrString* aValue,
1220 : bool aNotify)
1221 : {
1222 0 : if (aNotify && aName == nsGkAtoms::disabled &&
1223 : aNameSpaceID == kNameSpaceID_None) {
1224 0 : mDisabledChanged = true;
1225 : }
1226 :
1227 : return nsGenericHTMLFormElement::BeforeSetAttr(aNameSpaceID, aName,
1228 0 : aValue, aNotify);
1229 : }
1230 :
1231 : void
1232 0 : nsHTMLTextAreaElement::CharacterDataChanged(nsIDocument* aDocument,
1233 : nsIContent* aContent,
1234 : CharacterDataChangeInfo* aInfo)
1235 : {
1236 0 : ContentChanged(aContent);
1237 0 : }
1238 :
1239 : void
1240 0 : nsHTMLTextAreaElement::ContentAppended(nsIDocument* aDocument,
1241 : nsIContent* aContainer,
1242 : nsIContent* aFirstNewContent,
1243 : PRInt32 /* unused */)
1244 : {
1245 0 : ContentChanged(aFirstNewContent);
1246 0 : }
1247 :
1248 : void
1249 0 : nsHTMLTextAreaElement::ContentInserted(nsIDocument* aDocument,
1250 : nsIContent* aContainer,
1251 : nsIContent* aChild,
1252 : PRInt32 /* unused */)
1253 : {
1254 0 : ContentChanged(aChild);
1255 0 : }
1256 :
1257 : void
1258 0 : nsHTMLTextAreaElement::ContentRemoved(nsIDocument* aDocument,
1259 : nsIContent* aContainer,
1260 : nsIContent* aChild,
1261 : PRInt32 aIndexInContainer,
1262 : nsIContent* aPreviousSibling)
1263 : {
1264 0 : ContentChanged(aChild);
1265 0 : }
1266 :
1267 : void
1268 0 : nsHTMLTextAreaElement::ContentChanged(nsIContent* aContent)
1269 : {
1270 0 : if (!mValueChanged && mDoneAddingChildren &&
1271 0 : nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
1272 : // Hard to say what the reset can trigger, so be safe pending
1273 : // further auditing.
1274 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
1275 0 : Reset();
1276 : }
1277 0 : }
1278 :
1279 : nsresult
1280 0 : nsHTMLTextAreaElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
1281 : const nsAttrValue* aValue, bool aNotify)
1282 : {
1283 0 : if (aNameSpaceID == kNameSpaceID_None) {
1284 0 : if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
1285 : aName == nsGkAtoms::readonly) {
1286 0 : UpdateValueMissingValidityState();
1287 :
1288 : // This *has* to be called *after* validity has changed.
1289 0 : if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
1290 0 : UpdateBarredFromConstraintValidation();
1291 : }
1292 0 : } else if (aName == nsGkAtoms::maxlength) {
1293 0 : UpdateTooLongValidityState();
1294 : }
1295 :
1296 0 : if (aName == nsGkAtoms::readonly) {
1297 0 : UpdateEditableState(aNotify);
1298 : }
1299 0 : UpdateState(aNotify);
1300 : }
1301 :
1302 : return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName, aValue,
1303 0 : aNotify);
1304 : }
1305 :
1306 : nsresult
1307 0 : nsHTMLTextAreaElement::CopyInnerTo(nsGenericElement* aDest) const
1308 : {
1309 0 : nsresult rv = nsGenericHTMLFormElement::CopyInnerTo(aDest);
1310 0 : NS_ENSURE_SUCCESS(rv, rv);
1311 :
1312 0 : if (aDest->OwnerDoc()->IsStaticDocument()) {
1313 0 : nsAutoString value;
1314 0 : GetValueInternal(value, true);
1315 0 : static_cast<nsHTMLTextAreaElement*>(aDest)->SetValue(value);
1316 : }
1317 0 : return NS_OK;
1318 : }
1319 :
1320 : bool
1321 0 : nsHTMLTextAreaElement::IsMutable() const
1322 : {
1323 0 : return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
1324 : }
1325 :
1326 : bool
1327 0 : nsHTMLTextAreaElement::IsValueEmpty() const
1328 : {
1329 0 : nsAutoString value;
1330 0 : GetValueInternal(value, true);
1331 :
1332 0 : return value.IsEmpty();
1333 : }
1334 :
1335 : // nsIConstraintValidation
1336 :
1337 : NS_IMETHODIMP
1338 0 : nsHTMLTextAreaElement::SetCustomValidity(const nsAString& aError)
1339 : {
1340 0 : nsIConstraintValidation::SetCustomValidity(aError);
1341 :
1342 0 : UpdateState(true);
1343 :
1344 0 : return NS_OK;
1345 : }
1346 :
1347 : bool
1348 0 : nsHTMLTextAreaElement::IsTooLong()
1349 : {
1350 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength) || !mValueChanged) {
1351 0 : return false;
1352 : }
1353 :
1354 0 : PRInt32 maxLength = -1;
1355 0 : GetMaxLength(&maxLength);
1356 :
1357 : // Maxlength of -1 means parsing error.
1358 0 : if (maxLength == -1) {
1359 0 : return false;
1360 : }
1361 :
1362 0 : PRInt32 textLength = -1;
1363 0 : GetTextLength(&textLength);
1364 :
1365 0 : return textLength > maxLength;
1366 : }
1367 :
1368 : bool
1369 0 : nsHTMLTextAreaElement::IsValueMissing() const
1370 : {
1371 0 : if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
1372 0 : return false;
1373 : }
1374 :
1375 0 : return IsValueEmpty();
1376 : }
1377 :
1378 : void
1379 0 : nsHTMLTextAreaElement::UpdateTooLongValidityState()
1380 : {
1381 : // TODO: this code will be re-enabled with bug 613016 and bug 613019.
1382 : #if 0
1383 : SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
1384 : #endif
1385 0 : }
1386 :
1387 : void
1388 0 : nsHTMLTextAreaElement::UpdateValueMissingValidityState()
1389 : {
1390 0 : SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
1391 0 : }
1392 :
1393 : void
1394 0 : nsHTMLTextAreaElement::UpdateBarredFromConstraintValidation()
1395 : {
1396 : SetBarredFromConstraintValidation(HasAttr(kNameSpaceID_None,
1397 0 : nsGkAtoms::readonly) ||
1398 0 : IsDisabled());
1399 0 : }
1400 :
1401 : nsresult
1402 0 : nsHTMLTextAreaElement::GetValidationMessage(nsAString& aValidationMessage,
1403 : ValidityStateType aType)
1404 : {
1405 0 : nsresult rv = NS_OK;
1406 :
1407 0 : switch (aType)
1408 : {
1409 : case VALIDITY_STATE_TOO_LONG:
1410 : {
1411 0 : nsXPIDLString message;
1412 0 : PRInt32 maxLength = -1;
1413 0 : PRInt32 textLength = -1;
1414 0 : nsAutoString strMaxLength;
1415 0 : nsAutoString strTextLength;
1416 :
1417 0 : GetMaxLength(&maxLength);
1418 0 : GetTextLength(&textLength);
1419 :
1420 0 : strMaxLength.AppendInt(maxLength);
1421 0 : strTextLength.AppendInt(textLength);
1422 :
1423 0 : const PRUnichar* params[] = { strMaxLength.get(), strTextLength.get() };
1424 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1425 : "FormValidationTextTooLong",
1426 0 : params, message);
1427 0 : aValidationMessage = message;
1428 : }
1429 0 : break;
1430 : case VALIDITY_STATE_VALUE_MISSING:
1431 : {
1432 0 : nsXPIDLString message;
1433 : rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1434 : "FormValidationValueMissing",
1435 0 : message);
1436 0 : aValidationMessage = message;
1437 : }
1438 0 : break;
1439 : default:
1440 0 : rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
1441 : }
1442 :
1443 0 : return rv;
1444 : }
1445 :
1446 : NS_IMETHODIMP_(bool)
1447 0 : nsHTMLTextAreaElement::IsSingleLineTextControl() const
1448 : {
1449 0 : return false;
1450 : }
1451 :
1452 : NS_IMETHODIMP_(bool)
1453 0 : nsHTMLTextAreaElement::IsTextArea() const
1454 : {
1455 0 : return true;
1456 : }
1457 :
1458 : NS_IMETHODIMP_(bool)
1459 0 : nsHTMLTextAreaElement::IsPlainTextControl() const
1460 : {
1461 : // need to check our HTML attribute and/or CSS.
1462 0 : return true;
1463 : }
1464 :
1465 : NS_IMETHODIMP_(bool)
1466 0 : nsHTMLTextAreaElement::IsPasswordTextControl() const
1467 : {
1468 0 : return false;
1469 : }
1470 :
1471 : NS_IMETHODIMP_(PRInt32)
1472 0 : nsHTMLTextAreaElement::GetCols()
1473 : {
1474 0 : const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::cols);
1475 0 : if (attr) {
1476 0 : PRInt32 cols = attr->Type() == nsAttrValue::eInteger ?
1477 0 : attr->GetIntegerValue() : 0;
1478 : // XXX why a default of 1 char, why hide it
1479 0 : return (cols <= 0) ? 1 : cols;
1480 : }
1481 :
1482 0 : return DEFAULT_COLS;
1483 : }
1484 :
1485 : NS_IMETHODIMP_(PRInt32)
1486 0 : nsHTMLTextAreaElement::GetWrapCols()
1487 : {
1488 : // wrap=off means -1 for wrap width no matter what cols is
1489 : nsHTMLTextWrap wrapProp;
1490 0 : nsITextControlElement::GetWrapPropertyEnum(this, wrapProp);
1491 0 : if (wrapProp == nsITextControlElement::eHTMLTextWrap_Off) {
1492 : // do not wrap when wrap=off
1493 0 : return -1;
1494 : }
1495 :
1496 : // Otherwise we just wrap at the given number of columns
1497 0 : return GetCols();
1498 : }
1499 :
1500 :
1501 : NS_IMETHODIMP_(PRInt32)
1502 0 : nsHTMLTextAreaElement::GetRows()
1503 : {
1504 0 : const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::rows);
1505 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
1506 0 : PRInt32 rows = attr->GetIntegerValue();
1507 0 : return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
1508 : }
1509 :
1510 0 : return DEFAULT_ROWS_TEXTAREA;
1511 : }
1512 :
1513 : NS_IMETHODIMP_(void)
1514 0 : nsHTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
1515 : {
1516 0 : GetDefaultValue(aValue);
1517 0 : }
1518 :
1519 : NS_IMETHODIMP_(bool)
1520 0 : nsHTMLTextAreaElement::ValueChanged() const
1521 : {
1522 0 : return mValueChanged;
1523 : }
1524 :
1525 : NS_IMETHODIMP_(void)
1526 0 : nsHTMLTextAreaElement::GetTextEditorValue(nsAString& aValue,
1527 : bool aIgnoreWrap) const
1528 : {
1529 0 : mState->GetValue(aValue, aIgnoreWrap);
1530 0 : }
1531 :
1532 : NS_IMETHODIMP_(void)
1533 0 : nsHTMLTextAreaElement::SetTextEditorValue(const nsAString& aValue,
1534 : bool aUserInput)
1535 : {
1536 0 : mState->SetValue(aValue, aUserInput);
1537 0 : }
1538 :
1539 : NS_IMETHODIMP_(void)
1540 0 : nsHTMLTextAreaElement::InitializeKeyboardEventListeners()
1541 : {
1542 0 : mState->InitializeKeyboardEventListeners();
1543 0 : }
1544 :
1545 : NS_IMETHODIMP_(void)
1546 0 : nsHTMLTextAreaElement::OnValueChanged(bool aNotify)
1547 : {
1548 : // Update the validity state
1549 0 : bool validBefore = IsValid();
1550 0 : UpdateTooLongValidityState();
1551 0 : UpdateValueMissingValidityState();
1552 :
1553 0 : if (validBefore != IsValid() ||
1554 0 : (HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)
1555 0 : && !nsContentUtils::IsFocusedContent((nsIContent*)(this)))) {
1556 0 : UpdateState(aNotify);
1557 : }
1558 0 : }
1559 :
1560 : NS_IMETHODIMP_(bool)
1561 0 : nsHTMLTextAreaElement::HasCachedSelection()
1562 : {
1563 0 : return mState->IsSelectionCached();
1564 : }
1565 :
1566 : void
1567 0 : nsHTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
1568 : {
1569 0 : UpdateValueMissingValidityState();
1570 0 : UpdateBarredFromConstraintValidation();
1571 :
1572 0 : nsGenericHTMLFormElement::FieldSetDisabledChanged(aNotify);
1573 4392 : }
|