<!DOCTYPE html>
<html>
  <head>
    <title>Test for sending IndexedDB Blobs through MessageManager</title>
    <script src="/tests/SimpleTest/SimpleTest.js">
    </script>
    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  </head>
  <body>
    <script type="application/javascript">
"use strict";

async function chromeScriptFunc() {
  function childFrameScript() {
    /* eslint-env mozilla/frame-script */
    "use strict";

    const mmName = "test:idb-and-mm";

    const dbName = "test_message_manager_ipc.html - CHILD";
    const dbVersion = 1;
    const objStoreName = "bar";
    const key = 1;

    const blobData = ["So", " ", "many", " ", "blobs!"];
    const blobText = blobData.join("");
    const blobType = "text/plain";

    Cu.importGlobalProperties(["indexedDB"]);

    function info(msg) {
      sendAsyncMessage(mmName, { op: "info", msg });
    }

    function ok(condition, name, diag) {
      sendAsyncMessage(mmName,
                       { op: "ok",
                         condition,
                         name,
                         diag });
    }

    function is(a, b, name) {
      let pass = a == b;
      let diag = pass ? "" : "got " + a + ", expected " + b;
      ok(pass, name, diag);
    }

    function finish(result) {
      sendAsyncMessage(mmName, { op: "done", result });
    }

    function grabAndContinue(arg) {
      testGenerator.next(arg);
    }

    function errorHandler(event) {
      ok(false,
         event.target + " received error event: '" + event.target.error.name +
         "'");
      finish();
    }

    function* testSteps() {
      addMessageListener(mmName, grabAndContinue);
      let message = yield undefined;

      let blob = message.data;

      ok(Blob.isInstance(blob), "Message manager sent a blob");
      is(blob.size, blobText.length, "Blob has correct length");
      is(blob.type, blobType, "Blob has correct type");

      info("Reading blob");

      let reader = new FileReader();
      reader.addEventListener("load", grabAndContinue);
      reader.readAsText(blob);

      yield undefined;

      is(reader.result, blobText, "Blob has correct data");

      let slice = blob.slice(0, blobData[0].length, blobType);

      ok(Blob.isInstance(slice), "Slice returned a blob");
      is(slice.size, blobData[0].length, "Slice has correct length");
      is(slice.type, blobType, "Slice has correct type");

      info("Reading slice");

      reader = new FileReader();
      reader.addEventListener("load", grabAndContinue);
      reader.readAsText(slice);

      yield undefined;

      is(reader.result, blobData[0], "Slice has correct data");

      info("Deleting database");

      let req = indexedDB.deleteDatabase(dbName);
      req.onerror = errorHandler;
      req.onsuccess = grabAndContinue;

      let event = yield undefined;
      is(event.type, "success", "Got success event");

      info("Opening database");

      req = indexedDB.open(dbName, dbVersion);
      req.onerror = errorHandler;
      req.onupgradeneeded = grabAndContinue;
      req.onsuccess = grabAndContinue;

      event = yield undefined;
      is(event.type, "upgradeneeded", "Got upgradeneeded event");

      event.target.result.createObjectStore(objStoreName);

      event = yield undefined;
      is(event.type, "success", "Got success event");

      let db = event.target.result;

      info("Storing blob from message manager in database");

      let objectStore =
        db.transaction(objStoreName, "readwrite").objectStore(objStoreName);
      req = objectStore.add(blob, key);
      req.onerror = errorHandler;
      req.onsuccess = grabAndContinue;

      event = yield undefined;

      info("Getting blob from database");

      objectStore = db.transaction(objStoreName).objectStore(objStoreName);
      req = objectStore.get(key);
      req.onerror = errorHandler;
      req.onsuccess = grabAndContinue;

      event = yield undefined;

      blob = event.target.result;

      ok(Blob.isInstance(blob), "Database gave us a blob");
      is(blob.size, blobText.length, "Blob has correct length");
      is(blob.type, blobType, "Blob has correct type");

      info("Reading blob");

      reader = new FileReader();
      reader.addEventListener("load", grabAndContinue);
      reader.readAsText(blob);

      yield undefined;

      is(reader.result, blobText, "Blob has correct data");

      info("Storing slice from message manager in database");

      objectStore =
        db.transaction(objStoreName, "readwrite").objectStore(objStoreName);
      req = objectStore.put(slice, key);
      req.onerror = errorHandler;
      req.onsuccess = grabAndContinue;

      event = yield undefined;

      info("Getting slice from database");

      objectStore = db.transaction(objStoreName).objectStore(objStoreName);
      req = objectStore.get(key);
      req.onerror = errorHandler;
      req.onsuccess = grabAndContinue;

      event = yield undefined;

      slice = event.target.result;

      ok(Blob.isInstance(slice), "Database gave us a blob");
      is(slice.size, blobData[0].length, "Slice has correct length");
      is(slice.type, blobType, "Slice has correct type");

      info("Reading Slice");

      reader = new FileReader();
      reader.addEventListener("load", grabAndContinue);
      reader.readAsText(slice);

      yield undefined;

      is(reader.result, blobData[0], "Slice has correct data");

      info("Sending blob and slice from database to message manager");
      finish([blob, slice]);
    }

    let testGenerator = testSteps();
    testGenerator.next();
  }

  /* globals Services, createWindowlessBrowser */
  let { windowlessBrowser, browser } = await createWindowlessBrowser();

  const system = Services.scriptSecurityManager.getSystemPrincipal();
  browser.loadURI(
    Services.io.newURI("data:text/html,<!DOCTYPE HTML><html><body></body></html>"),
    { triggeringPrincipal: system }
  );

  function finish() {
    sendAsyncMessage("done");
    windowlessBrowser.close();
  }

  let mm = browser.messageManager;

  const messageName = "test:idb-and-mm";
  const blobData = ["So", " ", "many", " ", "blobs!"];
  const blobText = blobData.join("");
  const blobType = "text/plain";
  const blob = new Blob(blobData, { type: blobType });

  function grabAndContinue(arg) {
    testGenerator.next(arg);
  }

  function* testSteps() {
    let result = yield undefined;

    is(Cu.getClassName(result, true), "Array", "Child delivered an array of results");
    is(result.length, 2, "Child delivered two results");

    let blob = result[0];
    is(Blob.isInstance(blob), true, "Child delivered a blob");
    is(blob.size, blobText.length, "Blob has correct size");
    is(blob.type, blobType, "Blob has correct type");

    let slice = result[1];
    is(Blob.isInstance(slice), true, "Child delivered a slice");
    is(slice.size, blobData[0].length, "Slice has correct size");
    is(slice.type, blobType, "Slice has correct type");

    info("Reading blob");

    let reader = new FileReader();
    reader.onload = grabAndContinue;
    reader.readAsText(blob);
    yield undefined;

    is(reader.result, blobText, "Blob has correct data");

    info("Reading slice");

    reader = new FileReader();
    reader.onload = grabAndContinue;
    reader.readAsText(slice);
    yield undefined;

    is(reader.result, blobData[0], "Slice has correct data");

    slice = blob.slice(0, blobData[0].length, blobType);

    is(Blob.isInstance(slice), true, "Child delivered a slice");
    is(slice.size, blobData[0].length, "Second slice has correct size");
    is(slice.type, blobType, "Second slice has correct type");

    info("Reading second slice");

    reader = new FileReader();
    reader.onload = grabAndContinue;
    reader.readAsText(slice);
    yield undefined;

    is(reader.result, blobData[0], "Second slice has correct data");

    finish();
  }

  let testGenerator = testSteps();
  testGenerator.next();

  mm.addMessageListener(messageName, function(message) {
    let data = message.data;
    switch (data.op) {
      case "info": {
        info(data.msg);
        break;
      }

      case "ok": {
        ok(data.condition, data.name + " - " + data.diag);
        break;
      }

      case "done": {
        testGenerator.next(data.result);
        break;
      }

      default: {
        ok(false, "Unknown op: " + data.op);
        finish();
      }
    }
  });

  mm.loadFrameScript(`data:,(${childFrameScript})();`,
                      false);

  mm.sendAsyncMessage(messageName, blob);
}

add_task(async function() {
  let chromeScript = SpecialPowers.loadChromeScript(chromeScriptFunc);
  await chromeScript.promiseOneMessage("done");
  await chromeScript.destroy();
});
    </script>
  </body>
</html>
