dom/workers/test/serviceworkers/fetch/context/index.html
author Ehsan Akhgari <ehsan@mozilla.com>
Mon, 15 Jun 2015 23:20:02 -0400
changeset 281380 09bc59954db83980b50c88fef9c72b2613c5cca3
parent 281376 7210dfe7ad781fd39aedea2e78be669c76532c33
child 281565 f46a712edf7ee9088854b220661df6857453f69c
permissions -rw-r--r--
Backed out changeset 7210dfe7ad78 (Bug 1147668) for test failure

<!DOCTYPE html>
<script>
  var isAndroid = navigator.userAgent.includes("Android");
  var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent);

  function ok(v, msg) {
    window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
  }

  function is(a, b, msg) {
    ok(a === b, msg + ", expected '" + b + "', got '" + a + "'");
  }

  function todo(v, msg) {
    window.parent.postMessage({status: "todo", result: !!v, message: msg}, "*");
  }

  function finish() {
    window.parent.postMessage({status: "done"}, "*");
  }

  function testFetch() {
    return fetch("fetch.txt").then(function(r) {
      return r.text();
    }).then(function(body) {
      is(body, "so fetch", "A fetch() Request should have the 'fetch' context");
    });
  }

  function testImage() {
    return new Promise(function(resolve, reject) {
      var img = document.createElement("img");
      img.src = "img.jpg";
      // The service worker will respond with an existing image only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      img.onload = resolve;
      img.onerror = reject;
    });
  }

  function testImageSrcSet() {
    return new Promise(function(resolve, reject) {
      var img = document.createElement("img");
      img.srcset = "responsive.jpg 100w";
      // The service worker will respond with an existing image only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      img.onload = resolve;
      img.onerror = reject;
    });
  }

  function testPicture() {
    return new Promise(function(resolve, reject) {
      var pic = document.createElement("picture");
      var img = document.createElement("img");
      pic.appendChild(img);
      img.src = "responsive.jpg?picture";
      // The service worker will respond with an existing image only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      img.onload = resolve;
      img.onerror = reject;
    });
  }

  function testAudio() {
    return new Promise(function(resolve, reject) {
      var audio = document.createElement("audio");
      audio.src = "audio.ogg";
      audio.preload = "metadata";
      // The service worker will respond with an existing audio only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      audio.onloadedmetadata = resolve;
      audio.onerror = reject;
    });
  }

  function testVideo() {
    return new Promise(function(resolve, reject) {
      var video = document.createElement("video");
      video.src = "video.ogg";
      video.preload = "metadata";
      // The service worker will respond with an existing video only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      video.onloadedmetadata = resolve;
      video.onerror = reject;
    });
  }

  function testBeacon() {
    ok(navigator.sendBeacon("beacon.sjs"), "Sending the beacon should succeed");
    // query the context from beacon.sjs
    return fetch("beacon.sjs?queryContext")
      .then(function(r) {
        return r.text();
      }).then(function(body) {
        is(body, "beacon", "The context for the intercepted beacon should be correct");
      });
  }

  function testCSPReport() {
    return new Promise(function(resolve, reject) {
      var iframe = document.createElement("iframe");
      iframe.src = "csp-violate.sjs";
      document.documentElement.appendChild(iframe);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "csp-report") {
          is(e.data.context, "cspreport", "Expected the cspreport context on a CSP violation report");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testEmbed() {
    return Promise.resolve().then(function() {
      todo(false, "<embed> tag is not currently intercepted. See Bug 1168676.");
    });

    return new Promise(function(resolve, reject) {
      var embed = document.createElement("embed");
      embed.src = "embed";
      document.documentElement.appendChild(embed);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "embed") {
          // FIXME: Bug 1148030: This should be "embed".
          is(e.data.context, "object", "Expected the object context on an embed");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testObject() {
    return Promise.resolve().then(function() {
      todo(false, "<object> tag is not currently intercepted. See Bug 1168676");
    });

    return new Promise(function(resolve, reject) {
      var object = document.createElement("object");
      object.data = "object";
      document.documentElement.appendChild(object);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "object") {
          is(e.data.context, "object", "Expected the object context on an object");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testFont() {
    return new Promise(function(resolve, reject) {
      var css = '@font-face { font-family: "sw-font"; src: url("font"); }';
      css += '* { font-family: "sw-font"; }';
      var style = document.createElement("style");
      style.appendChild(document.createTextNode(css));
      document.documentElement.appendChild(style);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "font") {
          is(e.data.context, "font", "Expected the font context on an font");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testIFrame() {
    return new Promise(function(resolve, reject) {
      var iframe = document.createElement("iframe");
      iframe.src = "iframe";
      document.documentElement.appendChild(iframe);
      // The service worker will respond with an existing document only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      iframe.onload = resolve;
      iframe.onerror = reject;
    });
  }

  function testFrame() {
    return new Promise(function(resolve, reject) {
      var frame = document.createElement("frame");
      frame.src = "frame";
      document.documentElement.appendChild(frame);
      // The service worker will respond with an existing document only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      frame.onload = resolve;
      frame.onerror = reject;
    });
  }

  function testInternal() {
    if (isB2G) {
      // We can't open new windows on b2g, so skip this part of the test there.
      return Promise.resolve();
    }
    return new Promise(function(resolve, reject) {
      // Test this with a new window opened through script.  There are of course
      // other possible ways of testing this too.
      var win = window.open("newwindow", "_blank", "width=100,height=100");
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "newwindow") {
          is(e.data.context, "internal", "Expected the internal context on a newly opened window");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          win.close();
          resolve();
        }
      }, false);
    });
  }

  function testPing() {
    return new Promise(function(resolve, reject) {
      var iframe = document.createElement("iframe");
      iframe.src = "ping.html";
      document.documentElement.appendChild(iframe);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "ping") {
          is(e.data.context, "ping", "Expected the ping context on an anchor ping");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testPlugin() {
    return Promise.resolve().then(function() {
      todo(false, "plugins are not currently intercepted. See Bug 1168676.");
    });

    var isMobile = /Mobile|Tablet/.test(navigator.userAgent);
    if (isMobile || parent.isMulet()) {
      // We can't use plugins on mobile, so skip this part of the test there.
      return Promise.resolve();
    }

    return new Promise(function(resolve, reject) {
      var embed = document.createElement("embed");
      embed.type = "application/x-test";
      embed.setAttribute("posturl", "plugin");
      embed.setAttribute("postmode", "stream");
      embed.setAttribute("streammode", "normal");
      embed.setAttribute("src", "fetch.txt");
      document.documentElement.appendChild(embed);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "plugin") {
          is(e.data.context, "plugin", "Expected the plugin context on a request coming from a plugin");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          // Without this, the test leaks in e10s!
          embed.parentNode.removeChild(embed);
          resolve();
        }
      }, false);
    });
  }

  function testScript() {
    return new Promise(function(resolve, reject) {
      var script = document.createElement("script");
      script.src = "script.js";
      document.documentElement.appendChild(script);
      // The service worker will respond with an existing script only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      script.onload = resolve;
      script.onerror = reject;
    });
  }

  function testStyle() {
    return new Promise(function(resolve, reject) {
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = "style.css";
      document.documentElement.appendChild(link);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "style") {
          is(e.data.context, "style", "Expected the style context on a request coming from a stylesheet");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testTrack() {
    return new Promise(function(resolve, reject) {
      var video = document.createElement("video");
      var track = document.createElement("track");
      track.src = "track";
      video.appendChild(track);
      document.documentElement.appendChild(video);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "track") {
          // FIXME: Bug 1147668: This should be "track".
          is(e.data.context, "audio", "Expected the audio context on a request coming from a track");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  function testXHR() {
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.open("get", "xhr", true);
      xhr.send();
      // The service worker will respond with an existing resource only if the
      // Request has the correct context, otherwise the Promise will get
      // rejected and the test will fail.
      xhr.onload = resolve;
      xhr.onerror = reject;
    });
  }

  function testXSLT() {
    return new Promise(function(resolve, reject) {
      var iframe = document.createElement("iframe");
      iframe.src = "xml.xml";
      document.documentElement.appendChild(iframe);
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "xslt") {
          is(e.data.context, "xslt", "Expected the xslt context on an XSLT stylesheet");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          // Without this, the test leaks in e10s!
          iframe.parentNode.removeChild(iframe);
          resolve();
        }
      }, false);
    });
  }

  function testCache() {
    return new Promise(function(resolve, reject) {
      // Issue an XHR that will be intercepted by the SW in order to start off
      // the test with a RequestContext value that is not the default ("fetch").
      // This needs to run inside a fetch event handler because synthesized
      // RequestContext objects can only have the "fetch" context, and we'd
      // prefer to test the more general case of some other RequestContext value.
      var xhr = new XMLHttpRequest();
      xhr.open("get", "cache", true);
      xhr.send();
      navigator.serviceWorker.addEventListener("message", function onMessage(e) {
        if (e.data.data == "cache") {
          ok(e.data.success, "The RequestContext can be persisted in the cache.");
          navigator.serviceWorker.removeEventListener("message", onMessage);
          resolve();
        }
      }, false);
    });
  }

  Promise.all([
    testFetch(),
    testImage(),
    testImageSrcSet(),
    testPicture(),
    testAudio(),
    testVideo(),
    testBeacon(),
    testCSPReport(),
    testEmbed(),
    testObject(),
    testFont(),
    testIFrame(),
    testFrame(),
    testInternal(),
    testPing(),
    testPlugin(),
    testScript(),
    testStyle(),
    testTrack(),
    testXHR(),
    testXSLT(),

    // Also, test to see if the type of the request can be persisted in the database.
    testCache(),
  ])
  .then(function() {
    finish();
  }, function(e) {
    ok(false, "A promise was rejected: " + e);
    finish();
  });
</script>