Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/nsIConstraintValidation.cpp
Line
Count
Source (jump to first uncovered line)
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 "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"
19
20
#include "nsIFormSubmitObserver.h"
21
#include "nsIObserverService.h"
22
23
const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
24
25
using namespace mozilla;
26
using namespace mozilla::dom;
27
28
nsIConstraintValidation::nsIConstraintValidation()
29
  : mValidityBitField(0)
30
  // By default, all elements are subjects to constraint validation.
31
  , mBarredFromConstraintValidation(false)
32
0
{
33
0
}
34
35
nsIConstraintValidation::~nsIConstraintValidation()
36
0
{
37
0
}
38
39
mozilla::dom::ValidityState*
40
nsIConstraintValidation::Validity()
41
0
{
42
0
  if (!mValidity) {
43
0
    mValidity = new mozilla::dom::ValidityState(this);
44
0
  }
45
0
46
0
  return mValidity;
47
0
}
48
49
void
50
nsIConstraintValidation::GetValidationMessage(nsAString& aValidationMessage,
51
                                              ErrorResult& aError)
52
0
{
53
0
  aValidationMessage.Truncate();
54
0
55
0
  if (IsCandidateForConstraintValidation() && !IsValid()) {
56
0
    nsCOMPtr<Element> element = do_QueryInterface(this);
57
0
    NS_ASSERTION(element, "This class should be inherited by HTML elements only!");
58
0
59
0
    nsAutoString authorMessage;
60
0
    element->GetAttr(kNameSpaceID_None, nsGkAtoms::x_moz_errormessage,
61
0
                     authorMessage);
62
0
63
0
    if (!authorMessage.IsEmpty()) {
64
0
      aValidationMessage.Assign(authorMessage);
65
0
      if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
66
0
        aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
67
0
      }
68
0
    } else if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
69
0
      aValidationMessage.Assign(mCustomValidity);
70
0
      if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
71
0
        aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
72
0
      }
73
0
    } else if (GetValidityState(VALIDITY_STATE_TOO_LONG)) {
74
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_TOO_LONG);
75
0
    } else if (GetValidityState(VALIDITY_STATE_TOO_SHORT)) {
76
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_TOO_SHORT);
77
0
    } else if (GetValidityState(VALIDITY_STATE_VALUE_MISSING)) {
78
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_VALUE_MISSING);
79
0
    } else if (GetValidityState(VALIDITY_STATE_TYPE_MISMATCH)) {
80
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_TYPE_MISMATCH);
81
0
    } else if (GetValidityState(VALIDITY_STATE_PATTERN_MISMATCH)) {
82
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_PATTERN_MISMATCH);
83
0
    } else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW)) {
84
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_OVERFLOW);
85
0
    } else if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW)) {
86
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_UNDERFLOW);
87
0
    } else if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH)) {
88
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_STEP_MISMATCH);
89
0
    } else if (GetValidityState(VALIDITY_STATE_BAD_INPUT)) {
90
0
      GetValidationMessage(aValidationMessage, VALIDITY_STATE_BAD_INPUT);
91
0
    } else {
92
0
      // There should not be other validity states.
93
0
      aError.Throw(NS_ERROR_UNEXPECTED);
94
0
      return;
95
0
    }
96
0
  } else {
97
0
    aValidationMessage.Truncate();
98
0
  }
99
0
}
100
101
bool
102
nsIConstraintValidation::CheckValidity()
103
0
{
104
0
  if (!IsCandidateForConstraintValidation() || IsValid()) {
105
0
    return true;
106
0
  }
107
0
108
0
  nsCOMPtr<nsIContent> content = do_QueryInterface(this);
109
0
  NS_ASSERTION(content, "This class should be inherited by HTML elements only!");
110
0
111
0
  nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(),
112
0
                                       content,
113
0
                                       NS_LITERAL_STRING("invalid"),
114
0
                                       CanBubble::eNo,
115
0
                                       Cancelable::eYes);
116
0
  return false;
