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