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

Extract information on cancelability of events #1534

Merged
merged 4 commits into from
Apr 19, 2024
Merged
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
1 change: 1 addition & 0 deletions schemas/browserlib/extract-events.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"items": { "$ref": "../common.json#/$defs/interface" }
},
"bubbles": { "type": "boolean" },
"cancelable": { "type": "boolean" },
"isExtension": { "type": "boolean" },
"href": { "$ref": "../common.json#/$defs/url" },
"src": {
Expand Down
1 change: 1 addition & 0 deletions schemas/postprocessing/events.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"href": { "$ref": "../common.json#/$defs/url" }
}
},
"cancelable": { "type": "boolean" },
"extendedIn": {
"type": "array",
"items": {
Expand Down
59 changes: 53 additions & 6 deletions src/browserlib/extract-events.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import extractWebIdl from './extract-webidl.mjs';
import {parse} from "../../node_modules/webidl2/index.js";
import getAbsoluteUrl from './get-absolute-url.mjs';

const isSameEvent = (e1, e2) => e1.type === e2.type &&
((e1.href && e1.href === e2.href ) ||
(e1.targets?.sort()?.join("|") === e2.targets?.sort()?.join("|")));

const singlePage = !document.querySelector('[data-reffy-page]');
const href = el => el?.getAttribute("id") ? getAbsoluteUrl(el, {singlePage}) : null;

Expand Down Expand Up @@ -37,6 +33,15 @@ export default function (spec) {
return acc;
}, {});

function isSameEvent(e1, e2) {
const res = e1.type === e2.type &&
((e1.href && e1.href === e2.href ) ||
(e1.targets?.sort()?.join("|") === e2.targets?.sort()?.join("|")));
if (res && e1.cancelable !== undefined && e2.cancelable !== undefined && e1.cancelable !== e2.cancelable) {
console.error(`[reffy] Found two occurrences of same event with different "cancelable" properties in ${spec.title}: type=${e1.type} targets=${e1.targets.join(', ')} href=${e1.href}`);
}
return res;
}

function fromEventElementToTargetInterfaces(eventEl) {
if (!eventEl) return;
Expand Down Expand Up @@ -74,6 +79,8 @@ export default function (spec) {
// Useful e.g. for pointerevents
const bubblingInfoColumn = [...table.querySelectorAll("thead th")]
.findIndex(n => n.textContent.trim().match(/^bubbl/i));
const cancelableInfoColumn = [...table.querySelectorAll("thead th")]
.findIndex(n => n.textContent.trim().match(/^cancel/i));
const interfaceColumn = [...table.querySelectorAll("thead th")]
.findIndex(n => n.textContent.trim().match(/^(dom )?interface/i));
const targetsColumn = [...table.querySelectorAll("thead th")]
Expand Down Expand Up @@ -115,6 +122,9 @@ export default function (spec) {
if (bubblingInfoColumn >= 0) {
event.bubbles = tr.querySelector(`td:nth-child(${bubblingInfoColumn + 1})`)?.textContent?.trim() === "Yes";
}
if (cancelableInfoColumn >= 0) {
event.cancelable = !!tr.querySelector(`td:nth-child(${cancelableInfoColumn + 1})`)?.textContent?.trim().match(/(yes)|✓|(varies)/i);
}
if (interfaceColumn >= 0) {
event.interface =
tr.querySelector(`td:nth-child(${interfaceColumn + 1}) a`)?.textContent ??
Expand All @@ -134,14 +144,17 @@ export default function (spec) {
}
const eventTypeRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim().match(/^type/i));
const bubblingInfoRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim() === "Bubbles");
const cancelableInfoRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim() === "Cancelable");
const interfaceRow = [...table.querySelectorAll("tbody th")].findIndex(n => n.textContent.trim().match(/^interface/i));
const eventName = table.querySelector(`tr:nth-child(${eventTypeRow + 1}) td:nth-child(2)`)?.textContent?.trim();
const bubblesCell = table.querySelector(`tr:nth-child(${bubblingInfoRow + 1}) td:nth-child(2)`);
const bubbles = bubblesCell ? bubblesCell.textContent.trim() === "Yes" : null;
const cancelableCell = table.querySelector(`tr:nth-child(${cancelableInfoRow + 1}) td:nth-child(2)`);
const cancelable = cancelableCell ? cancelableCell.textContent.trim() === "Yes" : null;
const iface = table.querySelector(`tr:nth-child(${interfaceRow + 1}) td:nth-child(2)`)?.textContent?.trim();
if (eventName) {
events.push({
type: eventName, interface: iface, bubbles,
type: eventName, interface: iface, bubbles, cancelable,
src: { format: "css definition table", href: href(table.closest('*[id]')) },
href: href(table.closest('*[id]')) });
}
Expand Down Expand Up @@ -208,6 +221,7 @@ export default function (spec) {
phrasing = "fire functional event";
}
}

if (phrasing) {
const name = m.groups.eventName;
let newEvent = true;
Expand Down Expand Up @@ -263,6 +277,17 @@ export default function (spec) {
}
}
}
if (event.bubbles === undefined && event.cancelable === undefined) {
if (parsedText.match(/bubbles and cancelable attributes/)) {
if (parsedText.match(/true/)) {
event.bubbles = true;
event.cancelable = true;
} else if (parsedText.match(/false/)) {
event.bubbles = false;
event.cancelable = false;
}
}
}
if (event.bubbles === undefined) {
if (parsedText.match(/bubbles attribute/)) {
if (parsedText.match(/true/)) {
Expand All @@ -276,6 +301,19 @@ export default function (spec) {
event.bubbles = false;
}
}
if (event.cancelable === undefined) {
if (parsedText.match(/cancelable attribute/)) {
if (parsedText.match(/true/)) {
event.cancelable = true;
} else if (parsedText.match(/false/)) {
event.cancelable = false;
}
} else if (parsedText.match(/not cancelable/) || parsedText.match(/not be cancelable/)) {
event.cancelable = false;
} else if (parsedText.match(/cancelable/)) {
event.cancelable = true;
}
}
if (newEvent) {
events.push(event);
}
Expand Down Expand Up @@ -329,13 +367,18 @@ export default function (spec) {
};
// CSS Animations & Transitions uses dt/dd to describe events
// and uses a ul in the dd to describe bubbling behavior
let bubbles, iface;
let bubbles, iface, cancelable;
if (container.tagName === "DT") {
const bubbleItem = [...container.nextElementSibling.querySelectorAll("li")]
.find(li => li.textContent.startsWith("Bubbles:"));
if (bubbleItem) {
bubbles = !!bubbleItem.textContent.match(/yes/i);
}
const cancelableItem = [...container.nextElementSibling.querySelectorAll("li")]
.find(li => li.textContent.startsWith("Cancelable:"));
if (cancelableItem) {
cancelable = !!cancelableItem.textContent.match(/yes/i);
}
// CSS Animation & Transitions document the event in the heading
// of the section where the definitions are located
let currentEl = container.parentNode;
Expand All @@ -356,6 +399,7 @@ export default function (spec) {
event.interface = iface;
}
event.bubbles = bubbles;
event.cancelable = cancelable;
events.push(event);
if (!iface) {
console.error(`[reffy] No interface hint found for event definition ${event.type} in ${spec.title}`);
Expand All @@ -370,6 +414,9 @@ export default function (spec) {
if (bubbles !== undefined) {
ev.bubbles = bubbles;
}
if (cancelable !== undefined) {
ev.cancelable = cancelable;
}
}
});
return events
Expand Down
24 changes: 15 additions & 9 deletions tests/extract-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const defaultResults = (format, {successIface} = {successIface: "SuccessEvent"})
{
type: "success",
interface: successIface,
cancelable: true,
targets: [ "Example" ],
bubbles: true,
href: "about:blank#success",
Expand All @@ -19,6 +20,7 @@ const defaultResults = (format, {successIface} = {successIface: "SuccessEvent"})
{
type: "error",
interface: "ErrorEvent",
cancelable: false,
targets: [ "Example" ],
bubbles: false,
href: "about:blank#error",
Expand All @@ -39,11 +41,11 @@ const tests = [
title: "extracts events from a summary table with data spread across columns, completed by an IDL fragment",
html: `<table>
<thead>
<tr><th>Event type</th><th>Interface</th><th>Bubbles</th></tr>
<tr><th>Event type</th><th>Interface</th><th>Bubbles</th><th>Cancelable</th></tr>
</thead>
<tbody>
<tr><th><dfn id=success>success</dfn></th><td><a href=''>SuccessEvent</a></td><td>Yes</td></tr>
<tr><th><dfn id=error>error</dfn></th><td><a href=''>ErrorEvent</a></td><td>No</td></tr>
<tr><th><dfn id=success>success</dfn></th><td><a href=''>SuccessEvent</a></td><td>Yes</td><td>✓</td></tr>
<tr><th><dfn id=error>error</dfn></th><td><a href=''>ErrorEvent</a></td><td>No</td><td>No</td></tr>
</tbody></table>${defaultIdl}`,
res: defaultResults("summary table")
},
Expand All @@ -54,13 +56,15 @@ const tests = [
<tbody>
<tr><th>Type<td>success
<tr><th>Bubbles<td>Yes
<tr><th>Cancelable<td>Yes
<tr><th>Interface<td>SuccessEvent
</table>
<h3><code>error</code> Event</h3>
<table class="def" id='error'>
<tbody>
<tr><th>Type<td>error
<tr><th>Bubbles<td>no
<tr><th>Cancelable<td>no
<tr><th>Interface<td>ErrorEvent
</table>
${defaultIdl}`,
Expand All @@ -73,24 +77,26 @@ ${defaultIdl}`,
<dt><dfn data-dfn-for=Example data-dfn-type=event id=success>success</dfn></dt>
<dd><ul>
<li>Bubbles: Yes</li>
<li>Cancelable: Yes</li>
</ul></dd>
<dt><dfn data-dfn-for=Example data-dfn-type=event id=error>error</dfn></dt>
<dd><ul>
<li>Bubbles: No</li>
<li>Cancelable: No</li>
</ul></dd>
`,
res: defaultResults("dfn", {successIface: "ErrorEvent"})
},
{
title: "extracts events from an event mentioned in a 'Fire an event' context, completed by an IDL fragment",
html: `<p id=success><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>success</code> using <a href=''>SuccessEvent</a> with the <code>bubbles</code> attribute initialized to <code>true</code></p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> attribute initialized to <code>false</code></p>${defaultIdl}`,
html: `<p id=success><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>success</code> using <a href=''>SuccessEvent</a> with the <code>bubbles</code> and <code>cancelable</code> attributes initialized to <code>true</code></p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> attribute initialized to <code>false</code> and the <code>cancelable</code> attribute set to <code>false</code></p>${defaultIdl}`,
res: defaultResults("fire an event phrasing")
},
{
title: "extracts events from an event mentioned in a 'Fire Functional Event' context, completed by an IDL fragment",
html: `<p id=success><a href='https://w3c.github.io/ServiceWorker/#fire-functional-event'>Fire Functional Event</a> <code>success</code> with the <code>bubbles</code> attribute initialized to <code>true</code></p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> attribute initialized to <code>false</code></p>${defaultIdl}`,
html: `<p id=success><a href='https://w3c.github.io/ServiceWorker/#fire-functional-event'>Fire Functional Event</a> <code>success</code> with the <code>bubbles</code> attribute initialized to <code>true</code> and the <code>cancelable</code> attribute initialized to <code>true</code></p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> and <code>cancelable</code> attributes initialized to <code>false</code></p>${defaultIdl}`,
res: defaultResults("fire an event phrasing", {successIface: "ExtendableEvent"})
},
{
Expand Down Expand Up @@ -161,8 +167,8 @@ ${defaultIdl}`,
{
title: "does not get confused by asides",
html: `<p id=success><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a>
named <code>success</code><span><span class="mdn-anno">Info</span></span> using <a href=''>SuccessEvent</a> with the <code>bubbles</code> attribute initialized to <code>true</code>.</p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> attribute initialized to <code>false</code></p>
named <code>success</code><span><span class="mdn-anno">Info</span></span> using <a href=''>SuccessEvent</a> with the <code>bubbles</code> and <code>cancelable</code> attributes initialized to <code>true</code>.</p>
<p id=error><a href='https://dom.spec.whatwg.org/#concept-event-fire'>Fire an event</a> named <code>error</code> using <a href=''>ErrorEvent</a> with the <code>bubbles</code> attribute initialized to <code>false</code> and must not be cancelable</p>
${defaultIdl}`,
res: defaultResults("fire an event phrasing")
}
Expand Down
Loading