forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1562 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1562 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/dom/HTMLTextAreaElement.h"
 | |
| 
 | |
| #include "mozAutoDocUpdate.h"
 | |
| #include "mozilla/AsyncEventDispatcher.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/dom/HTMLFormSubmission.h"
 | |
| #include "mozilla/dom/HTMLTextAreaElementBinding.h"
 | |
| #include "mozilla/EventDispatcher.h"
 | |
| #include "mozilla/EventStates.h"
 | |
| #include "mozilla/MouseEvents.h"
 | |
| #include "nsAttrValueInlines.h"
 | |
| #include "nsContentCID.h"
 | |
| #include "nsContentCreatorFunctions.h"
 | |
| #include "nsError.h"
 | |
| #include "nsFocusManager.h"
 | |
| #include "nsIComponentManager.h"
 | |
| #include "nsIConstraintValidation.h"
 | |
| #include "nsIControllers.h"
 | |
| #include "nsIDocument.h"
 | |
| #include "nsIDOMHTMLFormElement.h"
 | |
| #include "nsIFormControlFrame.h"
 | |
| #include "nsIFormControl.h"
 | |
| #include "nsIForm.h"
 | |
| #include "nsIFrame.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsITextControlFrame.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsLinebreakConverter.h"
 | |
| #include "nsMappedAttributes.h"
 | |
| #include "nsPIDOMWindow.h"
 | |
| #include "nsPresContext.h"
 | |
| #include "nsPresState.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsRuleData.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsTextEditorState.h"
 | |
| #include "nsIController.h"
 | |
| 
 | |
| static NS_DEFINE_CID(kXULControllersCID,  NS_XULCONTROLLERS_CID);
 | |
| 
 | |
| #define NS_NO_CONTENT_DISPATCH (1 << 0)
 | |
| 
 | |
| NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| HTMLTextAreaElement::HTMLTextAreaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
 | |
|                                          FromParser aFromParser)
 | |
|   : nsGenericHTMLFormElementWithState(aNodeInfo),
 | |
|     mValueChanged(false),
 | |
|     mHandlingSelect(false),
 | |
|     mDoneAddingChildren(!aFromParser),
 | |
|     mInhibitStateRestoration(!!(aFromParser & FROM_PARSER_FRAGMENT)),
 | |
|     mDisabledChanged(false),
 | |
|     mCanShowInvalidUI(true),
 | |
|     mCanShowValidUI(true),
 | |
|     mState(this)
 | |
| {
 | |
|   AddMutationObserver(this);
 | |
| 
 | |
|   // Set up our default state.  By default we're enabled (since we're
 | |
|   // a control type that can be disabled but not actually disabled
 | |
|   // right now), optional, and valid.  We are NOT readwrite by default
 | |
|   // until someone calls UpdateEditableState on us, apparently!  Also
 | |
|   // by default we don't have to show validity UI and so forth.
 | |
|   AddStatesSilently(NS_EVENT_STATE_ENABLED |
 | |
|                     NS_EVENT_STATE_OPTIONAL |
 | |
|                     NS_EVENT_STATE_VALID);
 | |
| }
 | |
| 
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement,
 | |
|                                    nsGenericHTMLFormElementWithState,
 | |
|                                    mValidity,
 | |
|                                    mControllers,
 | |
|                                    mState)
 | |
| 
 | |
| NS_IMPL_ADDREF_INHERITED(HTMLTextAreaElement, Element)
 | |
| NS_IMPL_RELEASE_INHERITED(HTMLTextAreaElement, Element)
 | |
| 
 | |
| 
 | |
| // QueryInterface implementation for HTMLTextAreaElement
 | |
| NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement)
 | |
|   NS_INTERFACE_TABLE_INHERITED(HTMLTextAreaElement,
 | |
|                                nsIDOMHTMLTextAreaElement,
 | |
|                                nsITextControlElement,
 | |
|                                nsIDOMNSEditableElement,
 | |
|                                nsIMutationObserver,
 | |
|                                nsIConstraintValidation)
 | |
| NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElementWithState)
 | |
| 
 | |
| 
 | |
| // nsIDOMHTMLTextAreaElement
 | |
| 
 | |
| 
 | |
| NS_IMPL_ELEMENT_CLONE(HTMLTextAreaElement)
 | |
| 
 | |
| // nsIConstraintValidation
 | |
| NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(HTMLTextAreaElement)
 | |
| 
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetForm(nsIDOMHTMLFormElement** aForm)
 | |
| {
 | |
|   return nsGenericHTMLFormElementWithState::GetForm(aForm);
 | |
| }
 | |
| 
 | |
| 
 | |
| // nsIContent
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::Select()
 | |
