summaryrefslogtreecommitdiffstats
path: root/dom/security/test/https-only/browser_save_as.js
blob: fbfdf276a89f08fcc6700befd093f2563aa405ba (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"use strict";

Services.scriptloader.loadSubScript(
  "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
  this
);

// Using insecure HTTP URL for a test cases around HTTP/HTTPS download interaction
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
const HTTP_LINK = `http://example.org/`;
const HTTPS_LINK = `https://example.org/`;
const TEST_PATH =
  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
  "http://example.com/browser/dom/security/test/https-only/file_save_as.html";

let MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window.browsingContext);
const tempDir = createTemporarySaveDirectory();
MockFilePicker.displayDirectory = tempDir;

add_setup(async function () {
  info("Setting MockFilePicker.");
  mockTransferRegisterer.register();

  registerCleanupFunction(function () {
    mockTransferRegisterer.unregister();
    MockFilePicker.cleanup();
    tempDir.remove(true);
  });
});

function createTemporarySaveDirectory() {
  let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
  saveDir.append("testsavedir");
  saveDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
  return saveDir;
}

function createPromiseForObservingChannel(expectedUrl) {
  return new Promise(resolve => {
    let observer = (aSubject, aTopic) => {
      if (aTopic === "http-on-modify-request") {
        let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);

        if (httpChannel.URI.spec != expectedUrl) {
          return;
        }

        Services.obs.removeObserver(observer, "http-on-modify-request");
        resolve();
      }
    };

    Services.obs.addObserver(observer, "http-on-modify-request");
  });
}

function createPromiseForTransferComplete() {
  return new Promise(resolve => {
    MockFilePicker.showCallback = fp => {
      info("MockFilePicker showCallback");

      let fileName = fp.defaultString;
      let destFile = tempDir.clone();
      destFile.append(fileName);

      MockFilePicker.setFiles([destFile]);
      MockFilePicker.filterIndex = 0; // kSaveAsType_Complete

      MockFilePicker.showCallback = null;
      mockTransferCallback = function (downloadSuccess) {
        ok(downloadSuccess, "File should have been downloaded successfully");
        mockTransferCallback = () => {};
        resolve();
      };
    };
  });
}

function createPromiseForConsoleError(message) {
  return new Promise(resolve => {
    function listener(msgObj) {
      let text = msgObj.message;
      if (text.includes(message)) {
        info(`Found occurence of '${message}'`);
        Services.console.unregisterListener(listener);
        resolve();
      }
    }
    Services.console.registerListener(listener);
  });
}

async function runTest(selector, expectedUrl, expectedError) {
  info(`Open a new tab for testing "Save link as" in context menu.`);
  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH);

  let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");

  let browser = gBrowser.selectedBrowser;

  info("Open the context menu.");
  await BrowserTestUtils.synthesizeMouseAtCenter(
    selector,
    {
      type: "contextmenu",
      button: 2,
    },
    browser
  );

  await popupShownPromise;

  let downloadEndPromise = expectedError
    ? createPromiseForConsoleError(expectedError)
    : createPromiseForTransferComplete();
  let observerPromise = createPromiseForObservingChannel(expectedUrl);

  let contextMenu = document.getElementById("contentAreaContextMenu");
  let popupHiddenPromise = BrowserTestUtils.waitForEvent(
    contextMenu,
    "popuphidden"
  );

  // Select "Save As" option from context menu.
  let saveElement = document.getElementById(`context-savelink`);
  info("Triggering the save process.");
  contextMenu.activateItem(saveElement);

  info("Waiting for the channel.");
  await observerPromise;

  info(
    expectedError
      ? "Waiting for error in console."
      : "Wait until the save is finished."
  );
  await downloadEndPromise;

  info("Wait until the menu is closed.");
  await popupHiddenPromise;

  BrowserTestUtils.removeTab(tab);
}

async function setHttpsFirstAndOnlyPrefs(httpsFirst, httpsOnly) {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["dom.security.https_first", httpsFirst],
      ["dom.security.https_only_mode", httpsOnly],
    ],
  });
}

add_task(async function testBaseline() {
  // Run with HTTPS-First and HTTPS-Only disabled
  await setHttpsFirstAndOnlyPrefs(false, false);
  await runTest(
    "#insecure-link",
    HTTP_LINK,
    "We blocked a download that’s not secure: “http://example.org/”."
  );
  await runTest("#secure-link", HTTPS_LINK, undefined);
});

add_task(async function testHttpsFirst() {
  // Run with HTTPS-First enabled
  // The the user will get a warning about really wanting to download
  // from a insecure site, because we upgraded the top level document,
  // but the download is still insecure. In the future we also want to
  // upgrade these Save-As downloads.
  await setHttpsFirstAndOnlyPrefs(true, false);
  await runTest(
    "#insecure-link",
    HTTP_LINK,
    "We blocked a download that’s not secure: “http://example.org/”."
  );
  await runTest("#secure-link", HTTPS_LINK, undefined);
});

add_task(async function testHttpsOnly() {
  // Run with HTTPS-Only enabled
  // Should have same behaviour as HTTPS-First
  await setHttpsFirstAndOnlyPrefs(false, true);
  await runTest(
    "#insecure-link",
    HTTP_LINK,
    "We blocked a download that’s not secure: “http://example.org/”."
  );
  await runTest("#secure-link", HTTPS_LINK, undefined);
});