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 };