Bug 1556351 - Part 8: Make HTMLElement inherit nsGenericHTMLFormElement; r=smaug

And add checks in form codes to ensure they won't be run on HTMLElement that
isn't a FACE.

Differential Revision: https://phabricator.services.mozilla.com/D125210
This commit is contained in:
Edgar Chen 2021-10-04 20:51:28 +00:00
parent 6067012a93
commit e2e899cd73
4 changed files with 51 additions and 34 deletions

View File

@ -11,7 +11,7 @@
namespace mozilla::dom {
HTMLElement::HTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
: nsGenericHTMLElement(std::move(aNodeInfo)) {
: nsGenericHTMLFormElement(std::move(aNodeInfo)) {
if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
AddStatesSilently(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO);
}

View File

@ -11,7 +11,7 @@
namespace mozilla::dom {
class HTMLElement final : public nsGenericHTMLElement {
class HTMLElement final : public nsGenericHTMLFormElement {
public:
explicit HTMLElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
virtual ~HTMLElement() = default;

View File

@ -1659,6 +1659,8 @@ nsGenericHTMLFormElement::nsGenericHTMLFormElement(
void nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm,
bool aUnbindOrDelete) {
MOZ_ASSERT(IsFormAssociatedElement());
HTMLFormElement* form = GetFormInternal();
NS_ASSERTION((form != nullptr) == HasFlag(ADDED_TO_FORM),
"Form control should have had flag set correctly");
@ -1694,15 +1696,16 @@ nsresult nsGenericHTMLFormElement::BindToTree(BindContext& aContext,
nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
NS_ENSURE_SUCCESS(rv, rv);
// If @form is set, the element *has* to be in a composed document, otherwise
// it wouldn't be possible to find an element with the corresponding id.
// If @form isn't set, the element *has* to have a parent, otherwise it
// wouldn't be possible to find a form ancestor.
// We should not call UpdateFormOwner if none of these conditions are
// fulfilled.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? IsInComposedDoc()
: aParent.IsContent()) {
UpdateFormOwner(true, nullptr);
if (IsFormAssociatedElement()) {
// If @form is set, the element *has* to be in a composed document,
// otherwise it wouldn't be possible to find an element with the
// corresponding id. If @form isn't set, the element *has* to have a parent,
// otherwise it wouldn't be possible to find a form ancestor. We should not
// call UpdateFormOwner if none of these conditions are fulfilled.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? IsInComposedDoc()
: aParent.IsContent()) {
UpdateFormOwner(true, nullptr);
}
}
// Set parent fieldset which should be used for the disabled state.
@ -1712,34 +1715,36 @@ nsresult nsGenericHTMLFormElement::BindToTree(BindContext& aContext,
}
void nsGenericHTMLFormElement::UnbindFromTree(bool aNullParent) {
if (HTMLFormElement* form = GetFormInternal()) {
// Might need to unset form
if (aNullParent) {
// No more parent means no more form
ClearForm(true, true);
} else {
// Recheck whether we should still have an form.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
!FindAncestorForm(form)) {
if (IsFormAssociatedElement()) {
if (HTMLFormElement* form = GetFormInternal()) {
// Might need to unset form
if (aNullParent) {
// No more parent means no more form
ClearForm(true, true);
} else {
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
// Recheck whether we should still have an form.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
!FindAncestorForm(form)) {
ClearForm(true, true);
} else {
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
}
}
if (!GetFormInternal()) {
// Our novalidate state might have changed
UpdateState(false);
}
}
if (!GetFormInternal()) {
// Our novalidate state might have changed
UpdateState(false);
// We have to remove the form id observer if there was one.
// We will re-add one later if needed (during bind to tree).
if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
nsGkAtoms::form)) {
RemoveFormIdObserver();
}
}
// We have to remove the form id observer if there was one.
// We will re-add one later if needed (during bind to tree).
if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
nsGkAtoms::form)) {
RemoveFormIdObserver();
}
nsGenericHTMLElement::UnbindFromTree(aNullParent);
// The element might not have a fieldset anymore.
@ -1749,7 +1754,7 @@ void nsGenericHTMLFormElement::UnbindFromTree(bool aNullParent) {
nsresult nsGenericHTMLFormElement::BeforeSetAttr(
int32_t aNameSpaceID, nsAtom* aName, const nsAttrValueOrString* aValue,
bool aNotify) {
if (aNameSpaceID == kNameSpaceID_None) {
if (aNameSpaceID == kNameSpaceID_None && IsFormAssociatedElement()) {
nsAutoString tmp;
HTMLFormElement* form = GetFormInternal();
@ -1799,7 +1804,7 @@ nsresult nsGenericHTMLFormElement::AfterSetAttr(
int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue,
const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal,
bool aNotify) {
if (aNameSpaceID == kNameSpaceID_None) {
if (aNameSpaceID == kNameSpaceID_None && IsFormAssociatedElement()) {
HTMLFormElement* form = GetFormInternal();
// add the control to the hashtable as needed
@ -1857,6 +1862,8 @@ void nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset) {
}
Element* nsGenericHTMLFormElement::AddFormIdObserver() {
MOZ_ASSERT(IsFormAssociatedElement());
nsAutoString formId;
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
@ -1868,6 +1875,8 @@ Element* nsGenericHTMLFormElement::AddFormIdObserver() {
}
void nsGenericHTMLFormElement::RemoveFormIdObserver() {
MOZ_ASSERT(IsFormAssociatedElement());
DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
if (!docOrShadow) {
return;
@ -1948,6 +1957,7 @@ bool nsGenericHTMLFormElement::IsElementDisabledForEvents(WidgetEvent* aEvent,
void nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
Element* aFormIdElement) {
MOZ_ASSERT(IsFormAssociatedElement());
MOZ_ASSERT(!aBindToTree || !aFormIdElement,
"aFormIdElement shouldn't be set if aBindToTree is true!");
@ -2023,7 +2033,7 @@ void nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
}
void nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify) {
if (IsInNativeAnonymousSubtree()) {
if (IsInNativeAnonymousSubtree() || !IsFormAssociatedElement()) {
MOZ_ASSERT(!GetFieldSetInternal());
return;
}

View File

@ -1081,6 +1081,12 @@ class nsGenericHTMLFormElement : public nsGenericHTMLElement {
* Returns if the readonly attribute applies.
*/
virtual bool DoesReadOnlyApply() const { return false; }
/**
* Returns true if the element is a form associated element.
* See https://html.spec.whatwg.org/#form-associated-element.
*/
virtual bool IsFormAssociatedElement() const { return false; }
};
class nsGenericHTMLFormControlElement : public nsGenericHTMLFormElement,
@ -1140,6 +1146,7 @@ class nsGenericHTMLFormControlElement : public nsGenericHTMLFormElement,
mozilla::dom::HTMLFieldSetElement* GetFieldSetInternal() const override;
void SetFieldSetInternal(
mozilla::dom::HTMLFieldSetElement* aFieldset) override;
bool IsFormAssociatedElement() const override { return true; }
/**
* Update our required/optional flags to match the given aIsRequired boolean.