Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test new EventTarget group option #6331

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions dom/events/EventTarget-group-option.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>EventTarget's ability to remove event listeners by group</title>
<link rel="author" title="Domenic Denicola" href="mailto:[email protected]">
<link rel="help" href="https://dom.spec.whatwg.org/#dom-addeventlisteneroptions-group">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<script>
"use strict";
test(() => {
const target = document.createElement("div");
const happenings = [];

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: "g" });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group: "g" });

target.removeEventListener({ group: "g" });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, []);
}, "Removing via a string group works");

test(() => {
const target = document.createElement("div");
const happenings = [];

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: "g" });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group: "g" });

target.removeEventListener("e1", { group: "g" });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, ["e2.1", "e2.2"]);
}, "Removing via a string group with a type specified works");

test(() => {
const target = document.createElement("div");
const happenings = [];
const group = Symbol();

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group });

target.removeEventListener({ group });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, []);
}, "Removing via a symbol group works");

test(() => {
const target = document.createElement("div");
const happenings = [];
const group = Symbol();

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group });

target.removeEventListener("e1", { group });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, ["e2.1", "e2.2"]);
}, "Removing via a symbol group with a type specified works");

test(() => {
const target = document.createElement("div");
const happenings = [];

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: navigator });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: "[object Navigator]" });
target.addEventListener("e1", () => { happenings.push("e1.3"); }, { group: "other value" });

target.removeEventListener({ group: navigator });

target.dispatchEvent(new CustomEvent("e1"));

assert_array_equals(happenings, ["e1.3"]);
}, "Attempting to use a platform object as a group converts it to a string");

test(() => {
const target = document.createElement("div");
const happenings = [];

assert_throws(new TypeError(), () => target.addEventListener("e1", () => {}, { group: null }));
assert_throws(new TypeError(), () => target.removeEventListener("e1", { group: null }));
assert_throws(new TypeError(), () => target.removeEventListener({ group: null }));
}, "Attempting to use null as a group does not work");

test(() => {
const target = document.createElement("div");
const happenings = [];

// undefined is treated as not present (i.e. not grouped) for addEventListener
target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: undefined });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: undefined });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group: undefined });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group: undefined });

// undefined is not allowed for removeEventListener
assert_throws(new TypeError(), () => target.removeEventListener({ group: undefined }));
assert_throws(new TypeError(), () => target.removeEventListener({ }));
assert_throws(new TypeError(), () => target.removeEventListener("e1", { group: undefined }));
assert_throws(new TypeError(), () => target.removeEventListener("e1", { }));

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, ["e1.1", "e1.2", "e2.1", "e2.2"]);
}, "Attempting to use undefined as a group does not work");

test(() => {
const target = document.createElement("div");
const happenings = [];
const g2 = Symbol("g2");

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: ["g", g2] });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: ["f", "g"] });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group: [g2] });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.3"); }, { group: "h" });

target.removeEventListener({ group: "g" });
target.removeEventListener({ group: g2 });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, ["e2.3"]);
}, "Multiple groups can be added via an array");

test(() => {
const target = document.createElement("div");

target.removeEventListener({ group: "g" });
target.removeEventListener({ group: new Symbol() });

target.removeEventListener("e1", { group: "g" });
target.removeEventListener("e1", { group: new Symbol() });
}, "Removing using non-existant groups doesn't throw or do anything weird");

test(() => {
const target = document.createElement("div");
const happenings = [];

target.addEventListener("e1", () => { happenings.push("e1.1"); }, { group: ["g", "g2"] });
target.addEventListener("e1", () => { happenings.push("e1.2"); }, { group: ["f", "g"] });
target.addEventListener("e2", () => { happenings.push("e2.1"); }, { group: ["g2"] });
target.addEventListener("e2", () => { happenings.push("e2.2"); }, { group: "g" });
target.addEventListener("e2", () => { happenings.push("e2.3"); }, { group: "g,g2" });

target.removeEventListener({ group: ["g", "g2"] });

target.dispatchEvent(new CustomEvent("e1"));
target.dispatchEvent(new CustomEvent("e2"));

assert_array_equals(happenings, ["e1.1", "e1.2", "e2.1", "e2.2"]);
}, "Attempting to pass an array to remove stringifies it");

test(() => {
const target = document.createElement("div");
let counter = 0;

function handler() {
++counter;
}

target.addEventListener("e1", handler, { group: "g" });
target.addEventListener("e1", handler);
target.addEventListener("e1", handler, { group: "f" });

target.dispatchEvent(new CustomEvent("e1"));
assert_equals(counter, 1, "The handler must only have been added once");

target.removeEventListener({ group: "g" });
target.dispatchEvent(new CustomEvent("e1"));
assert_equals(counter, 1, "Dispatching the event after removing the handler must not call the handler");
}, "Groups are not considered when checking for duplicates");
</script>
21 changes: 17 additions & 4 deletions interfaces/dom.idl
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ dictionary CustomEventInit : EventInit {
};


//[Exposed=(Window,Worker)]
[Exposed=(Window,Worker)]
interface EventTarget {
void addEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);

void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
void removeEventListener(DOMString type, RemoveEventListenerGroupOptions options);
void removeEventListener(RemoveEventListenerGroupOptions options);

boolean dispatchEvent(Event event);
};

Expand All @@ -56,8 +60,17 @@ callback interface EventListener {
};

dictionary EventListenerOptions {
boolean capture;
boolean passive;
boolean capture = false;
};

dictionary RemoveEventListenerGroupOptions {
required any group;
};

dictionary AddEventListenerOptions : EventListenerOptions {
boolean passive = false;
boolean once = false;
any group;
};


Expand Down