117
0
}
118
119
nsresult
120
nsIConstraintValidation::CheckValidity(bool* aValidity)
121
0
{
122
0
  NS_ENSURE_ARG_POINTER(aValidity);
123
0
124
0
  *aValidity = CheckValidity();
125
0
126
0
  return NS_OK;
127
0
}
128
129
bool
130
nsIConstraintValidation::ReportValidity()
131
0
{
132
0
  if (!IsCandidateForConstraintValidation() || IsValid()) {
133
0
    return true;
134
0
  }
135
0
136
0
  nsCOMPtr<Element> element = do_QueryInterface(this);
137
0
  MOZ_ASSERT(element, "This class should be inherited by HTML elements only!");
138
0
139
0
  bool defaultAction = true;
140
0
  nsContentUtils::DispatchTrustedEvent(element->OwnerDoc(), element,
141
0
                                       NS_LITERAL_STRING("invalid"),
142
0
                                       CanBubble::eNo,
143
0
                                       Cancelable::eYes,
144
0
                                       &defaultAction);
145
0
  if (!defaultAction) {
146
0
    return false;
147
0
  }
148
0
149
0
  AutoTArray<RefPtr<Element>, 1> invalidElements;
150
0
  invalidElements.AppendElement(element);
151
0
152
0
  AutoJSAPI jsapi;
153
0
  if (!jsapi.Init(element->GetOwnerGlobal())) {
154
0
    return false;
155
0
  }
156
0
  JS::Rooted<JS::Value> detail(jsapi.cx());
157
0
  if (!ToJSValue(jsapi.cx(), invalidElements, &detail)) {
158
0
    return false;
159
0
  }
160
0
161
0
  RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(element->OwnerDoc(),
162
0
                                                   nullptr, nullptr);
163
0
  event->InitCustomEvent(jsapi.cx(),
164
0
                         NS_LITERAL_STRING("MozInvalidForm"),
165
0
                         /* CanBubble */ true,
166
0
                         /* Cancelable */ true,
167
0
                         detail);
168
0
  event->SetTrusted(true);
169
0
  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
170
0
171
0
  element->DispatchEvent(*event);
172
0
173
0
  nsCOMPtr<nsIObserverService> service =
174
0
    mozilla::services::GetObserverService();
175
0
  if (!service) {
176
0
    NS_WARNING("No observer service available!");
177
0
    return true;
178
0
  }
179
0
180
0
  nsCOMPtr<nsISimpleEnumerator> theEnum;
181
0
  nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
182
0
                                            getter_AddRefs(theEnum));
183
0
184
0
  // Return true on error here because that's what we always did
185
0
  NS_ENSURE_SUCCESS(rv, true);
186
0
187
0
  bool hasObserver = false;
188
0
  rv = theEnum->HasMoreElements(&hasObserver);
189
0
190
0
  NS_ENSURE_SUCCESS(rv, true);
191
0
  nsCOMPtr<nsISupports> inst;
192
0
  nsCOMPtr<nsIFormSubmitObserver> observer;
193
0
  bool more = true;
194
0
  while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
195
0
    theEnum->GetNext(getter_AddRefs(inst));
196
0
    observer = do_QueryInterface(inst);
197
0
198
0
    if (observer) {
199
0
      observer->NotifyInvalidSubmit(nullptr, invalidElements);
200
0
    }
201
0
  }
202
0
203
0
  if (element->IsHTMLElement(nsGkAtoms::input) &&
204
0
      // We don't use nsContentUtils::IsFocusedContent here, because it doesn't
205
0
      // really do what we want for number controls: it's true for the
206
0
      // anonymous textnode inside, but not the number control itself.  We can
207
0
      // use the focus state, though, because that gets synced to the number
208
0
      // control by the anonymous text control.
209
0
      element->State().HasState(NS_EVENT_STATE_FOCUS)) {
210
0
    HTMLInputElement* inputElement = HTMLInputElement::FromNode(element);
211
0
    inputElement->UpdateValidityUIBits(true);
212
0
  }
213
0
214
0
  element->UpdateState(true);
215
0
  return false;
216
0
}
217
218
void
219
nsIConstraintValidation::SetValidityState(ValidityStateType aState,
220
                                          bool aValue)
221
0
{
222
0
  bool previousValidity = IsValid();
223
0
224
0
  if (aValue) {
225
0
    mValidityBitField |= aState;
226
0
  } else {
227
0
    mValidityBitField &= ~aState;
228
0
  }
229
0
230
0
  // Inform the form and fieldset elements if our validity has changed.
231
0
  if (previousValidity != IsValid() && IsCandidateForConstraintValidation()) {
232
0
    nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
233
0
    NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
234
0
235
0
    HTMLFormElement* form =
236
0
      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
237
0
    if (form) {
238
0
      form->UpdateValidity(IsValid());
239
0
    }
240
0
    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
241
0
      if (fieldSet) {
242
0
      fieldSet->UpdateValidity(IsValid());
243
0
    }
244
0
  }
245
0
}
246
247
void
248
nsIConstraintValidation::SetCustomValidity(const nsAString& aError)
249
0
{
250
0
  mCustomValidity.Assign(aError);
251
0
  SetValidityState(VALIDITY_STATE_CUSTOM_ERROR, !mCustomValidity.IsEmpty());
252
0
}
253
254
void
255
nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred)
256
0
{
257
0
  bool previousBarred = mBarredFromConstraintValidation;
258
0
259
0
  mBarredFromConstraintValidation = aBarred;
260
0
261
0
  // Inform the form and fieldset elements if our status regarding constraint
262
0
  // validation is going to change.
263
0
  if (!IsValid() && previousBarred != mBarredFromConstraintValidation) {
264
0
    nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
265
0
    NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
266
0
267
0
    // If the element is going to be barred from constraint validation, we can
268
0
    // inform the form and fieldset that we are now valid. Otherwise, we are now
269
0
    // invalid.
270
0
    HTMLFormElement* form =
271
0
      static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
272
0
    if (form) {
273
0
      form->UpdateValidity(aBarred);
274
0
    }
275
0
    HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
276
0
    if (fieldSet) {
277
0
      fieldSet->UpdateValidity(aBarred);
278
0
    }
279
0
  }
280
0
}
281