summaryrefslogtreecommitdiffstats
path: root/dom/security/test/https-first/browser_slow_download.js
blob: 1583c6d3617c244d5ff85a7b6c43ac23e9107162 (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"use strict";

ChromeUtils.defineESModuleGetters(this, {
  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
});
// Create a uri for an https site
const testPath = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content",
  "https://example.com"
);
const TEST_URI = testPath + "file_slow_download.html";
const EXPECTED_DOWNLOAD_URL =
  "example.com/browser/dom/security/test/https-first/file_slow_download.sjs";

// Since the server send the complete download file after 3 seconds we need an longer timeout
requestLongerTimeout(4);

function promisePanelOpened() {
  if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
    return Promise.resolve();
  }
  return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown");
}

/**
 * Waits for a download to finish, in case it has not finished already.
 *
 * @param aDownload
 *        The Download object to wait upon.
 *
 * @return {Promise}
 * @resolves When the download has finished successfully.
 * @rejects JavaScript exception if the download failed.
 */
function promiseDownloadStopped(aDownload) {
  if (!aDownload.stopped) {
    // The download is in progress, wait for the current attempt to finish and
    // report any errors that may occur.
    return aDownload.start();
  }

  if (aDownload.succeeded) {
    return Promise.resolve();
  }

  // The download failed or was canceled.
  return Promise.reject(aDownload.error || new Error("Download canceled."));
}

// Verifys that no background request was send
let requestCounter = 0;
function examiner() {
  SpecialPowers.addObserver(this, "specialpowers-http-notify-request");
}

examiner.prototype = {
  observe(subject, topic, data) {
    if (topic !== "specialpowers-http-notify-request") {
      return;
    }
    // On Android we have other requests appear here as well. Let's make
    // sure we only evaluate requests triggered by the test.
    if (
      !data.startsWith("http://example.com") &&
      !data.startsWith("https://example.com")
    ) {
      return;
    }
    ++requestCounter;
    if (requestCounter == 1) {
      is(data, TEST_URI, "Download start page is https");
      return;
    }
    if (requestCounter == 2) {
      // The specialpowers-http-notify-request fires before the internal redirect( /upgrade) to
      // https happens.
      is(
        data,
        "http://" + EXPECTED_DOWNLOAD_URL,
        "First download request is http (internal)"
      );
      return;
    }
    if (requestCounter == 3) {
      is(
        data,
        "https://" + EXPECTED_DOWNLOAD_URL,
        "Download got upgraded to https"
      );
      return;
    }
    ok(false, "we should never get here, but just in case");
  },
  remove() {
    SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
  },
};

// Test description:
// 1. Open https://example.com
// 2. Start download - location of download is http
// 3. https-first upgrades to https
// 4. Server send first part of download and after 3 seconds the rest
// 5. Complete download of text file
add_task(async function test_slow_download() {
  await SpecialPowers.pushPrefEnv({
    set: [["dom.security.https_first", true]],
  });

  // remove all previous downloads
  let downloadsList = await Downloads.getList(Downloads.PUBLIC);
  await downloadsList.removeFinished();

  // add observer to ensure that the background request gets canceled for the upgraded Download
  this.examiner = new examiner();

  let downloadsPanelPromise = promisePanelOpened();
  let downloadsPromise = Downloads.getList(Downloads.PUBLIC);
  BrowserTestUtils.startLoadingURIString(gBrowser, TEST_URI);
  // wait for downloadsPanel to open before continuing with test
  await downloadsPanelPromise;
  let downloadList = await downloadsPromise;
  is(DownloadsPanel.isPanelShowing, true, "DownloadsPanel should be open.");
  is(downloadList._downloads.length, 1, "File should be downloaded.");
  let [download] = downloadList._downloads;
  // wait for download to finish (with success or error)
  await promiseDownloadStopped(download);
  is(download.contentType, "text/plain", "File contentType should be correct.");
  // ensure https-first did upgrade the scheme.
  is(
    download.source.url,
    "https://" + EXPECTED_DOWNLOAD_URL,
    "Scheme should be https."
  );
  // ensure that no background request was send
  is(
    requestCounter,
    3,
    "three requests total (download page, download http, download https/ upgraded)"
  );
  // ensure that downloaded is complete
  is(download.target.size, 25, "Download size is correct");
  //clean up
  this.examiner.remove();
  info("cleaning up downloads");
  try {
    if (Services.appinfo.OS === "WINNT") {
      // We need to make the file writable to delete it on Windows.
      await IOUtils.setPermissions(download.target.path, 0o600);
    }
    await IOUtils.remove(download.target.path);
  } catch (error) {
    info("The file " + download.target.path + " is not removed, " + error);
  }

  await downloadList.remove(download);
  await download.finalize();
});