tor-browser

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

FilePickerCrashed.sys.mjs (4131B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
      9  DownloadsCommon:
     10    "moz-src:///browser/components/downloads/DownloadsCommon.sys.mjs",
     11 });
     12 
     13 const L10MessageSelectorMap = {
     14  "crashed:open": "file-picker-crashed-open",
     15  "crashed:save-somewhere": "file-picker-crashed-save-somewhere",
     16  "crashed:save-nowhere": "file-picker-crashed-save-nowhere",
     17  "failed:open": "file-picker-failed-open",
     18  "failed:save-somewhere": "file-picker-failed-save-somewhere",
     19  "failed:save-nowhere": "file-picker-failed-save-nowhere",
     20 };
     21 
     22 export const FilePickerCrashed = {
     23  async observe(subject, topic, _data) {
     24    const bag = subject.QueryInterface(Ci.nsIPropertyBag2);
     25    const ctx = bag.getPropertyAsInterface("ctx", Ci.nsILoadContext);
     26 
     27    const nbox = (() => {
     28      let window = ctx.topChromeWindow;
     29 
     30      // If our associated window isn't a browser window (e.g., it's an
     31      // extension window or the History window or the like), just grab the
     32      // topmost browser window.
     33      if (!window?.gBrowser) {
     34        window = lazy.BrowserWindowTracker.getTopWindow({
     35          allowFromInactiveWorkspace: true,
     36        });
     37      }
     38 
     39      // If there _is_ no topmost browser window... throw an error and hope it
     40      // shows up in logs somewhere?
     41      if (!window) {
     42        const err = new Error(
     43          "file picker crashed, but no browser windows were available to report this"
     44        );
     45        // (for further investigation via the browser console, if that's accessible)
     46        console.error({ err, bag, ctx });
     47        throw err;
     48      }
     49 
     50      // This will get the notification-box for the window's currently-shown
     51      // tab, which may or may not be the tab which attempted to spawn a
     52      // file-dialog. This only really matters for delayed-open but instafailing
     53      // save-dialogs; hopefully the filename will presumably be sufficient to
     54      // disambiguate.
     55      return window.gBrowser.getNotificationBox();
     56    })();
     57 
     58    const mode = bag.getPropertyAsUint32("mode");
     59 
     60    const isCrash = bag.getPropertyAsBool("crash");
     61 
     62    const isOpen = mode != Ci.nsIFilePicker.modeSave;
     63    const file = (() => {
     64      if (isOpen) {
     65        return null;
     66      }
     67 
     68      try {
     69        return bag.getPropertyAsInterface("file", Ci.nsIFile);
     70      } catch (e) {
     71        // property presumably not present; proceed
     72      }
     73 
     74      try {
     75        // This probably isn't user-actionable, but may be useful to developers
     76        const file_error = bag.getPropertyAsUint32("file-error");
     77        console.error(
     78          "Failed to get fallback file location: nsresult 0x" +
     79            file_error.toString(16).padLeft(8, 0)
     80        );
     81      } catch (e) {
     82        // Report this meta-error to the browser console; then continue onward to
     83        // also report the original failure.
     84        console.error(e);
     85      }
     86 
     87      return null;
     88    })();
     89 
     90    const cause = isCrash ? "crashed" : "failed";
     91    const consequence = isOpen
     92      ? "open"
     93      : "save-" + (file ? "somewhere" : "nowhere");
     94    const msgId = L10MessageSelectorMap[cause + ":" + consequence];
     95 
     96    const label = {
     97      "l10n-id": msgId,
     98      "l10n-args": file ? { path: file.path } : {},
     99    };
    100 
    101    const buttons = [];
    102 
    103    /* TODO(rkraesig): add "More Info" button? */
    104 
    105    // Offer to show the file's location, if one is provided.
    106    if (file) {
    107      buttons.push({
    108        "l10n-id": "file-picker-crashed-show-in-folder",
    109        callback() {
    110          lazy.DownloadsCommon.showDownloadedFile(file);
    111        },
    112      });
    113    }
    114 
    115    const notification = await nbox.appendNotification(
    116      topic,
    117      {
    118        label,
    119        image: "chrome://global/skin/icons/error.svg",
    120        priority: nbox.PRIORITY_CRITICAL_LOW,
    121      },
    122      buttons
    123    );
    124    // Persist the notification until the user removes it.
    125    notification.persistence = -1;
    126  },
    127 };