/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs", }); const L10MessageSelectorMap = { "crashed:open": "file-picker-crashed-open", "crashed:save-somewhere": "file-picker-crashed-save-somewhere", "crashed:save-nowhere": "file-picker-crashed-save-nowhere", "failed:open": "file-picker-failed-open", "failed:save-somewhere": "file-picker-failed-save-somewhere", "failed:save-nowhere": "file-picker-failed-save-nowhere", }; export const FilePickerCrashed = { async observe(subject, topic, _data) { const bag = subject.QueryInterface(Ci.nsIPropertyBag2); const ctx = bag.getPropertyAsInterface("ctx", Ci.nsILoadContext); const nbox = (() => { let window = ctx.topChromeWindow; // If our associated window isn't a browser window (e.g., it's an // extension window or the History window or the like), just grab the // topmost browser window. if (!window?.gBrowser) { window = lazy.BrowserWindowTracker.getTopWindow(); } // If there _is_ no topmost browser window... throw an error and hope it // shows up in logs somewhere? if (!window) { const err = new Error( "file picker crashed, but no browser windows were available to report this" ); // (for further investigation via the browser console, if that's accessible) console.error({ err, bag, ctx }); throw err; } // This will get the notification-box for the window's currently-shown // tab, which may or may not be the tab which attempted to spawn a // file-dialog. This only really matters for delayed-open but instafailing // save-dialogs; hopefully the filename will presumably be sufficient to // disambiguate. return window.gBrowser.getNotificationBox(); })(); const mode = bag.getPropertyAsUint32("mode"); const isCrash = bag.getPropertyAsBool("crash"); const isOpen = mode != Ci.nsIFilePicker.modeSave; const file = (() => { if (isOpen) { return null; } try { return bag.getPropertyAsInterface("file", Ci.nsIFile); } catch (e) { // property presumably not present; proceed } try { // This probably isn't user-actionable, but may be useful to developers const file_error = bag.getPropertyAsUint32("file-error"); console.error( "Failed to get fallback file location: nsresult 0x" + file_error.toString(16).padLeft(8, 0) ); } catch (e) { // Report this meta-error to the browser console; then continue onward to // also report the original failure. console.error(e); } return null; })(); const cause = isCrash ? "crashed" : "failed"; const consequence = isOpen ? "open" : "save-" + (file ? "somewhere" : "nowhere"); const msgId = L10MessageSelectorMap[cause + ":" + consequence]; const label = { "l10n-id": msgId, "l10n-args": file ? { path: file.path } : {}, }; const buttons = []; /* TODO(rkraesig): add "More Info" button? */ // Offer to show the file's location, if one is provided. if (file) { buttons.push({ "l10n-id": "file-picker-crashed-show-in-folder", callback() { lazy.DownloadsCommon.showDownloadedFile(file); }, }); } const notification = await nbox.appendNotification( topic, { label, image: "chrome://global/skin/icons/error.svg", priority: nbox.PRIORITY_CRITICAL_LOW, }, buttons ); // Persist the notification until the user removes it. notification.persistence = -1; }, };