2015-05-03 19:32:37 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 11:12:37 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2010-08-18 18:28:08 +00:00
|
|
|
|
2010-08-21 18:52:49 +00:00
|
|
|
#include "nsIConstraintValidation.h"
|
2010-08-18 18:28:08 +00:00
|
|
|
|
|
|
|
#include "nsAString.h"
|
|
|
|
#include "nsGenericHTMLElement.h"
|
2017-07-14 03:46:28 +00:00
|
|
|
#include "mozilla/ErrorResult.h"
|
2018-08-16 02:22:30 +00:00
|
|
|
#include "mozilla/dom/CustomEvent.h"
|
2013-06-19 14:24:37 +00:00
|
|
|
#include "mozilla/dom/HTMLFormElement.h"
|
2013-08-17 00:32:47 +00:00
|
|
|
#include "mozilla/dom/HTMLFieldSetElement.h"
|
2016-04-15 08:13:00 +00:00
|
|
|
#include "mozilla/dom/HTMLInputElement.h"
|
2013-01-06 19:20:02 +00:00
|
|
|
#include "mozilla/dom/ValidityState.h"
|
2010-09-10 05:08:56 +00:00
|
|
|
#include "nsIFormControl.h"
|
2011-08-11 13:29:50 +00:00
|
|
|
#include "nsContentUtils.h"
|
2010-08-21 17:52:57 +00:00
|
|
|
|
2016-04-15 08:13:00 +00:00
|
|
|
#include "nsIFormSubmitObserver.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
const uint16_t nsIConstraintValidation::sContentSpecifiedMaxLengthMessage = 256;
|
2010-10-25 22:02:24 +00:00
|
|
|
|
2013-08-17 00:32:47 +00:00
|
|
|
using namespace mozilla;
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
|
2010-08-21 18:52:49 +00:00
|
|
|
nsIConstraintValidation::nsIConstraintValidation()
|
2010-08-21 17:52:57 +00:00
|
|
|
: mValidityBitField(0)
|
2010-09-10 05:08:56 +00:00
|
|
|
// By default, all elements are subjects to constraint validation.
|
2011-10-17 14:59:28 +00:00
|
|
|
,
|
|
|
|
mBarredFromConstraintValidation(false) {}
|
2010-08-18 18:28:08 +00:00
|
|
|
|
2010-08-21 18:52:49 +00:00
|
|
|
nsIConstraintValidation::~nsIConstraintValidation() {}
|
2010-08-18 18:28:08 +00:00
|
|
|
|
2013-01-16 18:01:01 +00:00
|
|
|
mozilla::dom::ValidityState* nsIConstraintValidation::Validity() {
|
2010-08-18 18:28:08 +00:00
|
|
|
if (!mValidity) {
|
2013-01-06 19:20:02 +00:00
|
|
|
mValidity = new mozilla::dom::ValidityState(this);
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|
|
|
|
|
2013-01-16 18:01:01 +00:00
|
|
|
return mValidity;
|
|
|
|
}
|
|
|
|
|
2017-07-14 03:46:28 +00:00
|
|
|
void nsIConstraintValidation::GetValidationMessage(
|
|
|
|
nsAString& aValidationMessage, ErrorResult& aError) {
|
2010-08-18 18:28:08 +00:00
|
|
|
aValidationMessage.Truncate();
|
|
|
|
|
2010-08-21 18:51:38 +00:00
|
|
|
if (IsCandidateForConstraintValidation() && !IsValid()) {
|
2017-12-07 18:13:50 +00:00
|
|
|
nsCOMPtr<Element> element = do_QueryInterface(this);
|
|
|
|
NS_ASSERTION(element,
|
|
|
|
"This class should be inherited by HTML elements only!");
|
2010-10-15 13:05:14 +00:00
|
|
|
|
|
|
|
nsAutoString authorMessage;
|
2017-12-07 18:13:50 +00:00
|
|
|
element->GetAttr(kNameSpaceID_None, nsGkAtoms::x_moz_errormessage,
|
2010-10-15 13:05:14 +00:00
|
|
|
authorMessage);
|
|
|
|
|
|
|
|
if (!authorMessage.IsEmpty()) {
|
|
|
|
aValidationMessage.Assign(authorMessage);
|
2010-10-25 22:02:24 +00:00
|
|
|
if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
|
|
|
|
aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
|
|
|
|
}
|
2010-10-15 13:05:14 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR)) {
|
2010-08-18 18:28:08 +00:00
|
|
|
aValidationMessage.Assign(mCustomValidity);
|
2010-10-25 22:02:24 +00:00
|
|
|
if (aValidationMessage.Length() > sContentSpecifiedMaxLengthMessage) {
|
|
|
|
aValidationMessage.Truncate(sContentSpecifiedMaxLengthMessage);
|
|
|
|
}
|
2010-08-21 17:52:57 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_TOO_LONG)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_TOO_LONG);
|
2016-08-17 04:11:24 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_TOO_SHORT)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_TOO_SHORT);
|
2010-08-21 17:52:57 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_VALUE_MISSING)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_VALUE_MISSING);
|
|
|
|
} else if (GetValidityState(VALIDITY_STATE_TYPE_MISMATCH)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_TYPE_MISMATCH);
|
|
|
|
} else if (GetValidityState(VALIDITY_STATE_PATTERN_MISMATCH)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_PATTERN_MISMATCH);
|
2012-06-22 09:36:24 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_OVERFLOW);
|
2012-06-22 09:38:20 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_UNDERFLOW);
|
2012-06-22 19:02:20 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_STEP_MISMATCH);
|
2014-01-30 12:54:12 +00:00
|
|
|
} else if (GetValidityState(VALIDITY_STATE_BAD_INPUT)) {
|
|
|
|
GetValidationMessage(aValidationMessage, VALIDITY_STATE_BAD_INPUT);
|
2010-08-18 18:28:08 +00:00
|
|
|
} else {
|
2012-06-22 19:02:20 +00:00
|
|
|
// There should not be other validity states.
|
2017-07-14 03:46:28 +00:00
|
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return;
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aValidationMessage.Truncate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-16 18:01:01 +00:00
|
|
|
bool nsIConstraintValidation::CheckValidity() {
|
2010-08-21 18:51:38 +00:00
|
|
|
if (!IsCandidateForConstraintValidation() || IsValid()) {
|
2013-01-16 18:01:01 +00:00
|
|
|
return true;
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|
|
|
|
|
2010-08-21 18:51:38 +00:00
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(this);
|
|
|
|
NS_ASSERTION(content,
|
|
|
|
"This class should be inherited by HTML elements only!");
|
|
|
|
|
2018-06-25 16:23:50 +00:00
|
|
|
nsContentUtils::DispatchTrustedEvent(content->OwnerDoc(), content,
|
2013-01-16 18:01:01 +00:00
|
|
|
NS_LITERAL_STRING("invalid"),
|
2018-06-25 16:23:50 +00:00
|
|
|
CanBubble::eNo, Cancelable::eYes);
|
2013-01-16 18:01:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsIConstraintValidation::CheckValidity(bool* aValidity) {
|
|
|
|
NS_ENSURE_ARG_POINTER(aValidity);
|
|
|
|
|
|
|
|
*aValidity = CheckValidity();
|
|
|
|
|
|
|
|
return NS_OK;
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-15 08:13:00 +00:00
|
|
|
bool nsIConstraintValidation::ReportValidity() {
|
|
|
|
if (!IsCandidateForConstraintValidation() || IsValid()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:04:26 +00:00
|
|
|
nsCOMPtr<Element> element = do_QueryInterface(this);
|
|
|
|
MOZ_ASSERT(element, "This class should be inherited by HTML elements only!");
|
2016-04-15 08:13:00 +00:00
|
|
|
|
|
|
|
bool defaultAction = true;
|
2018-06-27 16:04:26 +00:00
|
|
|
nsContentUtils::DispatchTrustedEvent(
|
2016-04-15 08:13:00 +00:00
|
|
|
element->OwnerDoc(), element, NS_LITERAL_STRING("invalid"),
|
2018-06-25 16:23:50 +00:00
|
|
|
CanBubble::eNo, Cancelable::eYes, &defaultAction);
|
2016-04-15 08:13:00 +00:00
|
|
|
if (!defaultAction) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-08-16 19:20:54 +00:00
|
|
|
AutoTArray<RefPtr<Element>, 1> invalidElements;
|
|
|
|
invalidElements.AppendElement(element);
|
|
|
|
|
2018-08-16 02:22:30 +00:00
|
|
|
AutoJSAPI jsapi;
|
|
|
|
if (!jsapi.Init(element->GetOwnerGlobal())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
JS::Rooted<JS::Value> detail(jsapi.cx());
|
|
|
|
if (!ToJSValue(jsapi.cx(), invalidElements, &detail)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<CustomEvent> event =
|
|
|
|
NS_NewDOMCustomEvent(element->OwnerDoc(), nullptr, nullptr);
|
|
|
|
event->InitCustomEvent(jsapi.cx(), NS_LITERAL_STRING("MozInvalidForm"),
|
|
|
|
/* CanBubble */ true,
|
|
|
|
/* Cancelable */ true, detail);
|
|
|
|
event->SetTrusted(true);
|
|
|
|
event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
|
|
|
|
|
|
|
|
element->DispatchEvent(*event);
|
|
|
|
|
2016-04-15 08:13:00 +00:00
|
|
|
nsCOMPtr<nsIObserverService> service =
|
|
|
|
mozilla::services::GetObserverService();
|
|
|
|
if (!service) {
|
|
|
|
NS_WARNING("No observer service available!");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> theEnum;
|
|
|
|
nsresult rv = service->EnumerateObservers(NS_INVALIDFORMSUBMIT_SUBJECT,
|
|
|
|
getter_AddRefs(theEnum));
|
|
|
|
|
|
|
|
// Return true on error here because that's what we always did
|
|
|
|
NS_ENSURE_SUCCESS(rv, true);
|
|
|
|
|
|
|
|
bool hasObserver = false;
|
|
|
|
rv = theEnum->HasMoreElements(&hasObserver);
|
|
|
|
|
|
|
|
NS_ENSURE_SUCCESS(rv, true);
|
|
|
|
nsCOMPtr<nsISupports> inst;
|
|
|
|
nsCOMPtr<nsIFormSubmitObserver> observer;
|
|
|
|
bool more = true;
|
|
|
|
while (NS_SUCCEEDED(theEnum->HasMoreElements(&more)) && more) {
|
|
|
|
theEnum->GetNext(getter_AddRefs(inst));
|
|
|
|
observer = do_QueryInterface(inst);
|
|
|
|
|
|
|
|
if (observer) {
|
|
|
|
observer->NotifyInvalidSubmit(nullptr, invalidElements);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:04:26 +00:00
|
|
|
if (element->IsHTMLElement(nsGkAtoms::input) &&
|
|
|
|
// We don't use nsContentUtils::IsFocusedContent here, because it doesn't
|
|
|
|
// really do what we want for number controls: it's true for the
|
|
|
|
// anonymous textnode inside, but not the number control itself. We can
|
|
|
|
// use the focus state, though, because that gets synced to the number
|
|
|
|
// control by the anonymous text control.
|
|
|
|
element->State().HasState(NS_EVENT_STATE_FOCUS)) {
|
|
|
|
HTMLInputElement* inputElement = HTMLInputElement::FromNode(element);
|
2016-04-15 08:13:00 +00:00
|
|
|
inputElement->UpdateValidityUIBits(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
element->UpdateState(true);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-09-10 05:08:56 +00:00
|
|
|
void nsIConstraintValidation::SetValidityState(ValidityStateType aState,
|
2011-09-29 06:19:26 +00:00
|
|
|
bool aValue) {
|
|
|
|
bool previousValidity = IsValid();
|
2010-09-10 05:08:56 +00:00
|
|
|
|
|
|
|
if (aValue) {
|
|
|
|
mValidityBitField |= aState;
|
|
|
|
} else {
|
|
|
|
mValidityBitField &= ~aState;
|
|
|
|
}
|
|
|
|
|
2013-08-17 00:32:47 +00:00
|
|
|
// Inform the form and fieldset elements if our validity has changed.
|
2010-09-10 05:08:56 +00:00
|
|
|
if (previousValidity != IsValid() && IsCandidateForConstraintValidation()) {
|
|
|
|
nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
|
|
|
|
NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
|
|
|
|
|
2013-08-17 00:32:47 +00:00
|
|
|
HTMLFormElement* form =
|
|
|
|
static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
|
2010-09-10 05:08:56 +00:00
|
|
|
if (form) {
|
|
|
|
form->UpdateValidity(IsValid());
|
|
|
|
}
|
2013-08-17 00:32:47 +00:00
|
|
|
HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
|
|
|
|
if (fieldSet) {
|
|
|
|
fieldSet->UpdateValidity(IsValid());
|
|
|
|
}
|
2010-09-10 05:08:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-21 18:52:49 +00:00
|
|
|
void nsIConstraintValidation::SetCustomValidity(const nsAString& aError) {
|
2010-08-18 18:28:08 +00:00
|
|
|
mCustomValidity.Assign(aError);
|
2010-08-21 17:52:57 +00:00
|
|
|
SetValidityState(VALIDITY_STATE_CUSTOM_ERROR, !mCustomValidity.IsEmpty());
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|
|
|
|
|
2011-09-29 06:19:26 +00:00
|
|
|
void nsIConstraintValidation::SetBarredFromConstraintValidation(bool aBarred) {
|
|
|
|
bool previousBarred = mBarredFromConstraintValidation;
|
2010-09-10 05:08:56 +00:00
|
|
|
|
|
|
|
mBarredFromConstraintValidation = aBarred;
|
|
|
|
|
2013-08-17 00:32:47 +00:00
|
|
|
// Inform the form and fieldset elements if our status regarding constraint
|
|
|
|
// validation is going to change.
|
2010-09-10 05:08:56 +00:00
|
|
|
if (!IsValid() && previousBarred != mBarredFromConstraintValidation) {
|
|
|
|
nsCOMPtr<nsIFormControl> formCtrl = do_QueryInterface(this);
|
|
|
|
NS_ASSERTION(formCtrl, "This interface should be used by form elements!");
|
|
|
|
|
2013-08-17 00:32:47 +00:00
|
|
|
// If the element is going to be barred from constraint validation, we can
|
|
|
|
// inform the form and fieldset that we are now valid. Otherwise, we are now
|
|
|
|
// invalid.
|
|
|
|
HTMLFormElement* form =
|
|
|
|
static_cast<HTMLFormElement*>(formCtrl->GetFormElement());
|
2010-09-10 05:08:56 +00:00
|
|
|
if (form) {
|
|
|
|
form->UpdateValidity(aBarred);
|
|
|
|
}
|
2013-08-17 00:32:47 +00:00
|
|
|
HTMLFieldSetElement* fieldSet = formCtrl->GetFieldSet();
|
|
|
|
if (fieldSet) {
|
|
|
|
fieldSet->UpdateValidity(aBarred);
|
|
|
|
}
|
2010-09-10 05:08:56 +00:00
|
|
|
}
|
2010-08-18 18:28:08 +00:00
|
|
|
}
|