Bug 1556352 - Part 2: Implement formAssociatedCallback; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D129197
This commit is contained in:
Edgar Chen 2021-10-28 10:29:38 +00:00
Родитель 3f791b5050
Коммит da31a82815
8 изменённых файлов: 65 добавлений и 12 удалений

Просмотреть файл

@ -164,6 +164,12 @@ UniquePtr<CustomElementCallback> CustomElementCallback::Create(
}
break;
case ElementCallbackType::eFormAssociated:
if (aDefinition->mCallbacks->mFormAssociatedCallback.WasPassed()) {
func = aDefinition->mCallbacks->mFormAssociatedCallback.Value();
}
break;
case ElementCallbackType::eFormReset:
if (aDefinition->mCallbacks->mFormResetCallback.WasPassed()) {
func = aDefinition->mCallbacks->mFormResetCallback.Value();
@ -209,6 +215,10 @@ void CustomElementCallback::Call() {
->Call(mThisObject, mArgs.mName, mArgs.mOldValue, mArgs.mNewValue,
mArgs.mNamespaceURI);
break;
case ElementCallbackType::eFormAssociated:
static_cast<LifecycleFormAssociatedCallback*>(mCallback.get())
->Call(mThisObject, mArgs.mForm);
break;
case ElementCallbackType::eFormReset:
static_cast<LifecycleFormResetCallback*>(mCallback.get())
->Call(mThisObject);
@ -1561,6 +1571,12 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition)
cb.NoteXPCOMChild(callbacks->mAdoptedCallback.Value());
}
if (callbacks->mFormAssociatedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mCallbacks->mFormAssociatedCallback");
cb.NoteXPCOMChild(callbacks->mFormAssociatedCallback.Value());
}
if (callbacks->mFormResetCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mFormResetCallback");
cb.NoteXPCOMChild(callbacks->mFormResetCallback.Value());

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInternals.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "mozilla/RefPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
@ -39,6 +40,7 @@ enum class ElementCallbackType {
eDisconnected,
eAdopted,
eAttributeChanged,
eFormAssociated,
eFormReset,
eFormDisabled,
eGetCustomInterface
@ -55,6 +57,9 @@ struct LifecycleCallbackArgs {
RefPtr<Document> mOldDocument;
RefPtr<Document> mNewDocument;
// Used by the form associated callback.
RefPtr<HTMLFormElement> mForm;
// Used by the form disabled callback.
bool mDisabled;

Просмотреть файл

@ -137,6 +137,16 @@ already_AddRefed<ElementInternals> HTMLElement::AttachInternals(
return do_AddRef(ceData->GetOrCreateElementInternals(this));
}
void HTMLElement::AfterClearForm(bool aUnbindOrDelete) {
// No need to enqueue formAssociated callback if we aren't releasing or
// unbinding from tree, UpdateFormOwner() will handle it.
if (aUnbindOrDelete) {
MOZ_ASSERT(IsFormAssociatedElement());
nsContentUtils::EnqueueLifecycleCallback(
ElementCallbackType::eFormAssociated, this, {});
}
}
void HTMLElement::UpdateFormOwner() {
MOZ_ASSERT(IsFormAssociatedElement());
@ -147,7 +157,7 @@ void HTMLElement::UpdateFormOwner() {
// call UpdateFormOwner if none of these conditions are fulfilled.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? IsInComposedDoc()
: !!GetParent()) {
nsGenericHTMLFormElement::UpdateFormOwner(true, nullptr);
UpdateFormOwner(true, nullptr);
}
UpdateFieldSet(true);
UpdateDisabledState(true);
@ -208,6 +218,18 @@ void HTMLElement::UpdateDisabledState(bool aNotify) {
}
}
void HTMLElement::UpdateFormOwner(bool aBindToTree, Element* aFormIdElement) {
HTMLFormElement* oldForm = GetFormInternal();
nsGenericHTMLFormElement::UpdateFormOwner(aBindToTree, aFormIdElement);
HTMLFormElement* newForm = GetFormInternal();
if (newForm != oldForm) {
LifecycleCallbackArgs args;
args.mForm = newForm;
nsContentUtils::EnqueueLifecycleCallback(
ElementCallbackType::eFormAssociated, this, args);
}
}
bool HTMLElement::IsFormAssociatedElement() const {
CustomElementData* data = GetCustomElementData();
bool isFormAssociatedCustomElement = data && data->IsFormAssociated();

Просмотреть файл

@ -35,6 +35,7 @@ class HTMLElement final : public nsGenericHTMLFormElement {
// nsGenericHTMLFormElement
bool IsFormAssociatedElement() const override;
void AfterClearForm(bool aUnbindOrDelete) override;
void UpdateFormOwner();
@ -58,6 +59,7 @@ class HTMLElement final : public nsGenericHTMLFormElement {
bool CanBeDisabled() const override;
bool DoesReadOnlyApply() const override;
void UpdateDisabledState(bool aNotify) override;
void UpdateFormOwner(bool aBindToTree, Element* aFormIdElement) override;
ElementInternals* GetElementInternals() const;
};

Просмотреть файл

@ -1040,7 +1040,7 @@ class nsGenericHTMLFormElement : public nsGenericHTMLElement {
* @note Callers of UpdateFormOwner have to be sure the element is in a
* document (GetUncomposedDoc() != nullptr).
*/
void UpdateFormOwner(bool aBindToTree, Element* aFormIdElement);
virtual void UpdateFormOwner(bool aBindToTree, Element* aFormIdElement);
/**
* This method will update mFieldset and set it to the first fieldset parent.

Просмотреть файл

@ -38,6 +38,8 @@ callback LifecycleAttributeChangedCallback = void(DOMString attrName,
DOMString? newValue,
DOMString? namespaceURI);
[MOZ_CAN_RUN_SCRIPT_BOUNDARY]
callback LifecycleFormAssociatedCallback = void(HTMLFormElement? form);
[MOZ_CAN_RUN_SCRIPT_BOUNDARY]
callback LifecycleFormResetCallback = void();
[MOZ_CAN_RUN_SCRIPT_BOUNDARY]
callback LifecycleFormDisabledCallback = void(boolean disabled);
@ -50,6 +52,7 @@ dictionary LifecycleCallbacks {
LifecycleDisconnectedCallback disconnectedCallback;
LifecycleAdoptedCallback adoptedCallback;
LifecycleAttributeChangedCallback attributeChangedCallback;
LifecycleFormAssociatedCallback formAssociatedCallback;
LifecycleFormResetCallback formResetCallback;
LifecycleFormDisabledCallback formDisabledCallback;
[ChromeOnly] LifecycleGetCustomInterfaceCallback getCustomInterfaceCallback;

Просмотреть файл

@ -1,9 +0,0 @@
[form-associated-callback.html]
[Associate by parser, customized at element creation]
expected: FAIL
[Parsed, connected, then upgraded]
expected: FAIL
[Disassociation]
expected: FAIL

Просмотреть файл

@ -176,20 +176,34 @@ test(() => {
test(() => {
$('#container').innerHTML = '<form id="form1"></form>' +
'<pre-defined id="pd1"></pre-defined><form id="form2"></form>';
'<pre-defined id="pd1"></pre-defined><form id="form2">' +
'</form><form id="form3"></form>';
const pd1 = $('#pd1');
const form1 = $('#form1');
const form2 = $('#form2');
const form3 = $('#form3');
assert_equals(pd1.form, null);
assert_array_equals(pd1.formHistory(), []);
pd1.setAttribute('form', 'form1');
assert_equals(pd1.form, form1);
assert_array_equals(pd1.formHistory(), [form1]);
pd1.setAttribute('form', 'invalid');
assert_equals(pd1.form, null);
assert_array_equals(pd1.formHistory(), [form1, null]);
pd1.setAttribute('form', 'form2');
assert_equals(pd1.form, form2);
assert_array_equals(pd1.formHistory(), [form1, null, form2]);
pd1.setAttribute('form', 'form3');
assert_equals(pd1.form, form3);
assert_array_equals(pd1.formHistory(), [form1, null, form2, form3]);
$('#container').innerHTML = '';
assert_equals(pd1.form, null);
assert_array_equals(pd1.formHistory(), [form1, null, form2, form3, null]);
}, 'Updating "form" content attribute');
</script>
</body>