1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsIConstraintValidation.h"
8
9 #include "nsGenericHTMLElement.h"
10 #include "mozilla/dom/CustomEvent.h"
11 #include "mozilla/dom/HTMLFormElement.h"
12 #include "mozilla/dom/HTMLFieldSetElement.h"
13 #include "mozilla/dom/HTMLInputElement.h"
14 #include "mozilla/dom/ValidityState.h"
15 #include "nsIFormControl.h"
16 #include "nsISimpleEnumerator.h"
17 #include "nsContentUtils.h"
18
19 const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
20
21 using namespace mozilla;
22 using namespace mozilla::dom;
23
nsIConstraintValidation()24 nsIConstraintValidation::nsIConstraintValidation()
25 : mValidityBitField(0)
26 // By default, all elements are subjects to constraint validation.
27 ,
28 mBarredFromConstraintValidation(false) {}
29
30 nsIConstraintValidation::~nsIConstraintValidation() = default;
31
Validity()32 mozilla::dom::ValidityState* nsIConstraintValidation::Validity() {
33 if (!mValidity) {
34 mValidity = new mozilla::dom::ValidityState(this);
35 }
36
37 return mValidity;
38 }
39
CheckValidity(nsIContent & aEventTarget,bool * aEventDefaultAction)40 bool nsIConstraintValidation::CheckValidity(nsIContent& aEventTarget,
41 bool* aEventDefaultAction) {
42 if (!IsCandidateForConstraintValidation() || IsValid()) {
43 return true;
44 }
45
46 nsContentUtils::DispatchTrustedEvent(
47 aEventTarget.OwnerDoc(), &aEventTarget, u"invalid"_ns, CanBubble::eNo,
48 Cancelable::eYes, Composed::eDefault, aEventDefaultAction);
49 return false;
50 }
51
ReportValidity()52 bool nsIConstraintValidation::ReportValidity() {
53 nsCOMPtr<Element> element = do_QueryInterface(this);
54 MOZ_ASSERT(element, "This class should be inherited by HTML elements only!");
55
56 bool defaultAction = true;
57 if (CheckValidity(*element, &defaultAction)) {
58 return true;
59 }
60
61 if (!defaultAction) {
62 return false;
63 }
64
65 AutoTArray<RefPtr<Element>, 1> invalidElements;
66 invalidElements.AppendElement(element);
67
68 AutoJSAPI jsapi;
69 if (!jsapi.Init(element->GetOwnerGlobal())) {
70 return false;
71 }
72 JS::Rooted<JS::Value> detail(jsapi.cx());
73 if (!ToJSValue(jsapi.cx(), invalidElements, &detail)) {
74 return false;
75 }
76
77 RefPtr<CustomEvent> event =
78 NS_NewDOMCustomEvent(element->OwnerDoc(), nullptr, nullptr);
79 event->InitCustomEvent(jsapi.cx(), u"MozInvalidForm"_ns,
80 /* CanBubble */ true,
81 /* Cancelable */ true, detail);
82 event->SetTrusted(true);
83 event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
84
85 element->DispatchEvent(*event);
86
87 auto* inputElement = HTMLInputElement::FromNode(element);
88 if (inputElement && inputElement->State().HasState(NS_EVENT_STATE_FOCUS)) {
89 inputElement->UpdateValidityUIBits(true);
90 }
91
92 element->UpdateState(true);
93 return false;
94 }
95
SetValidityState(ValidityStateType aState,bool aValue)96 void nsIConstraintValidation::SetValidityState(ValidityStateType aState,
97 bool aValue) {
98 bool previousValidity = IsValid();
99
100 if (aValue) {
101 mValidityBitField |= aState;
102 } else {
103 mValidityBitField &= ~aState;
104 }
105
106 // Inform the form and fieldset elements if our validity has changed.
107 if (previousValidity != IsValid() && IsCandidateForConstraintValidation()) {
108 nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
109 NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
110
111 if (HTMLFormElement* form = formCtrl->GetForm()) {
112 form->UpdateValidity(IsValid());
113 }
114 HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
115 if (fieldSet) {
116 fieldSet->UpdateValidity(IsValid());
117 }
118 }
119 }
120
SetBarredFromConstraintValidation(bool aBarred)121 void nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred) {
122 bool previousBarred = mBarredFromConstraintValidation;
123
124 mBarredFromConstraintValidation = aBarred;
125
126 // Inform the form and fieldset elements if our status regarding constraint
127 // validation is going to change.
128 if (!IsValid() && previousBarred != mBarredFromConstraintValidation) {
129 nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
130 NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
131
132 // If the element is going to be barred from constraint validation, we can
133 // inform the form and fieldset that we are now valid. Otherwise, we are now
134 // invalid.
135 if (HTMLFormElement* form = formCtrl->GetForm()) {
136 form->UpdateValidity(aBarred);
137 }
138 HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
139 if (fieldSet) {
140 fieldSet->UpdateValidity(aBarred);
141 }
142 }
143 }
144