Bug 1560374 - Set testharness and reftest web-platform-tests to Tier-1; r=jmaher...
[gecko.git] / dom / html / nsIConstraintValidation.cpp
blob4efac0032d2fa815b5c3311a52d79c30c7ff0d24
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"
9 #include "nsAString.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() {
37 if (!mValidity) {
38 mValidity = new mozilla::dom::ValidityState(this);
41 return mValidity;
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);
72 } else {
73 // There should not be other validity states.
74 aError.Throw(NS_ERROR_UNEXPECTED);
75 return;
77 } else {
78 aValidationMessage.Truncate();
82 bool nsIConstraintValidation::CheckValidity() {
83 if (!IsCandidateForConstraintValidation() || IsValid()) {
84 return true;
87 nsCOMPtr<nsIContent> content = do_QueryInterface(this);
88 NS_ASSERTION(content,
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);
94 return false;
97 nsresult nsIConstraintValidation::CheckValidity(bool* aValidity) {
98 NS_ENSURE_ARG_POINTER(aValidity);
100 *aValidity = CheckValidity();
102 return NS_OK;
105 bool nsIConstraintValidation::ReportValidity() {
106 if (!IsCandidateForConstraintValidation() || IsValid()) {
107 return true;
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) {
118 return false;
121 AutoTArray<RefPtr<Element>, 1> invalidElements;
122 invalidElements.AppendElement(element);
124 AutoJSAPI jsapi;
125 if (!jsapi.Init(element->GetOwnerGlobal())) {
126 return false;
128 JS::Rooted<JS::Value> detail(jsapi.cx());
129 if (!ToJSValue(jsapi.cx(), invalidElements, &detail)) {
130 return false;
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();
145 if (!service) {
146 NS_WARNING("No observer service available!");
147 return true;
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;
163 bool more = true;
164 while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
165 theEnum->GetNext(getter_AddRefs(inst));
166 observer = do_QueryInterface(inst);
168 if (observer) {
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);
185 return false;
188 void nsIConstraintValidation::SetValidityState(ValidityStateType aState,
189 bool aValue) {
190 bool previousValidity = IsValid();
192 if (aValue) {
193 mValidityBitField |= aState;
194 } else {
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());
205 if (form) {
206 form->UpdateValidity(IsValid());
208 HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
209 if (fieldSet) {
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
233 // invalid.
234 HTMLFormElement* form =
235 static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
236 if (form) {
237 form->UpdateValidity(aBarred);
239 HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
240 if (fieldSet) {
241 fieldSet->UpdateValidity(aBarred);