summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpinstall/slowinstall.sjs
blob: e2a889c3299e71348eb65455dc5b665b1f1b9ea2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// In an SJS file we need to get NetUtil ourselves, despite
// what eslint might think applies for browser tests.
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
let { NetUtil } = ChromeUtils.importESModule(
  "resource://gre/modules/NetUtil.sys.mjs"
);

const RELATIVE_PATH = "browser/toolkit/mozapps/extensions/test/xpinstall";
const NOTIFICATION_TOPIC = "slowinstall-complete";

/**
 * Helper function to create a JS object representing the url parameters from
 * the request's queryString.
 *
 * @param  aQueryString
 *         The request's query string.
 * @return A JS object representing the url parameters from the request's
 *         queryString.
 */
function parseQueryString(aQueryString) {
  var paramArray = aQueryString.split("&");
  var regex = /^([^=]+)=(.*)$/;
  var params = {};
  for (var i = 0, sz = paramArray.length; i < sz; i++) {
    var match = regex.exec(paramArray[i]);
    if (!match) {
      throw new Error("Bad parameter in queryString!  '" + paramArray[i] + "'");
    }
    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
  }

  return params;
}

function handleRequest(aRequest, aResponse) {
  let id = +getState("ID");
  setState("ID", "" + (id + 1));

  function LOG(str) {
    dump("slowinstall.sjs[" + id + "]: " + str + "\n");
  }

  aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");

  var params = {};
  if (aRequest.queryString) {
    params = parseQueryString(aRequest.queryString);
  }

  if (params.file) {
    let xpiFile = "";

    function complete_download() {
      LOG("Completing download");

      try {
        // Doesn't seem to be a sane way to read using IOUtils and write to an
        // nsIOutputStream so here we are.
        let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
        file.initWithPath(xpiFile);
        let stream = Cc[
          "@mozilla.org/network/file-input-stream;1"
        ].createInstance(Ci.nsIFileInputStream);
        stream.init(file, -1, -1, stream.DEFER_OPEN + stream.CLOSE_ON_EOF);

        NetUtil.asyncCopy(stream, aResponse.bodyOutputStream, () => {
          LOG("Download complete");
          aResponse.finish();
        });
      } catch (e) {
        LOG("Exception " + e);
      }
    }

    let waitForComplete = new Promise(resolve => {
      function complete() {
        Services.obs.removeObserver(complete, NOTIFICATION_TOPIC);
        resolve();
      }

      Services.obs.addObserver(complete, NOTIFICATION_TOPIC);
    });

    aResponse.processAsync();

    const dir = Services.dirsvc.get("CurWorkD", Ci.nsIFile).path;
    xpiFile = PathUtils.join(dir, ...RELATIVE_PATH.split("/"), params.file);
    LOG("Starting slow download of " + xpiFile);

    IOUtils.stat(xpiFile).then(info => {
      aResponse.setHeader("Content-Type", "binary/octet-stream");
      aResponse.setHeader("Content-Length", info.size.toString());

      LOG("Download paused");
      waitForComplete.then(complete_download);
    });
  } else if (params.continue) {
    dump(
      "slowinstall.sjs: Received signal to complete all current downloads.\n"
    );
    Services.obs.notifyObservers(null, NOTIFICATION_TOPIC);
  }
}