| {
 | |
|   // XXX Bug?  We have to give the input focus before contents can be
 | |
|   // selected
 | |
| 
 | |
|   FocusTristate state = FocusState();
 | |
|   if (state == eUnfocusable) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
| 
 | |
|   RefPtr<nsPresContext> presContext = GetPresContext(eForComposedDoc);
 | |
|   if (state == eInactiveWindow) {
 | |
|     if (fm)
 | |
|       fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
 | |
|     SelectAll(presContext);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsEventStatus status = nsEventStatus_eIgnore;
 | |
|   WidgetGUIEvent event(true, eFormSelect, nullptr);
 | |
|   // XXXbz HTMLInputElement guards against this reentering; shouldn't we?
 | |
|   EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
 | |
|                             &event, nullptr, &status);
 | |
| 
 | |
|   // If the DOM event was not canceled (e.g. by a JS event handler
 | |
|   // returning false)
 | |
|   if (status == nsEventStatus_eIgnore) {
 | |
|     if (fm) {
 | |
|       fm->SetFocus(this, nsIFocusManager::FLAG_NOSCROLL);
 | |
| 
 | |
|       // ensure that the element is actually focused
 | |
|       nsCOMPtr<nsIDOMElement> focusedElement;
 | |
|       fm->GetFocusedElement(getter_AddRefs(focusedElement));
 | |
|       if (SameCOMIdentity(static_cast<nsIDOMNode*>(this), focusedElement)) {
 | |
|         // Now Select all the text!
 | |
|         SelectAll(presContext);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SelectAll(nsPresContext* aPresContext)
 | |
| {
 | |
|   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 | |
| 
 | |
|   if (formControlFrame) {
 | |
|     formControlFrame->SetFormProperty(nsGkAtoms::select, EmptyString());
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsHTMLFocusable(bool aWithMouse,
 | |
|                                      bool *aIsFocusable, int32_t *aTabIndex)
 | |
| {
 | |
|   if (nsGenericHTMLFormElementWithState::IsHTMLFocusable(aWithMouse, aIsFocusable,
 | |
|                                                          aTabIndex))
 | |
|   {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // disabled textareas are not focusable
 | |
|   *aIsFocusable = !IsDisabled();
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Autofocus, autofocus)
 | |
| NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Cols, cols, DEFAULT_COLS)
 | |
| NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Disabled, disabled)
 | |
| NS_IMPL_NON_NEGATIVE_INT_ATTR(HTMLTextAreaElement, MaxLength, maxlength)
 | |
| NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Name, name)
 | |
| NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, ReadOnly, readonly)
 | |
| NS_IMPL_BOOL_ATTR(HTMLTextAreaElement, Required, required)
 | |
| NS_IMPL_UINT_ATTR_NON_ZERO_DEFAULT_VALUE(HTMLTextAreaElement, Rows, rows, DEFAULT_ROWS_TEXTAREA)
 | |
| NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Wrap, wrap)
 | |
| NS_IMPL_STRING_ATTR(HTMLTextAreaElement, Placeholder, placeholder)
 | |
|   
 | |
| int32_t
 | |
| HTMLTextAreaElement::TabIndexDefault()
 | |
| {
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP 
 | |
| HTMLTextAreaElement::GetType(nsAString& aType)
 | |
| {
 | |
|   aType.AssignLiteral("textarea");
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP 
 | |
| HTMLTextAreaElement::GetValue(nsAString& aValue)
 | |
| {
 | |
|   GetValueInternal(aValue, true);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const
 | |
| {
 | |
|   mState.GetValue(aValue, aIgnoreWrap);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(nsIEditor*)
 | |
| HTMLTextAreaElement::GetTextEditor()
 | |
| {
 | |
|   return GetEditor();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(nsISelectionController*)
 | |
| HTMLTextAreaElement::GetSelectionController()
 | |
| {
 | |
|   return mState.GetSelectionController();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(nsFrameSelection*)
 | |
| HTMLTextAreaElement::GetConstFrameSelection()
 | |
| {
 | |
|   return mState.GetConstFrameSelection();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::BindToFrame(nsTextControlFrame* aFrame)
 | |
| {
 | |
|   return mState.BindToFrame(aFrame);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::UnbindFromFrame(nsTextControlFrame* aFrame)
 | |
| {
 | |
|   if (aFrame) {
 | |
|     mState.UnbindFromFrame(aFrame);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::CreateEditor()
 | |
| {
 | |
|   return mState.PrepareEditor();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(nsIContent*)
 | |
| HTMLTextAreaElement::GetRootEditorNode()
 | |
| {
 | |
|   return mState.GetRootNode();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(Element*)
 | |
| HTMLTextAreaElement::CreatePlaceholderNode()
 | |
| {
 | |
|   NS_ENSURE_SUCCESS(mState.CreatePlaceholderNode(), nullptr);
 | |
|   return mState.GetPlaceholderNode();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(Element*)
 | |
| HTMLTextAreaElement::GetPlaceholderNode()
 | |
| {
 | |
|   return mState.GetPlaceholderNode();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::UpdatePlaceholderVisibility(bool aNotify)
 | |
| {
 | |
|   mState.UpdatePlaceholderVisibility(aNotify);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::GetPlaceholderVisibility()
 | |
| {
 | |
|   return mState.GetPlaceholderVisibility();
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
 | |
|                                       uint32_t aFlags)
 | |
| {
 | |
|   // Need to set the value changed flag here, so that
 | |
|   // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
 | |
|   // if needed.
 | |
|   SetValueChanged(true);
 | |
|   aFlags |= nsTextEditorState::eSetValue_Notify;
 | |
|   if (!mState.SetValue(aValue, aFlags)) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP 
 | |
| HTMLTextAreaElement::SetValue(const nsAString& aValue)
 | |
| {
 | |
|   // If the value has been set by a script, we basically want to keep the
 | |
|   // current change event state. If the element is ready to fire a change
 | |
|   // event, we should keep it that way. Otherwise, we should make sure the
 | |
|   // element will not fire any event because of the script interaction.
 | |
|   //
 | |
|   // NOTE: this is currently quite expensive work (too much string
 | |
|   // manipulation). We should probably optimize that.
 | |
|   nsAutoString currentValue;
 | |
|   GetValueInternal(currentValue, true);
 | |
| 
 | |
|   nsresult rv =
 | |
|     SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   if (mFocusedValue.Equals(currentValue)) {
 | |
|     GetValueInternal(mFocusedValue, true);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP 
 | |
| HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 | |
| {
 | |
|   if (!nsContentUtils::IsCallerChrome()) {
 | |
|     return NS_ERROR_DOM_SECURITY_ERR;
 | |
|   }
 | |
|   return SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
 | |
| {
 | |
|   bool previousValue = mValueChanged;
 | |
| 
 | |
|   mValueChanged = aValueChanged;
 | |
|   if (!aValueChanged && !mState.IsEmpty()) {
 | |
|     mState.EmptyValue();
 | |
|   }
 | |
| 
 | |
|   if (mValueChanged != previousValue) {
 | |
|     UpdateState(true);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetDefaultValue(nsAString& aDefaultValue)
 | |
| {
 | |
|   if (!nsContentUtils::GetNodeTextContent(this, false, aDefaultValue, fallible)) {
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }  
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   SetDefaultValue(aDefaultValue, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError)
 | |
| {
 | |
|   nsresult rv = nsContentUtils::SetNodeTextContent(this, aDefaultValue, true);
 | |
|   if (NS_SUCCEEDED(rv) && !mValueChanged) {
 | |
|     Reset();
 | |
|   }
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID,
 | |
|                                     nsIAtom* aAttribute,
 | |
|                                     const nsAString& aValue,
 | |
|                                     nsAttrValue& aResult)
 | |
| {
 | |
|   if (aNamespaceID == kNameSpaceID_None) {
 | |
|     if (aAttribute == nsGkAtoms::maxlength) {
 | |
|       return aResult.ParseNonNegativeIntValue(aValue);
 | |
|     } else if (aAttribute == nsGkAtoms::cols ||
 | |
|                aAttribute == nsGkAtoms::rows) {
 | |
|       return aResult.ParsePositiveIntValue(aValue);
 | |
|     }
 | |
|   }
 | |
|   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
 | |
|                                               aResult);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
 | |
|                                            nsRuleData* aData)
 | |
| {
 | |
|   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
 | |
|     // wrap=off
 | |
|     nsCSSValue* whiteSpace = aData->ValueForWhiteSpace();
 | |
|     if (whiteSpace->GetUnit() == eCSSUnit_Null) {
 | |
|       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
 | |
|       if (value && value->Type() == nsAttrValue::eString &&
 | |
|           value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
 | |
|         whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE, eCSSUnit_Enumerated);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes, aData);
 | |
|   nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
 | |
| }
 | |
| 
 | |
| nsChangeHint
 | |
| HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
 | |
|                                             int32_t aModType) const
 | |
| {
 | |
|   nsChangeHint retval =
 | |
|       nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
 | |
|   if (aAttribute == nsGkAtoms::rows ||
 | |
|       aAttribute == nsGkAtoms::cols) {
 | |
|     retval |= NS_STYLE_HINT_REFLOW;
 | |
|   } else if (aAttribute == nsGkAtoms::wrap) {
 | |
|     retval |= nsChangeHint_ReconstructFrame;
 | |
|   } else if (aAttribute == nsGkAtoms::placeholder) {
 | |
|     retval |= NS_STYLE_HINT_FRAMECHANGE;
 | |
|   }
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 | |
| {
 | |
|   static const MappedAttributeEntry attributes[] = {
 | |
|     { &nsGkAtoms::wrap },
 | |
|     { nullptr }
 | |
|   };
 | |
| 
 | |
|   static const MappedAttributeEntry* const map[] = {
 | |
|     attributes,
 | |
|     sDivAlignAttributeMap,
 | |
|     sCommonAttributeMap,
 | |
|   };
 | |
| 
 | |
|   return FindAttributeDependence(aAttribute, map);
 | |
| }
 | |
| 
 | |
| nsMapRuleToAttributesFunc
 | |
| HTMLTextAreaElement::GetAttributeMappingFunction() const
 | |
| {
 | |
|   return &MapAttributesIntoRule;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsDisabledForEvents(EventMessage aMessage)
 | |
| {
 | |
|   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
 | |
|   nsIFrame* formFrame = do_QueryFrame(formControlFrame);
 | |
|   return IsElementDisabledForEvents(aMessage, formFrame);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::PreHandleEvent(EventChainPreVisitor& aVisitor)
 | |
| {
 | |
|   aVisitor.mCanHandle = false;
 | |
|   if (IsDisabledForEvents(aVisitor.mEvent->mMessage)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Don't dispatch a second select event if we are already handling
 | |
|   // one.
 | |
|   if (aVisitor.mEvent->mMessage == eFormSelect) {
 | |
|     if (mHandlingSelect) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|     mHandlingSelect = true;
 | |
|   }
 | |
| 
 | |
|   // If noContentDispatch is true we will not allow content to handle
 | |
|   // this event.  But to allow middle mouse button paste to work we must allow 
 | |
|   // middle clicks to go to text fields anyway.
 | |
|   if (aVisitor.mEvent->mFlags.mNoContentDispatch) {
 | |
|     aVisitor.mItemFlags |= NS_NO_CONTENT_DISPATCH;
 | |
|   }
 | |
|   if (aVisitor.mEvent->mMessage == eMouseClick &&
 | |
|       aVisitor.mEvent->AsMouseEvent()->button ==
 | |
|         WidgetMouseEvent::eMiddleButton) {
 | |
|     aVisitor.mEvent->mFlags.mNoContentDispatch = false;
 | |
|   }
 | |
| 
 | |
|   // Fire onchange (if necessary), before we do the blur, bug 370521.
 | |
|   if (aVisitor.mEvent->mMessage == eBlur) {
 | |
|     FireChangeEventIfNeeded();
 | |
|   }
 | |
| 
 | |
|   return nsGenericHTMLFormElementWithState::PreHandleEvent(aVisitor);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::FireChangeEventIfNeeded()
 | |
| {
 | |
|   nsString value;
 | |
|   GetValueInternal(value, true);
 | |
| 
 | |
|   if (mFocusedValue.Equals(value)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Dispatch the change event.
 | |
|   mFocusedValue = value;
 | |
|   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
 | |
|                                        static_cast<nsIContent*>(this),
 | |
|                                        NS_LITERAL_STRING("change"), true,
 | |
|                                        false);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
 | |
| {
 | |
|   if (aVisitor.mEvent->mMessage == eFormSelect) {
 | |
|     mHandlingSelect = false;
 | |
|   }
 | |
| 
 | |
|   if (aVisitor.mEvent->mMessage == eFocus ||
 | |
|       aVisitor.mEvent->mMessage == eBlur) {
 | |
|     if (aVisitor.mEvent->mMessage == eFocus) {
 | |
|       // If the invalid UI is shown, we should show it while focusing (and
 | |
|       // update). Otherwise, we should not.
 | |
|       GetValueInternal(mFocusedValue, true);
 | |
|       mCanShowInvalidUI = !IsValid() && ShouldShowValidityUI();
 | |
| 
 | |
|       // If neither invalid UI nor valid UI is shown, we shouldn't show the valid
 | |
|       // UI while typing.
 | |
|       mCanShowValidUI = ShouldShowValidityUI();
 | |
|     } else { // eBlur
 | |
|       mCanShowInvalidUI = true;
 | |
|       mCanShowValidUI = true;
 | |
|     }
 | |
| 
 | |
|     UpdateState(true);
 | |
|   }
 | |
| 
 | |
|   // Reset the flag for other content besides this text field
 | |
|   aVisitor.mEvent->mFlags.mNoContentDispatch =
 | |
|     ((aVisitor.mItemFlags & NS_NO_CONTENT_DISPATCH) != 0);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::DoneAddingChildren(bool aHaveNotified)
 | |
| {
 | |
|   if (!mValueChanged) {
 | |
|     if (!mDoneAddingChildren) {
 | |
|       // Reset now that we're done adding children if the content sink tried to
 | |
|       // sneak some text in without calling AppendChildTo.
 | |
|       Reset();
 | |
|     }
 | |
| 
 | |
|     if (!mInhibitStateRestoration) {
 | |
|       nsresult rv = GenerateStateKey();
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         RestoreFormControlState();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mDoneAddingChildren = true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsDoneAddingChildren()
 | |
| {
 | |
|   return mDoneAddingChildren;
 | |
| }
 | |
| 
 | |
| // Controllers Methods
 | |
| 
 | |
| nsIControllers*
 | |
| HTMLTextAreaElement::GetControllers(ErrorResult& aError)
 | |
| {
 | |
|   if (!mControllers)
 | |
|   {
 | |
|     nsresult rv;
 | |
|     mControllers = do_CreateInstance(kXULControllersCID, &rv);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       aError.Throw(rv);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIController> controller = do_CreateInstance("@mozilla.org/editor/editorcontroller;1", &rv);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       aError.Throw(rv);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     mControllers->AppendController(controller);
 | |
| 
 | |
|     controller = do_CreateInstance("@mozilla.org/editor/editingcontroller;1", &rv);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       aError.Throw(rv);
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     mControllers->AppendController(controller);
 | |
|   }
 | |
| 
 | |
|   return mControllers;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetControllers(nsIControllers** aResult)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aResult);
 | |
| 
 | |
|   ErrorResult error;
 | |
|   *aResult = GetControllers(error);
 | |
|   NS_IF_ADDREF(*aResult);
 | |
| 
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| HTMLTextAreaElement::GetTextLength()
 | |
| {
 | |
|   nsAutoString val;
 | |
|   GetValue(val);
 | |
|   return val.Length();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetTextLength(int32_t *aTextLength)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aTextLength);
 | |
|   *aTextLength = GetTextLength();
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetSelectionStart(int32_t *aSelectionStart)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aSelectionStart);
 | |
| 
 | |
|   ErrorResult error;
 | |
|   *aSelectionStart = GetSelectionStart(error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
 | |
| {
 | |
|   int32_t selStart, selEnd;
 | |
|   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 | |
| 
 | |
|   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
 | |
|     return mState.GetSelectionProperties().mStart;
 | |
|   }
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
|   return selStart;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetSelectionStart(int32_t aSelectionStart)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   SetSelectionStart(aSelectionStart, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError)
 | |
| {
 | |
|   if (mState.IsSelectionCached()) {
 | |
|     mState.GetSelectionProperties().mStart = aSelectionStart;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsAutoString direction;
 | |
|   nsresult rv = GetSelectionDirection(direction);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
|   int32_t start, end;
 | |
|   rv = GetSelectionRange(&start, &end);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
|   start = aSelectionStart;
 | |
|   if (end < start) {
 | |
|     end = start;
 | |
|   }
 | |
|   rv = SetSelectionRange(start, end, direction);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::GetSelectionEnd(int32_t *aSelectionEnd)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aSelectionEnd);
 | |
| 
 | |
|   ErrorResult error;
 | |
|   *aSelectionEnd = GetSelectionEnd(error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| HTMLTextAreaElement::GetSelectionEnd(ErrorResult& aError)
 | |
| {
 | |
|   int32_t selStart, selEnd;
 | |
|   nsresult rv = GetSelectionRange(&selStart, &selEnd);
 | |
| 
 | |
|   if (NS_FAILED(rv) && mState.IsSelectionCached()) {
 | |
|     return mState.GetSelectionProperties().mEnd;
 | |
|   }
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
|   return selEnd;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetSelectionEnd(int32_t aSelectionEnd)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   SetSelectionEnd(aSelectionEnd, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError)
 | |
| {
 | |
|   if (mState.IsSelectionCached()) {
 | |
|     mState.GetSelectionProperties().mEnd = aSelectionEnd;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsAutoString direction;
 | |
|   nsresult rv = GetSelectionDirection(direction);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
|   int32_t start, end;
 | |
|   rv = GetSelectionRange(&start, &end);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
|   end = aSelectionEnd;
 | |
|   if (start > end) {
 | |
|     start = end;
 | |
|   }
 | |
|   rv = SetSelectionRange(start, end, direction);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::GetSelectionRange(int32_t* aSelectionStart,
 | |
|                                        int32_t* aSelectionEnd)
 | |
| {
 | |
|   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 | |
|   nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
 | |
|   if (textControlFrame) {
 | |
|     return textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
 | |
|   }
 | |
| 
 | |
|   return NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
 | |
| {
 | |
|   if (dir == nsITextControlFrame::eNone) {
 | |
|     aDirection.AssignLiteral("none");
 | |
|   } else if (dir == nsITextControlFrame::eForward) {
 | |
|     aDirection.AssignLiteral("forward");
 | |
|   } else if (dir == nsITextControlFrame::eBackward) {
 | |
|     aDirection.AssignLiteral("backward");
 | |
|   } else {
 | |
|     NS_NOTREACHED("Invalid SelectionDirection value");
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   GetSelectionDirection(aDirection, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aError)
 | |
| {
 | |
|   nsresult rv = NS_ERROR_FAILURE;
 | |
|   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 | |
|   nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
 | |
|   if (textControlFrame) {
 | |
|     nsITextControlFrame::SelectionDirection dir;
 | |
|     rv = textControlFrame->GetSelectionRange(nullptr, nullptr, &dir);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       DirectionToName(dir, aDirection);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     if (mState.IsSelectionCached()) {
 | |
|       DirectionToName(mState.GetSelectionProperties().mDirection, aDirection);
 | |
|       return;
 | |
|     }
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   SetSelectionDirection(aDirection, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection, ErrorResult& aError)
 | |
| {
 | |
|   if (mState.IsSelectionCached()) {
 | |
|     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eNone;
 | |
|     if (aDirection.EqualsLiteral("forward")) {
 | |
|       dir = nsITextControlFrame::eForward;
 | |
|     } else if (aDirection.EqualsLiteral("backward")) {
 | |
|       dir = nsITextControlFrame::eBackward;
 | |
|     }
 | |
|     mState.GetSelectionProperties().mDirection = dir;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   int32_t start, end;
 | |
|   nsresult rv = GetSelectionRange(&start, &end);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     rv = SetSelectionRange(start, end, aDirection);
 | |
|   }
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetSelectionRange(int32_t aSelectionStart,
 | |
|                                        int32_t aSelectionEnd,
 | |
|                                        const nsAString& aDirection)
 | |
| {
 | |
|   ErrorResult error;
 | |
|   Optional<nsAString> dir;
 | |
|   dir = &aDirection;
 | |
|   SetSelectionRange(aSelectionStart, aSelectionEnd, dir, error);
 | |
|   return error.StealNSResult();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetSelectionRange(uint32_t aSelectionStart,
 | |
|                                        uint32_t aSelectionEnd,
 | |
|                                        const Optional<nsAString>& aDirection,
 | |
|                                        ErrorResult& aError)
 | |
| {
 | |
|   nsresult rv = NS_ERROR_FAILURE;
 | |
|   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
 | |
|   nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
 | |
|   if (textControlFrame) {
 | |
|     // Default to forward, even if not specified.
 | |
|     // Note that we don't currently support directionless selections, so
 | |
|     // "none" is treated like "forward".
 | |
|     nsITextControlFrame::SelectionDirection dir = nsITextControlFrame::eForward;
 | |
|     if (aDirection.WasPassed() && aDirection.Value().EqualsLiteral("backward")) {
 | |
|       dir = nsITextControlFrame::eBackward;
 | |
|     }
 | |
| 
 | |
|     rv = textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd, dir);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       rv = textControlFrame->ScrollSelectionIntoView();
 | |
|       RefPtr<AsyncEventDispatcher> asyncDispatcher =
 | |
|         new AsyncEventDispatcher(this, NS_LITERAL_STRING("select"),
 | |
|                                  true, false);
 | |
|       asyncDispatcher->PostDOMEvent();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aError.Throw(rv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
 | |
|                                   ErrorResult& aRv)
 | |
| {
 | |
|   int32_t start, end;
 | |
|   aRv = GetSelectionRange(&start, &end);
 | |
|   if (aRv.Failed()) {
 | |
|     if (mState.IsSelectionCached()) {
 | |
|       start = mState.GetSelectionProperties().mStart;
 | |
|       end = mState.GetSelectionProperties().mEnd;
 | |
|       aRv = NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SetRangeText(aReplacement, start, end, mozilla::dom::SelectionMode::Preserve,
 | |
|                aRv, start, end);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
 | |
|                                   uint32_t aStart, uint32_t aEnd,
 | |
|                                   const SelectionMode& aSelectMode,
 | |
|                                   ErrorResult& aRv, int32_t aSelectionStart,
 | |
|                                   int32_t aSelectionEnd)
 | |
| {
 | |
|   if (aStart > aEnd) {
 | |
|     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsAutoString value;
 | |
|   GetValueInternal(value, false);
 | |
|   uint32_t inputValueLength = value.Length();
 | |
| 
 | |
|   if (aStart > inputValueLength) {
 | |
|     aStart = inputValueLength;
 | |
|   }
 | |
| 
 | |
|   if (aEnd > inputValueLength) {
 | |
|     aEnd = inputValueLength;
 | |
|   }
 | |
| 
 | |
|   if (aSelectionStart == -1 && aSelectionEnd == -1) {
 | |
|     aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd);
 | |
|     if (aRv.Failed()) {
 | |
|       if (mState.IsSelectionCached()) {
 | |
|         aSelectionStart = mState.GetSelectionProperties().mStart;
 | |
|         aSelectionEnd = mState.GetSelectionProperties().mEnd;
 | |
|         aRv = NS_OK;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aStart <= aEnd) {
 | |
|     value.Replace(aStart, aEnd - aStart, aReplacement);
 | |
|     nsresult rv =
 | |
|       SetValueInternal(value, nsTextEditorState::eSetValue_ByContent);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       aRv.Throw(rv);
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   uint32_t newEnd = aStart + aReplacement.Length();
 | |
|   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
 | |
| 
 | |
|   switch (aSelectMode) {
 | |
|     case mozilla::dom::SelectionMode::Select:
 | |
|     {
 | |
|       aSelectionStart = aStart;
 | |
|       aSelectionEnd = newEnd;
 | |
|     }
 | |
|     break;
 | |
|     case mozilla::dom::SelectionMode::Start:
 | |
|     {
 | |
|       aSelectionStart = aSelectionEnd = aStart;
 | |
|     }
 | |
|     break;
 | |
|     case mozilla::dom::SelectionMode::End:
 | |
|     {
 | |
|       aSelectionStart = aSelectionEnd = newEnd;
 | |
|     }
 | |
|     break;
 | |
|     case mozilla::dom::SelectionMode::Preserve:
 | |
|     {
 | |
|       if ((uint32_t)aSelectionStart > aEnd) {
 | |
|         aSelectionStart += delta;
 | |
|       } else if ((uint32_t)aSelectionStart > aStart) {
 | |
|         aSelectionStart = aStart;
 | |
|       }
 | |
| 
 | |
|       if ((uint32_t)aSelectionEnd > aEnd) {
 | |
|         aSelectionEnd += delta;
 | |
|       } else if ((uint32_t)aSelectionEnd > aStart) {
 | |
|         aSelectionEnd = newEnd;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
|     default:
 | |
|       MOZ_CRASH("Unknown mode!");
 | |
|   }
 | |
| 
 | |
|   Optional<nsAString> direction;
 | |
|   SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::Reset()
 | |
| {
 | |
|   nsresult rv;
 | |
| 
 | |
|   // To get the initial spellchecking, reset value to
 | |
|   // empty string before setting the default value.
 | |
|   rv = SetValue(EmptyString());
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
|   nsAutoString resetVal;
 | |
|   GetDefaultValue(resetVal);
 | |
|   rv = SetValue(resetVal);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   SetValueChanged(false);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
 | |
| {
 | |
|   // Disabled elements don't submit
 | |
|   if (IsDisabled()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the name (if no name, no submit)
 | |
|   //
 | |
|   nsAutoString name;
 | |
|   GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
 | |
|   if (name.IsEmpty()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the value
 | |
|   //
 | |
|   nsAutoString value;
 | |
|   GetValueInternal(value, false);
 | |
| 
 | |
|   //
 | |
|   // Submit
 | |
|   //
 | |
|   return aFormSubmission->AddNameValuePair(name, value);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SaveState()
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   // Only save if value != defaultValue (bug 62713)
 | |
|   nsPresState *state = nullptr;
 | |
|   if (mValueChanged) {
 | |
|     state = GetPrimaryPresState();
 | |
|     if (state) {
 | |
|       nsAutoString value;
 | |
|       GetValueInternal(value, true);
 | |
| 
 | |
|       rv = nsLinebreakConverter::ConvertStringLineBreaks(
 | |
|                value,
 | |
|                nsLinebreakConverter::eLinebreakPlatform,
 | |
|                nsLinebreakConverter::eLinebreakContent);
 | |
| 
 | |
|       if (NS_FAILED(rv)) {
 | |
|         NS_ERROR("Converting linebreaks failed!");
 | |
|         return rv;
 | |
|       }
 | |
| 
 | |
|       nsCOMPtr<nsISupportsString> pState =
 | |
|         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
 | |
|       if (!pState) {
 | |
|         return NS_ERROR_OUT_OF_MEMORY;
 | |
|       }
 | |
|       pState->SetData(value);
 | |
|       state->SetStateProperty(pState);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mDisabledChanged) {
 | |
|     if (!state) {
 | |
|       state = GetPrimaryPresState();
 | |
|       rv = NS_OK;
 | |
|     }
 | |
|     if (state) {
 | |
|       // We do not want to save the real disabled state but the disabled
 | |
|       // attribute.
 | |
|       state->SetDisabled(HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::RestoreState(nsPresState* aState)
 | |
| {
 | |
|   nsCOMPtr<nsISupportsString> state
 | |
|     (do_QueryInterface(aState->GetStateProperty()));
 | |
|   
 | |
|   if (state) {
 | |
|     nsAutoString data;
 | |
|     state->GetData(data);
 | |
|     nsresult rv = SetValue(data);
 | |
|     NS_ENSURE_SUCCESS(rv, false);
 | |
|   }
 | |
| 
 | |
|   if (aState->IsDisabledSet()) {
 | |
|     SetDisabled(aState->GetDisabled());
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| EventStates
 | |
| HTMLTextAreaElement::IntrinsicState() const
 | |
| {
 | |
|   EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
 | |
| 
 | |
|   if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
 | |
|     state |= NS_EVENT_STATE_REQUIRED;
 | |
|   } else {
 | |
|     state |= NS_EVENT_STATE_OPTIONAL;
 | |
|   }
 | |
| 
 | |
|   if (IsCandidateForConstraintValidation()) {
 | |
|     if (IsValid()) {
 | |
|       state |= NS_EVENT_STATE_VALID;
 | |
|     } else {
 | |
|       state |= NS_EVENT_STATE_INVALID;
 | |
|       // :-moz-ui-invalid always apply if the element suffers from a custom
 | |
|       // error and never applies if novalidate is set on the form owner.
 | |
|       if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
 | |
|           (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
 | |
|            (mCanShowInvalidUI && ShouldShowValidityUI()))) {
 | |
|         state |= NS_EVENT_STATE_MOZ_UI_INVALID;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // :-moz-ui-valid applies if all the following are true:
 | |
|     // 1. The element is not focused, or had either :-moz-ui-valid or
 | |
|     //    :-moz-ui-invalid applying before it was focused ;
 | |
|     // 2. The element is either valid or isn't allowed to have
 | |
|     //    :-moz-ui-invalid applying ;
 | |
|     // 3. The element has no form owner or its form owner doesn't have the
 | |
|     //    novalidate attribute set ;
 | |
|     // 4. The element has already been modified or the user tried to submit the
 | |
|     //    form owner while invalid.
 | |
|     if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
 | |
|         (mCanShowValidUI && ShouldShowValidityUI() &&
 | |
|          (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
 | |
|                         !mCanShowInvalidUI)))) {
 | |
|       state |= NS_EVENT_STATE_MOZ_UI_VALID;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return state;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
 | |
|                                 nsIContent* aBindingParent,
 | |
|                                 bool aCompileEventHandlers)
 | |
| {
 | |
|   nsresult rv = nsGenericHTMLFormElementWithState::BindToTree(aDocument, aParent,
 | |
|                                                               aBindingParent,
 | |
|                                                               aCompileEventHandlers);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // If there is a disabled fieldset in the parent chain, the element is now
 | |
|   // barred from constraint validation and can't suffer from value missing.
 | |
|   UpdateValueMissingValidityState();
 | |
|   UpdateBarredFromConstraintValidation();
 | |
| 
 | |
|   // And now make sure our state is up to date
 | |
|   UpdateState(false);
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::UnbindFromTree(bool aDeep, bool aNullParent)
 | |
| {
 | |
|   nsGenericHTMLFormElementWithState::UnbindFromTree(aDeep, aNullParent);
 | |
| 
 | |
|   // We might be no longer disabled because of parent chain changed.
 | |
|   UpdateValueMissingValidityState();
 | |
|   UpdateBarredFromConstraintValidation();
 | |
| 
 | |
|   // And now make sure our state is up to date
 | |
|   UpdateState(false);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
 | |
|                                    nsAttrValueOrString* aValue,
 | |
|                                    bool aNotify)
 | |
| {
 | |
|   if (aNotify && aName == nsGkAtoms::disabled &&
 | |
|       aNameSpaceID == kNameSpaceID_None) {
 | |
|     mDisabledChanged = true;
 | |
|   }
 | |
| 
 | |
|   return nsGenericHTMLFormElementWithState::BeforeSetAttr(aNameSpaceID, aName,
 | |
|                                                  aValue, aNotify);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::CharacterDataChanged(nsIDocument* aDocument,
 | |
|                                           nsIContent* aContent,
 | |
|                                           CharacterDataChangeInfo* aInfo)
 | |
| {
 | |
|   ContentChanged(aContent);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::ContentAppended(nsIDocument* aDocument,
 | |
|                                      nsIContent* aContainer,
 | |
|                                      nsIContent* aFirstNewContent,
 | |
|                                      int32_t /* unused */)
 | |
| {
 | |
|   ContentChanged(aFirstNewContent);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::ContentInserted(nsIDocument* aDocument,
 | |
|                                      nsIContent* aContainer,
 | |
|                                      nsIContent* aChild,
 | |
|                                      int32_t /* unused */)
 | |
| {
 | |
|   ContentChanged(aChild);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::ContentRemoved(nsIDocument* aDocument,
 | |
|                                     nsIContent* aContainer,
 | |
|                                     nsIContent* aChild,
 | |
|                                     int32_t aIndexInContainer,
 | |
|                                     nsIContent* aPreviousSibling)
 | |
| {
 | |
|   ContentChanged(aChild);
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::ContentChanged(nsIContent* aContent)
 | |
| {
 | |
|   if (!mValueChanged && mDoneAddingChildren &&
 | |
|       nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
 | |
|     // Hard to say what the reset can trigger, so be safe pending
 | |
|     // further auditing.
 | |
|     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
 | |
|     Reset();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
 | |
|                                   const nsAttrValue* aValue, bool aNotify)
 | |
| {
 | |
|   if (aNameSpaceID == kNameSpaceID_None) {
 | |
|     if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
 | |
|         aName == nsGkAtoms::readonly) {
 | |
|       UpdateValueMissingValidityState();
 | |
| 
 | |
|       // This *has* to be called *after* validity has changed.
 | |
|       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
 | |
|         UpdateBarredFromConstraintValidation();
 | |
|       }
 | |
|     } else if (aName == nsGkAtoms::maxlength) {
 | |
|       UpdateTooLongValidityState();
 | |
|     }
 | |
| 
 | |
|     UpdateState(aNotify);
 | |
|   }
 | |
| 
 | |
|   return nsGenericHTMLFormElementWithState::AfterSetAttr(aNameSpaceID, aName, aValue,
 | |
|                                                          aNotify);
 | |
|   }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::CopyInnerTo(Element* aDest)
 | |
| {
 | |
|   nsresult rv = nsGenericHTMLFormElementWithState::CopyInnerTo(aDest);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   if (aDest->OwnerDoc()->IsStaticDocument()) {
 | |
|     nsAutoString value;
 | |
|     GetValueInternal(value, true);
 | |
|     return static_cast<HTMLTextAreaElement*>(aDest)->SetValue(value);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsMutable() const
 | |
| {
 | |
|   return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsValueEmpty() const
 | |
| {
 | |
|   nsAutoString value;
 | |
|   GetValueInternal(value, true);
 | |
| 
 | |
|   return value.IsEmpty();
 | |
| }
 | |
| 
 | |
| // nsIConstraintValidation
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| HTMLTextAreaElement::SetCustomValidity(const nsAString& aError)
 | |
| {
 | |
|   nsIConstraintValidation::SetCustomValidity(aError);
 | |
| 
 | |
|   UpdateState(true);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsTooLong()
 | |
| {
 | |
|   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::maxlength) || !mValueChanged) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   int32_t maxLength = -1;
 | |
|   GetMaxLength(&maxLength);
 | |
| 
 | |
|   // Maxlength of -1 means parsing error.
 | |
|   if (maxLength == -1) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   int32_t textLength = -1;
 | |
|   GetTextLength(&textLength);
 | |
| 
 | |
|   return textLength > maxLength;
 | |
| }
 | |
| 
 | |
| bool
 | |
| HTMLTextAreaElement::IsValueMissing() const
 | |
| {
 | |
|   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return IsValueEmpty();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::UpdateTooLongValidityState()
 | |
| {
 | |
|   // TODO: this code will be re-enabled with bug 613016 and bug 613019.
 | |
| #if 0
 | |
|   SetValidityState(VALIDITY_STATE_TOO_LONG, IsTooLong());
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::UpdateValueMissingValidityState()
 | |
| {
 | |
|   SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::UpdateBarredFromConstraintValidation()
 | |
| {
 | |
|   SetBarredFromConstraintValidation(HasAttr(kNameSpaceID_None,
 | |
|                                             nsGkAtoms::readonly) ||
 | |
|                                     IsDisabled());
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| HTMLTextAreaElement::GetValidationMessage(nsAString& aValidationMessage,
 | |
|                                           ValidityStateType aType)
 | |
| {
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   switch (aType)
 | |
|   {
 | |
|     case VALIDITY_STATE_TOO_LONG:
 | |
|       {
 | |
|         nsXPIDLString message;
 | |
|         int32_t maxLength = -1;
 | |
|         int32_t textLength = -1;
 | |
|         nsAutoString strMaxLength;
 | |
|         nsAutoString strTextLength;
 | |
| 
 | |
|         GetMaxLength(&maxLength);
 | |
|         GetTextLength(&textLength);
 | |
| 
 | |
|         strMaxLength.AppendInt(maxLength);
 | |
|         strTextLength.AppendInt(textLength);
 | |
| 
 | |
|         const char16_t* params[] = { strMaxLength.get(), strTextLength.get() };
 | |
|         rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
 | |
|                                                    "FormValidationTextTooLong",
 | |
|                                                    params, message);
 | |
|         aValidationMessage = message;
 | |
|       }
 | |
|       break;
 | |
|     case VALIDITY_STATE_VALUE_MISSING:
 | |
|       {
 | |
|         nsXPIDLString message;
 | |
|         rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
 | |
|                                                 "FormValidationValueMissing",
 | |
|                                                 message);
 | |
|         aValidationMessage = message;
 | |
|       }
 | |
|       break;
 | |
|     default:
 | |
|       rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::IsSingleLineTextControl() const
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::IsTextArea() const
 | |
| {
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::IsPlainTextControl() const
 | |
| {
 | |
|   // need to check our HTML attribute and/or CSS.
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::IsPasswordTextControl() const
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(int32_t)
 | |
| HTMLTextAreaElement::GetCols()
 | |
| {
 | |
|   return Cols();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(int32_t)
 | |
| HTMLTextAreaElement::GetWrapCols()
 | |
| {
 | |
|   // wrap=off means -1 for wrap width no matter what cols is
 | |
|   nsHTMLTextWrap wrapProp;
 | |
|   nsITextControlElement::GetWrapPropertyEnum(this, wrapProp);
 | |
|   if (wrapProp == nsITextControlElement::eHTMLTextWrap_Off) {
 | |
|     // do not wrap when wrap=off
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   // Otherwise we just wrap at the given number of columns
 | |
|   return GetCols();
 | |
| }
 | |
| 
 | |
| 
 | |
| NS_IMETHODIMP_(int32_t)
 | |
| HTMLTextAreaElement::GetRows()
 | |
| {
 | |
|   const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::rows);
 | |
|   if (attr && attr->Type() == nsAttrValue::eInteger) {
 | |
|     int32_t rows = attr->GetIntegerValue();
 | |
|     return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
 | |
|   }
 | |
| 
 | |
|   return DEFAULT_ROWS_TEXTAREA;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::GetDefaultValueFromContent(nsAString& aValue)
 | |
| {
 | |
|   GetDefaultValue(aValue);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::ValueChanged() const
 | |
| {
 | |
|   return mValueChanged;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::GetTextEditorValue(nsAString& aValue,
 | |
|                                         bool aIgnoreWrap) const
 | |
| {
 | |
|   mState.GetValue(aValue, aIgnoreWrap);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::InitializeKeyboardEventListeners()
 | |
| {
 | |
|   mState.InitializeKeyboardEventListeners();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(void)
 | |
| HTMLTextAreaElement::OnValueChanged(bool aNotify)
 | |
| {
 | |
|   // Update the validity state
 | |
|   bool validBefore = IsValid();
 | |
|   UpdateTooLongValidityState();
 | |
|   UpdateValueMissingValidityState();
 | |
| 
 | |
|   if (validBefore != IsValid()) {
 | |
|     UpdateState(aNotify);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP_(bool)
 | |
| HTMLTextAreaElement::HasCachedSelection()
 | |
| {
 | |
|   return mState.IsSelectionCached();
 | |
| }
 | |
| 
 | |
| void
 | |
| HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
 | |
| {
 | |
|   UpdateValueMissingValidityState();
 | |
|   UpdateBarredFromConstraintValidation();
 | |
| 
 | |
|   nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
 | |
| }
 | |
| 
 | |
| JSObject*
 | |
| HTMLTextAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 | |
| {
 | |
|   return HTMLTextAreaElementBinding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| } // namespace dom
 | |
| } // namespace mozilla
 | 
