tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_test_mixed_content_download.js (10133B)


      1 ChromeUtils.defineESModuleGetters(this, {
      2  Downloads: "resource://gre/modules/Downloads.sys.mjs",
      3  DownloadsCommon:
      4    "moz-src:///browser/components/downloads/DownloadsCommon.sys.mjs",
      5 });
      6 
      7 const HandlerService = Cc[
      8  "@mozilla.org/uriloader/handler-service;1"
      9 ].getService(Ci.nsIHandlerService);
     10 
     11 const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
     12 
     13 let INSECURE_BASE_URL =
     14  getRootDirectory(gTestPath).replace(
     15    "chrome://mochitests/content/",
     16    "http://example.com/"
     17  ) + "download_page.html";
     18 let SECURE_BASE_URL =
     19  getRootDirectory(gTestPath).replace(
     20    "chrome://mochitests/content/",
     21    "https://example.com/"
     22  ) + "download_page.html";
     23 
     24 function promiseFocus() {
     25  return new Promise(resolve => {
     26    waitForFocus(resolve);
     27  });
     28 }
     29 
     30 function promisePanelOpened() {
     31  if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
     32    return Promise.resolve();
     33  }
     34  return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown");
     35 }
     36 
     37 async function task_openPanel() {
     38  await promiseFocus();
     39 
     40  let promise = promisePanelOpened();
     41  DownloadsPanel.showPanel();
     42  await promise;
     43 }
     44 
     45 const downloadMonitoringView = {
     46  _listeners: [],
     47  onDownloadAdded(download) {
     48    for (let listener of this._listeners) {
     49      listener(download);
     50    }
     51    this._listeners = [];
     52  },
     53  waitForDownload(listener) {
     54    this._listeners.push(listener);
     55  },
     56 };
     57 
     58 /**
     59 * Waits until a download is triggered.
     60 * Unless the always_ask_before_handling_new_types pref is true, the download
     61 * will simply be saved, so resolve when the view is notified of the new
     62 * download. Otherwise, it waits until a prompt is shown, selects the choosen
     63 * <action>, then accepts the dialog
     64 *
     65 * @param [action] Which action to select, either:
     66 *        "handleInternally", "save" or "open".
     67 * @returns {Promise} Resolved once done.
     68 */
     69 
     70 function shouldTriggerDownload(action = "save") {
     71  if (
     72    Services.prefs.getBoolPref(
     73      "browser.download.always_ask_before_handling_new_types"
     74    )
     75  ) {
     76    return new Promise((resolve, reject) => {
     77      Services.wm.addListener({
     78        onOpenWindow(xulWin) {
     79          Services.wm.removeListener(this);
     80          let win = xulWin.docShell.domWindow;
     81          waitForFocus(() => {
     82            if (
     83              win.location ==
     84              "chrome://mozapps/content/downloads/unknownContentType.xhtml"
     85            ) {
     86              let dialog = win.document.getElementById("unknownContentType");
     87              let button = dialog.getButton("accept");
     88              let actionRadio = win.document.getElementById(action);
     89              actionRadio.click();
     90              button.disabled = false;
     91              dialog.acceptDialog();
     92              resolve();
     93            } else {
     94              reject();
     95            }
     96          }, win);
     97        },
     98      });
     99    });
    100  }
    101  return new Promise(res => {
    102    downloadMonitoringView.waitForDownload(res);
    103  });
    104 }
    105 
    106 const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content";
    107 
    108 function shouldConsoleError() {
    109  // Waits until CONSOLE_ERROR_MESSAGE was logged
    110  return new Promise((resolve, reject) => {
    111    function listener(msgObj) {
    112      let text = msgObj.message;
    113      if (text.includes(CONSOLE_ERROR_MESSAGE)) {
    114        Services.console.unregisterListener(listener);
    115        resolve();
    116      }
    117    }
    118    Services.console.registerListener(listener);
    119  });
    120 }
    121 
    122 async function resetDownloads() {
    123  // Removes all downloads from the download List
    124  const types = new Set();
    125  let publicList = await Downloads.getList(Downloads.PUBLIC);
    126  let downloads = await publicList.getAll();
    127  for (let download of downloads) {
    128    if (download.contentType) {
    129      types.add(download.contentType);
    130    }
    131    publicList.remove(download);
    132    await download.finalize(true);
    133  }
    134 
    135  if (types.size) {
    136    // reset handlers for the contentTypes of any files previously downloaded
    137    for (let type of types) {
    138      const mimeInfo = MIMEService.getFromTypeAndExtension(type, "");
    139      info("resetting handler for type: " + type);
    140      HandlerService.remove(mimeInfo);
    141    }
    142  }
    143 }
    144 
    145 function shouldNotifyDownloadUI() {
    146  return new Promise(res => {
    147    downloadMonitoringView.waitForDownload(async aDownload => {
    148      let { error } = aDownload;
    149      if (
    150        error.becauseBlockedByReputationCheck &&
    151        error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
    152      ) {
    153        // It's an insecure Download, now Check that it has been cleaned up properly
    154        if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
    155          throw new Error(`Download target is not empty!`);
    156        }
    157        if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
    158          throw new Error(`Download partFile was not cleaned up properly`);
    159        }
    160        // Assert that the Referrer is presnt
    161        if (!aDownload.source.referrerInfo) {
    162          throw new Error("The Blocked download is missing the ReferrerInfo");
    163        }
    164 
    165        res(aDownload);
    166      } else {
    167        ok(false, "No error for download that was expected to error!");
    168      }
    169    });
    170  });
    171 }
    172 
    173 async function runTest(url, link, checkFunction, description) {
    174  await SpecialPowers.pushPrefEnv({
    175    set: [["dom.block_download_insecure", true]],
    176  });
    177  await resetDownloads();
    178 
    179  let tab = BrowserTestUtils.addTab(gBrowser, url);
    180  gBrowser.selectedTab = tab;
    181 
    182  let browser = gBrowser.getBrowserForTab(tab);
    183  await BrowserTestUtils.browserLoaded(browser);
    184 
    185  info("Checking: " + description);
    186 
    187  let checkPromise = checkFunction();
    188  // Click the Link to trigger the download
    189  SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
    190    content.document.getElementById(contentLink).click();
    191  });
    192 
    193  await checkPromise;
    194 
    195  ok(true, description);
    196  BrowserTestUtils.removeTab(tab);
    197 
    198  await SpecialPowers.popPrefEnv();
    199 }
    200 
    201 add_setup(async () => {
    202  let list = await Downloads.getList(Downloads.ALL);
    203  list.addView(downloadMonitoringView);
    204  registerCleanupFunction(() => list.removeView(downloadMonitoringView));
    205 });
    206 
    207 // Test Blocking
    208 add_task(async function test_blocking() {
    209  for (let prefVal of [true, false]) {
    210    await SpecialPowers.pushPrefEnv({
    211      set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
    212    });
    213    await runTest(
    214      INSECURE_BASE_URL,
    215      "insecure",
    216      shouldTriggerDownload,
    217      "Insecure -> Insecure should download"
    218    );
    219    await runTest(
    220      INSECURE_BASE_URL,
    221      "secure",
    222      shouldTriggerDownload,
    223      "Insecure -> Secure should download"
    224    );
    225    await runTest(
    226      SECURE_BASE_URL,
    227      "insecure",
    228      () =>
    229        Promise.all([
    230          shouldTriggerDownload(),
    231          shouldNotifyDownloadUI(),
    232          shouldConsoleError(),
    233        ]),
    234      "Secure -> Insecure should Error"
    235    );
    236    await runTest(
    237      SECURE_BASE_URL,
    238      "secure",
    239      shouldTriggerDownload,
    240      "Secure -> Secure should Download"
    241    );
    242    await SpecialPowers.popPrefEnv();
    243  }
    244 });
    245 
    246 // Test Manual Unblocking
    247 add_task(async function test_manual_unblocking() {
    248  for (let prefVal of [true, false]) {
    249    await SpecialPowers.pushPrefEnv({
    250      set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
    251    });
    252    await runTest(
    253      SECURE_BASE_URL,
    254      "insecure",
    255      async () => {
    256        let [, download] = await Promise.all([
    257          shouldTriggerDownload(),
    258          shouldNotifyDownloadUI(),
    259        ]);
    260        await download.unblock();
    261        Assert.equal(
    262          download.error,
    263          null,
    264          "There should be no error after unblocking"
    265        );
    266      },
    267      "A Blocked Download Should succeeded to Download after a Manual unblock"
    268    );
    269    await SpecialPowers.popPrefEnv();
    270  }
    271 });
    272 
    273 // Test Unblock Download Visible
    274 add_task(async function test_unblock_download_visible() {
    275  for (let prefVal of [true, false]) {
    276    await SpecialPowers.pushPrefEnv({
    277      set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
    278    });
    279    // Focus, open and close the panel once
    280    // to make sure the panel is loaded and ready
    281    await promiseFocus();
    282    await runTest(
    283      SECURE_BASE_URL,
    284      "insecure",
    285      async () => {
    286        let panelHasOpened = promisePanelOpened();
    287        info("awaiting that the download is triggered and added to the list");
    288        await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]);
    289        info("awaiting that the Download list shows itself");
    290        await panelHasOpened;
    291        DownloadsPanel.hidePanel();
    292        ok(true, "The Download Panel should have opened on blocked download");
    293      },
    294      "A Blocked Download Should open the Download Panel"
    295    );
    296    await SpecialPowers.popPrefEnv();
    297  }
    298 });
    299 
    300 // Test Download an insecure svg and choose "Open with Firefox"
    301 add_task(async function download_open_insecure_SVG() {
    302  const mimeInfo = MIMEService.getFromTypeAndExtension("image/svg+xml", "svg");
    303  mimeInfo.alwaysAskBeforeHandling = false;
    304  mimeInfo.preferredAction = mimeInfo.handleInternally;
    305  HandlerService.store(mimeInfo);
    306 
    307  await SpecialPowers.pushPrefEnv({
    308    set: [["browser.download.always_ask_before_handling_new_types", false]],
    309  });
    310  await promiseFocus();
    311  await runTest(
    312    SECURE_BASE_URL,
    313    "insecureSVG",
    314    async () => {
    315      info("awaiting that the download is triggered and added to the list");
    316      let [_, download] = await Promise.all([
    317        shouldTriggerDownload("handleInternally"),
    318        shouldNotifyDownloadUI(),
    319      ]);
    320 
    321      let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
    322      await download.unblock();
    323      Assert.equal(
    324        download.error,
    325        null,
    326        "There should be no error after unblocking"
    327      );
    328 
    329      let tab = await newTabPromise;
    330 
    331      ok(
    332        tab.linkedBrowser._documentURI.filePath.includes(".svg"),
    333        "The download target was opened"
    334      );
    335      BrowserTestUtils.removeTab(tab);
    336      ok(true, "The Content was opened in a new tab");
    337      await SpecialPowers.popPrefEnv();
    338    },
    339    "A Blocked SVG can be opened internally"
    340  );
    341 
    342  HandlerService.remove(mimeInfo);
    343 });