<!DOCTYPE html>
<html>
<head>
  <script src="/resources/testharness.js"></script>
  <script src="/resources/testharnessreport.js"></script>
  <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
</head>
<body>
<script>
  promise_test(t => {
    // Setup: We create an <iframe> and trigger loading the document. We use
    // WPTserve's ?pipe functionality to make sure it gets delivered in chunks
    // and that parsing will be deferred.
    var frame = document.createElement("iframe");
    document.body.appendChild(frame);
    frame.src = "support/HTMLScriptElement-internal-slot-support.html?pipe=trickle(200:d1)";

    // This function tries to find the first <script> element in our iframe and
    // manipulated it with DOM APIs (appendChild + createTextNode). If it
    // doesn't find the script element then it'll just enqueue itself again
    // in 100ms.
    var manipulator = _ => {
      let script = frame.contentDocument.getElementsByTagName("script")[0];
      if (script) {
        script.appendChild(frame.contentDocument.createTextNode("/*byapi*/"));
      } else {
        t.step_timeout(manipulator, 100);
      }
    }
    manipulator();

    // Now we'll wait for the postMessages to arrive. We expect the iframe's
    // first message to be blocked by Trusted Types, since the manipulator
    // above should have manipulated it (while loading). The second one should
    // pass.
    return new Promise((resolve, reject) => {
      window.addEventListener("message", e => {
        if (e.data.includes("first")) {
          reject("First message should have been blocked: " + e.data);
        } else if (e.data.includes("second")) {
          resolve();
        } else {
          reject("Unknown message: " + e.data);
        }
      });
    });
  }, "Test TT application when manipulating <script> elements during loading.");

  // Test that a script set via textContent or innerTest actually executes
  // (and not only doesn't throw an exception, which it wouldn't do due to the
  // "internal slot" mechanism).
  const policy = trustedTypes.createPolicy("testpolicy", {
      createScript: x => x, createScriptURL: x => x});
  promise_test(t => {
    const s = document.createElement("script");
    document.body.appendChild(s);
    s.textContent = policy.createScript("window.postMessage('hello');");
    return new Promise(resolve => {
      window.addEventListener("message", e => {
        if (e.data == "hello") resolve();
      });
    });
  }, "Script set via .textContent executes on a connected HTMLScriptElement.");
  promise_test(t => {
    const s = document.createElement("script");
    s.textContent = policy.createScript("window.postMessage('world');");
    document.body.appendChild(s);
    return new Promise(resolve => {
      window.addEventListener("message", e => {
        if (e.data == "world") resolve();
      });
    });
  }, "Script set via .textContent executes on an unconnected HTMLScriptElement.");
  promise_test(t => {
    const s = document.createElement("script");
    document.body.appendChild(s);
    s.innerText = policy.createScript("window.postMessage('hello');");
    return new Promise(resolve => {
      window.addEventListener("message", e => {
        if (e.data == "hello") resolve();
      });
    });
  }, "Script set via .innerText executes on a connected HTMLScriptElement.");
  promise_test(t => {
    const s = document.createElement("script");
    s.innerText= policy.createScript("window.postMessage('world');");
    document.body.appendChild(s);
    return new Promise(resolve => {
      window.addEventListener("message", e => {
        if (e.data == "world") resolve();
      });
    });
  }, "Script set via .innerText executes on an unconnected HTMLScriptElement.");

  // Test that interactions between the script content, the .src attribute, and
  // exceptions still work correctly with TT and the "internal slot" thingy.
  promise_test(t => {
    const s = document.createElement("script");
    s.textContent = policy.createScript("window.postMessage('script body');");
    try {
      s.src = "support/HTMLScriptElement-internal-slot-support.js";
    } catch (e) {}
    document.body.appendChild(s);
    return new Promise((resolve, reject) => {
      window.addEventListener("message", e => {
        // .src was set with plain string => should not have been accepted.
        if (e.data == "script body") resolve();
        if (e.data == "script url") reject();
      });
    });
  }, "Setting .src to a plain string should throw an exception and not modify the script state, on an unconnected script element.");

  promise_test(t => {
    const s = document.createElement("script");
    s.textContent = policy.createScript("window.postMessage('script body');");
    s.src = policy.createScriptURL("support/HTMLScriptElement-internal-slot-support.js");
    document.body.appendChild(s);
    return new Promise((resolve, reject) => {
      window.addEventListener("message", e => {
        // .src was set with a TrustedScriptURL => the .src should get executed.
        if (e.data == "script body") reject();
        if (e.data == "script url") resolve();
      });
    });
  }, "Setting .src to a TrustedScriptURL should work and should execute the referenced script instead of the script body, on an unconnected script element.");

  promise_test(t => {
    const s = document.createElement("script");
    document.body.appendChild(s);
    s.textContent = policy.createScript("window.postMessage('script body');");
    try {
      s.src = "support/HTMLScriptElement-internal-slot-support.js";
    } catch (e) {}
    return new Promise((resolve, reject) => {
      window.addEventListener("message", e => {
        // .src was set with plain string => should not have been accepted.
        if (e.data == "script body") resolve();
        if (e.data == "script url") reject();
      });
    });
  }, "Setting .src to a plain string should throw an exception and not modify the script state, on a connected script element.");

  promise_test(t => {
    const s = document.createElement("script");
    s.textContent = policy.createScript("window.postMessage('script body');");
    s.src = policy.createScriptURL("support/HTMLScriptElement-internal-slot-support.js");
    document.body.appendChild(s);
    return new Promise((resolve, reject) => {
      window.addEventListener("message", e => {
        // .src was set with a TrustedScriptURL => the .src should get executed.
        if (e.data == "script body") reject();
        if (e.data == "script url") resolve();
      });
    });
  },  "Setting .src to a TrustedScriptURL should work and should execute the referenced script instead of the script body, on a onnected script element.");

</script>
</body>
</html>
