<!DOCTYPE html>
<title>GlobalEventHandlers</title>
<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
<link rel="help" href="https://html.spec.whatwg.org/multipage/#globaleventhandlers">
<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>

<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 });

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(() => {
      for (const location of [window, HTMLElement.prototype, SVGElement.prototype, Document.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 htmlElement = document.createElement("span");
      const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "g");

      for (var location of [window, htmlElement, svgElement, document]) {
        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 el = document.createElement("div");
      el.setAttribute(name, `window.${name}Happened = true;`);
      const compiledHandler = el[name];

      assert_equals(typeof compiledHandler, "function", `The ${name} property must be a function`);
      compiledHandler();
      assert_true(window[name + "Happened"], "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.createElement("div");
      el.setAttribute(name, `window.${name}Happened2 = true;`);

      let eventType = withoutOn;
      if (prefixedAnimationAttributeToEventType.has(eventType)) {
        eventType = prefixedAnimationAttributeToEventType.get(eventType);
      }
      el.dispatchEvent(new Event(eventType));

      assert_true(window[name + "Happened2"], "Dispatching an event must run the code");
    }, `${name}: the content attribute must execute when an event is dispatched`);

    test(() => {
      const element = document.createElement("meta");
      element[name] = e => {
        assert_equals(e.currentTarget, element, "The event must be fired at the <meta> element");
      };

      element.dispatchEvent(new Event(withoutOn));
    }, `${name}: dispatching an Event at a <meta> element must trigger element.${name}`);
  }

  done();
});
</script>
