1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsIConstraintValidation.h"
10 #include "nsGenericHTMLElement.h"
11 #include "mozilla/ErrorResult.h"
12 #include "mozilla/dom/CustomEvent.h"
13 #include "mozilla/dom/HTMLFormElement.h"
14 #include "mozilla/dom/HTMLFieldSetElement.h"
15 #include "mozilla/dom/HTMLInputElement.h"
16 #include "mozilla/dom/ValidityState.h"
17 #include "nsIFormControl.h"
18 #include "nsContentUtils.h"
20 #include "nsIFormSubmitObserver.h"
21 #include "nsIObserverService.h"
23 const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage
= 256;
25 using namespace mozilla
;
26 using namespace mozilla::dom
;
28 nsIConstraintValidation::nsIConstraintValidation()
29 : mValidityBitField(0)
30 // By default, all elements are subjects to constraint validation.
32 mBarredFromConstraintValidation(false) {}
34 nsIConstraintValidation::~nsIConstraintValidation() {}
36 mozilla::dom::ValidityState
* nsIConstraintValidation::Validity() {
38 mValidity
= new mozilla::dom::ValidityState(this);
44 void nsIConstraintValidation::GetValidationMessage(
45 nsAString
& aValidationMessage
, ErrorResult
& aError
) {
46 aValidationMessage
.Truncate();
48 if (IsCandidateForConstraintValidation() && !IsValid()) {
49 if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR
)) {
50 aValidationMessage
.Assign(mCustomValidity
);
51 if (aValidationMessage
.Length() > sContentSpecifiedMaxLengthMessage
) {
52 aValidationMessage
.Truncate(sContentSpecifiedMaxLengthMessage
);
54 } else if (GetValidityState(VALIDITY_STATE_TOO_LONG
)) {
55 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_TOO_LONG
);
56 } else if (GetValidityState(VALIDITY_STATE_TOO_SHORT
)) {
57 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_TOO_SHORT
);
58 } else if (GetValidityState(VALIDITY_STATE_VALUE_MISSING
)) {
59 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_VALUE_MISSING
);
60 } else if (GetValidityState(VALIDITY_STATE_TYPE_MISMATCH
)) {
61 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_TYPE_MISMATCH
);
62 } else if (GetValidityState(VALIDITY_STATE_PATTERN_MISMATCH
)) {
63 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_PATTERN_MISMATCH
);
64 } else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW
)) {
65 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_RANGE_OVERFLOW
);
66 } else if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW
)) {
67 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_RANGE_UNDERFLOW
);
68 } else if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH
)) {
69 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_STEP_MISMATCH
);
70 } else if (GetValidityState(VALIDITY_STATE_BAD_INPUT
)) {
71 GetValidationMessage(aValidationMessage
, VALIDITY_STATE_BAD_INPUT
);
73 // There should not be other validity states.
74 aError
.Throw(NS_ERROR_UNEXPECTED
);
78 aValidationMessage
.Truncate();
82 bool nsIConstraintValidation::CheckValidity() {
83 if (!IsCandidateForConstraintValidation() || IsValid()) {
87 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(this);
89 "This class should be inherited by HTML elements only!");
91 nsContentUtils::DispatchTrustedEvent(content
->OwnerDoc(), content
,
92 NS_LITERAL_STRING("invalid"),
93 CanBubble::eNo
, Cancelable::eYes
);
97 nsresult
nsIConstraintValidation::CheckValidity(bool* aValidity
) {
98 NS_ENSURE_ARG_POINTER(aValidity
);
100 *aValidity
= CheckValidity();
105 bool nsIConstraintValidation::ReportValidity() {
106 if (!IsCandidateForConstraintValidation() || IsValid()) {
110 nsCOMPtr
<Element
> element
= do_QueryInterface(this);
111 MOZ_ASSERT(element
, "This class should be inherited by HTML elements only!");
113 bool defaultAction
= true;
114 nsContentUtils::DispatchTrustedEvent(
115 element
->OwnerDoc(), element
, NS_LITERAL_STRING("invalid"),
116 CanBubble::eNo
, Cancelable::eYes
, &defaultAction
);
117 if (!defaultAction
) {
121 AutoTArray
<RefPtr
<Element
>, 1> invalidElements
;
122 invalidElements
.AppendElement(element
);
125 if (!jsapi
.Init(element
->GetOwnerGlobal())) {
128 JS::Rooted
<JS::Value
> detail(jsapi
.cx());
129 if (!ToJSValue(jsapi
.cx(), invalidElements
, &detail
)) {
133 RefPtr
<CustomEvent
> event
=
134 NS_NewDOMCustomEvent(element
->OwnerDoc(), nullptr, nullptr);
135 event
->InitCustomEvent(jsapi
.cx(), NS_LITERAL_STRING("MozInvalidForm"),
136 /* CanBubble */ true,
137 /* Cancelable */ true, detail
);
138 event
->SetTrusted(true);
139 event
->WidgetEventPtr()->mFlags
.mOnlyChromeDispatch
= true;
141 element
->DispatchEvent(*event
);
143 nsCOMPtr
<nsIObserverService
> service
=
144 mozilla::services::GetObserverService();
146 NS_WARNING("No observer service available!");
150 nsCOMPtr
<nsISimpleEnumerator
> theEnum
;
151 nsresult rv
= service
->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT
,
152 getter_AddRefs(theEnum
));
154 // Return true on error here because that's what we always did
155 NS_ENSURE_SUCCESS(rv
, true);
157 bool hasObserver
= false;
158 rv
= theEnum
->HasMoreElements(&hasObserver
);
160 NS_ENSURE_SUCCESS(rv
, true);
161 nsCOMPtr
<nsISupports
> inst
;
162 nsCOMPtr
<nsIFormSubmitObserver
> observer
;
164 while (NS_SUCCEEDED(theEnum
->HasMoreElements(&more
)) && more
) {
165 theEnum
->GetNext(getter_AddRefs(inst
));
166 observer
= do_QueryInterface(inst
);
169 observer
->NotifyInvalidSubmit(nullptr, invalidElements
);
173 if (element
->IsHTMLElement(nsGkAtoms::input
) &&
174 // We don't use nsContentUtils::IsFocusedContent here, because it doesn't
175 // really do what we want for number controls: it's true for the
176 // anonymous textnode inside, but not the number control itself. We can
177 // use the focus state, though, because that gets synced to the number
178 // control by the anonymous text control.
179 element
->State().HasState(NS_EVENT_STATE_FOCUS
)) {
180 HTMLInputElement
* inputElement
= HTMLInputElement::FromNode(element
);
181 inputElement
->UpdateValidityUIBits(true);
184 element
->UpdateState(true);
188 void nsIConstraintValidation::SetValidityState(ValidityStateType aState
,
190 bool previousValidity
= IsValid();
193 mValidityBitField
|= aState
;
195 mValidityBitField
&= ~aState
;
198 // Inform the form and fieldset elements if our validity has changed.
199 if (previousValidity
!= IsValid() && IsCandidateForConstraintValidation()) {
200 nsCOMPtr
<nsIFormControl
> formCtrl
= do_QueryInterface(this);
201 NS_ASSERTION(formCtrl
, "This interface should be used by form elements!");
203 HTMLFormElement
* form
=
204 static_cast<HTMLFormElement
*>(formCtrl
->GetFormElement());
206 form
->UpdateValidity(IsValid());
208 HTMLFieldSetElement
* fieldSet
= formCtrl
->GetFieldSet();
210 fieldSet
->UpdateValidity(IsValid());
215 void nsIConstraintValidation::SetCustomValidity(const nsAString
& aError
) {
216 mCustomValidity
.Assign(aError
);
217 SetValidityState(VALIDITY_STATE_CUSTOM_ERROR
, !mCustomValidity
.IsEmpty());
220 void nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred
) {
221 bool previousBarred
= mBarredFromConstraintValidation
;
223 mBarredFromConstraintValidation
= aBarred
;
225 // Inform the form and fieldset elements if our status regarding constraint
226 // validation is going to change.
227 if (!IsValid() && previousBarred
!= mBarredFromConstraintValidation
) {
228 nsCOMPtr
<nsIFormControl
> formCtrl
= do_QueryInterface(this);
229 NS_ASSERTION(formCtrl
, "This interface should be used by form elements!");
231 // If the element is going to be barred from constraint validation, we can
232 // inform the form and fieldset that we are now valid. Otherwise, we are now
234 HTMLFormElement
* form
=
235 static_cast<HTMLFormElement
*>(formCtrl
->GetFormElement());
237 form
->UpdateValidity(aBarred
);
239 HTMLFieldSetElement
* fieldSet
= formCtrl
->GetFieldSet();
241 fieldSet
->UpdateValidity(aBarred
);