| <!DOCTYPE html> |
| <title>MathMLElement GlobalEventHandlers</title> |
| <link rel="author" title="Brian Kardell" href="mailto:bkardell@igalia.com" /> |
| <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dom-and-javascript"/> |
| <link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-idl-attributes"/> |
| <link rel="help" href="https://html.spec.whatwg.org/multipage/#event-handler-content-attributes"/> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/resources/WebIDLParser.js"></script> |
| |
| <div style="display: none" id="container"></div> |
| |
| <script> |
| "use strict"; |
| |
| // The prefixed animation events are special; their event types are |
| // camel-case. |
| const prefixedAnimationAttributeToEventType = new Map([ |
| ["webkitanimationend", "webkitAnimationEnd"], |
| ["webkitanimationiteration", "webkitAnimationIteration"], |
| ["webkitanimationstart", "webkitAnimationStart"], |
| ["webkittransitionend", "webkitTransitionEnd"], |
| ]); |
| |
| setup({ explicit_done: true }); |
| |
| // basic pattern lifted from /html/webappapis/scripting/events/event-handler-all-global-events.html |
| fetch("/interfaces/html.idl") |
| .then(res => res.text()) |
| .then(htmlIDL => { |
| const parsedHTMLIDL = WebIDL2.parse(htmlIDL); |
| const globalEventHandlers = parsedHTMLIDL.find( |
| idl => idl.name === "GlobalEventHandlers" |
| ); |
| |
| // onerror is too special |
| const names = globalEventHandlers.members |
| .map(member => member.name) |
| .filter(name => name !== "onerror"); |
| |
| for (const name of names) { |
| const withoutOn = name.substring(2); |
| |
| test(() => { |
| const location = MathMLElement.prototype; |
| assert_true( |
| location.hasOwnProperty(name), |
| `${location.constructor.name} has an own property named "${name}"` |
| ); |
| |
| assert_false( |
| name in Element.prototype, |
| `Element.prototype must not contain a "${name}" property` |
| ); |
| }, `${name}: must be on the appropriate locations for GlobalEventHandlers`); |
| |
| test(() => { |
| const location = document.createElementNS( |
| "http://www.w3.org/1998/Math/MathML", |
| "math" |
| ); |
| |
| assert_equals( |
| location[name], |
| null, |
| `The default value of the property is null for a ${ |
| location.constructor.name |
| } instance` |
| ); |
| }, `${name}: the default value must be null`); |
| |
| test(() => { |
| const div = document.getElementById("container"); |
| div.innerHTML = `<math ${name}="window.${name}Happened1 = true;"></math>`; |
| const compiledHandler = div.firstElementChild[name]; |
| assert_equals( |
| typeof compiledHandler, |
| "function", |
| `The ${name} property must be a function` |
| ); |
| compiledHandler(); |
| assert_true( |
| window[`${name}Happened1`], |
| "Calling the handler must run the code" |
| ); |
| }, `${name}: the content attribute must be compiled into a function as the corresponding property`); |
| |
| test(() => { |
| const el = document.createElementNS( |
| "http://www.w3.org/1998/Math/MathML", |
| "math" |
| ); |
| assert_equals(el[name], null, `The ${name} property must be null (no attribute)`); |
| |
| el.setAttribute(name, `window.${name}Happened2 = true;`); |
| const compiledHandler = el[name]; |
| assert_equals( |
| typeof compiledHandler, |
| "function", |
| `The ${name} property must be a function (set attribute)` |
| ); |
| compiledHandler(); |
| assert_true( |
| window[`${name}Happened2`], |
| "Calling the handler must run the code (set attribute)" |
| ); |
| |
| window[`${name}Happened2`] = false; |
| const clonedEl = el.cloneNode(true); |
| const clonedCompiledHandler = clonedEl[name]; |
| assert_equals( |
| typeof clonedCompiledHandler, |
| "function", |
| `The ${name} property must be a function (clone node)` |
| ); |
| clonedCompiledHandler(); |
| assert_true( |
| window[`${name}Happened2`], |
| "Calling the handler must run the code (clone node)" |
| ); |
| |
| el.setAttribute(name, `window.${name}Happened3 = true;`); |
| const newCompiledHandler = el[name]; |
| assert_equals( |
| typeof newCompiledHandler, |
| "function", |
| `The ${name} property must be a function (modify attribute)` |
| ); |
| newCompiledHandler(); |
| assert_true( |
| window[`${name}Happened3`], |
| "Calling the handler must run the code (modify attribute)" |
| ); |
| |
| el.removeAttribute(name); |
| assert_equals(el[name], null, `The ${name} property must be null (remove attribute)`); |
| }, `${name}: dynamic changes on the attribute`); |
| |
| test(() => { |
| const element = document.createElementNS( |
| "http://www.w3.org/1998/Math/MathML", |
| "math" |
| ); |
| let target = undefined; |
| element[name] = (e) => { target = e.currentTarget; } |
| let eventType = withoutOn; |
| if (prefixedAnimationAttributeToEventType.has(eventType)) { |
| eventType = prefixedAnimationAttributeToEventType.get(eventType); |
| } |
| element.dispatchEvent(new Event(eventType)); |
| assert_equals(target, element, "The event must be fired at the <math> element"); |
| }, `${name}: dispatching an Event at a <math> element must trigger element.${name}`); |
| } |
| |
| done(); |
| }); |
| </script> |