Bug 1800370 - Make form-associate custom element behave the same as other form control element on event dispatching; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D165292
This commit is contained in:
Edgar Chen 2023-01-04 13:08:39 +00:00
Родитель 9cf9360385
Коммит 9b1c4ebd8d
4 изменённых файлов: 60 добавлений и 19 удалений

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

@ -8,6 +8,7 @@
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/EventDispatcher.h"
#include "nsContentUtils.h"
namespace mozilla::dom {
@ -38,6 +39,16 @@ JSObject* HTMLElement::WrapNode(JSContext* aCx,
return dom::HTMLElement_Binding::Wrap(aCx, this, aGivenProto);
}
void HTMLElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
if (IsDisabledForEvents(aVisitor.mEvent)) {
// Do not process any DOM events if the element is disabled
aVisitor.mCanHandle = false;
return;
}
nsGenericHTMLFormElement::GetEventTargetParent(aVisitor);
}
nsresult HTMLElement::BindToTree(BindContext& aContext, nsINode& aParent) {
nsresult rv = nsGenericHTMLFormElement::BindToTree(aContext, aParent);
NS_ENSURE_SUCCESS(rv, rv);
@ -179,6 +190,14 @@ void HTMLElement::UpdateFormOwner() {
UpdateBarredFromConstraintValidation();
}
bool HTMLElement::IsDisabledForEvents(WidgetEvent* aEvent) {
if (IsFormAssociatedElement()) {
return IsElementDisabledForEvents(aEvent, GetPrimaryFrame());
}
return false;
}
nsresult HTMLElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,

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

@ -20,6 +20,9 @@ class HTMLElement final : public nsGenericHTMLFormElement {
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLElement,
nsGenericHTMLFormElement)
// EventTarget
void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
// nsINode
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
@ -36,6 +39,7 @@ class HTMLElement final : public nsGenericHTMLFormElement {
// https://html.spec.whatwg.org/multipage/custom-elements.html#dom-attachinternals
already_AddRefed<mozilla::dom::ElementInternals> AttachInternals(
ErrorResult& aRv) override;
bool IsDisabledForEvents(WidgetEvent* aEvent) override;
// nsGenericHTMLFormElement
bool IsFormAssociatedElement() const override;

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

@ -1,4 +1,5 @@
<!DOCTYPE html>
<meta name="timeout" content="long">
<link rel=author href="mailto:jarhar@chromium.org">
<link rel=help href="https://github.com/whatwg/html/issues/2368">
<link rel=help href="https://github.com/whatwg/html/issues/5886">
@ -9,37 +10,48 @@
<script src="/resources/testdriver-actions.js"></script>
<div id=targetparent>
<button id=target disabled>
<button disabled>
hello world
<span style="border: 1px solid black" id=targetchild>child</span>
<span style="border: 1px solid black">child</span>
</button>
<my-control disabled>
hello world
<span style="border: 1px solid black">child</span>
</my-control>
</div>
<script>
customElements.define('my-control', class extends HTMLElement {
static get formAssociated() { return true; }
});
['mousedown', 'mouseup', 'pointerdown', 'pointerup', 'click'].forEach(eventName => {
[true, false].forEach(clickChildElement => {
promise_test(async () => {
let parentReceivedEvent = false;
targetparent.addEventListener(eventName, () => parentReceivedEvent = true);
for (const target of targetparent.children) {
promise_test(async () => {
let parentReceivedEvent = false;
targetparent.addEventListener(eventName, () => parentReceivedEvent = true);
let targetReceivedEvent = false;
target.addEventListener(eventName, () => targetReceivedEvent = true);
let targetReceivedEvent = false;
target.addEventListener(eventName, () => targetReceivedEvent = true);
let childReceivedEvent = false;
targetchild.addEventListener(eventName, () => childReceivedEvent = true);
let childReceivedEvent = false;
let targetchild = target.firstElementChild;
targetchild.addEventListener(eventName, () => childReceivedEvent = true);
await test_driver.click(clickChildElement ? targetchild : target);
await test_driver.click(clickChildElement ? targetchild : target);
const parentShouldReceiveEvents = eventName.startsWith('pointer');
assert_equals(parentReceivedEvent, parentShouldReceiveEvents,
`parent element received ${eventName} events`);
const parentShouldReceiveEvents = eventName.startsWith('pointer');
assert_equals(parentReceivedEvent, parentShouldReceiveEvents,
`parent element received ${eventName} events`);
const targetShouldReceiveEvents = eventName.startsWith('pointer');
assert_equals(targetReceivedEvent, targetShouldReceiveEvents,
`target element received ${eventName} events`);
assert_equals(childReceivedEvent, clickChildElement,
`child element received ${eventName} events`);
}, `Testing ${eventName} events when clicking ${clickChildElement ? 'child of ' : ''}disabled form controls.`);
const targetShouldReceiveEvents = eventName.startsWith('pointer');
assert_equals(targetReceivedEvent, targetShouldReceiveEvents,
`target element received ${eventName} events`);
assert_equals(childReceivedEvent, clickChildElement,
`child element received ${eventName} events`);
}, `Testing ${eventName} events when clicking ${clickChildElement ? 'child of ' : ''}disabled ${target.localName}.`);
}
});
});
</script>

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

@ -47,9 +47,15 @@
<input disabled type="time">
<input disabled type="url">
<input disabled type="week">
<my-control disabled>Text</my-control>
</div>
<script>
customElements.define('my-control', class extends HTMLElement {
static get formAssociated() { return true; }
get disabled() { return this.hasAttribute("disabled"); }
});
/**
* @param {Element} element
*/