PluginParent.sys.mjs (6019B)
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 7 8 const lazy = {}; 9 10 ChromeUtils.defineESModuleGetters(lazy, { 11 CrashSubmit: "resource://gre/modules/CrashSubmit.sys.mjs", 12 }); 13 14 ChromeUtils.defineLazyGetter(lazy, "gNavigatorBundle", function () { 15 const url = "chrome://browser/locale/browser.properties"; 16 return Services.strings.createBundle(url); 17 }); 18 19 export const PluginManager = { 20 gmpCrashes: new Map(), 21 22 observe(subject, topic) { 23 switch (topic) { 24 case "gmp-plugin-crash": 25 this._registerGMPCrash(subject); 26 break; 27 } 28 }, 29 30 _registerGMPCrash(subject) { 31 let propertyBag = subject; 32 if ( 33 !(propertyBag instanceof Ci.nsIWritablePropertyBag2) || 34 !propertyBag.hasKey("pluginID") || 35 !propertyBag.hasKey("pluginDumpID") || 36 !propertyBag.hasKey("pluginName") 37 ) { 38 console.error("PluginManager can not read plugin information."); 39 return; 40 } 41 42 let pluginID = propertyBag.getPropertyAsUint32("pluginID"); 43 let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID"); 44 let pluginName = propertyBag.getPropertyAsACString("pluginName"); 45 if (pluginDumpID) { 46 this.gmpCrashes.set(pluginID, { pluginDumpID, pluginID, pluginName }); 47 } 48 49 // Only the parent process gets the gmp-plugin-crash observer 50 // notification, so we need to inform any content processes that 51 // the GMP has crashed. This then fires PluginCrashed events in 52 // all the relevant windows, which will trigger child actors being 53 // created, which will contact us again, when we'll use the 54 // gmpCrashes collection to respond. 55 if (Services.ppmm) { 56 Services.ppmm.broadcastAsyncMessage("gmp-plugin-crash", { 57 pluginName, 58 pluginID, 59 }); 60 } 61 }, 62 63 /** 64 * Submit a crash report for a crashed plugin. 65 * 66 * @param pluginCrashID 67 * An object with a pluginID. 68 * @param keyVals 69 * An object whose key-value pairs will be merged 70 * with the ".extra" file submitted with the report. 71 * The properties of htis object will override properties 72 * of the same name in the .extra file. 73 */ 74 submitCrashReport(pluginCrashID, keyVals = {}) { 75 let report = this.getCrashReport(pluginCrashID); 76 if (!report) { 77 console.error( 78 `Could not find plugin dump IDs for ${JSON.stringify(pluginCrashID)}.` + 79 `It is possible that a report was already submitted.` 80 ); 81 return; 82 } 83 84 let { pluginDumpID } = report; 85 lazy.CrashSubmit.submit( 86 pluginDumpID, 87 lazy.CrashSubmit.SUBMITTED_FROM_CRASH_TAB, 88 { 89 recordSubmission: true, 90 extraExtraKeyVals: keyVals, 91 } 92 ); 93 94 this.gmpCrashes.delete(pluginCrashID.pluginID); 95 }, 96 97 getCrashReport(pluginCrashID) { 98 return this.gmpCrashes.get(pluginCrashID.pluginID); 99 }, 100 }; 101 102 export class PluginParent extends JSWindowActorParent { 103 receiveMessage(msg) { 104 let browser = this.manager.rootFrameLoader.ownerElement; 105 switch (msg.name) { 106 case "PluginContent:ShowPluginCrashedNotification": 107 this.showPluginCrashedNotification(browser, msg.data.pluginCrashID); 108 break; 109 110 default: 111 console.error( 112 "PluginParent did not expect to handle message ", 113 msg.name 114 ); 115 break; 116 } 117 118 return null; 119 } 120 121 /** 122 * Shows a plugin-crashed notification bar for a browser that has had a 123 * GMP plugin crash. 124 * 125 * @param browser 126 * The browser to show the notification for. 127 * @param pluginCrashID 128 * The unique-per-process identifier for GMP. 129 */ 130 showPluginCrashedNotification(browser, pluginCrashID) { 131 // If there's already an existing notification bar, don't do anything. 132 let notificationBox = browser.getTabBrowser().getNotificationBox(browser); 133 let notification = 134 notificationBox.getNotificationWithValue("plugin-crashed"); 135 136 let report = PluginManager.getCrashReport(pluginCrashID); 137 if (notification || !report) { 138 return; 139 } 140 141 // Configure the notification bar 142 let priority = notificationBox.PRIORITY_WARNING_MEDIUM; 143 let iconURL = "chrome://global/skin/icons/plugin.svg"; 144 let reloadLabel = lazy.gNavigatorBundle.GetStringFromName( 145 "crashedpluginsMessage.reloadButton.label" 146 ); 147 let reloadKey = lazy.gNavigatorBundle.GetStringFromName( 148 "crashedpluginsMessage.reloadButton.accesskey" 149 ); 150 151 let buttons = [ 152 { 153 label: reloadLabel, 154 accessKey: reloadKey, 155 popup: null, 156 callback() { 157 browser.reload(); 158 }, 159 }, 160 ]; 161 162 if (AppConstants.MOZ_CRASHREPORTER) { 163 let submitLabel = lazy.gNavigatorBundle.GetStringFromName( 164 "crashedpluginsMessage.submitButton.label" 165 ); 166 let submitKey = lazy.gNavigatorBundle.GetStringFromName( 167 "crashedpluginsMessage.submitButton.accesskey" 168 ); 169 let submitButton = { 170 label: submitLabel, 171 accessKey: submitKey, 172 popup: null, 173 callback: () => { 174 PluginManager.submitCrashReport(pluginCrashID); 175 }, 176 }; 177 178 buttons.push(submitButton); 179 } 180 181 // Add the "learn more" link. 182 let learnMoreLink = { 183 supportPage: "plugin-crashed-notificationbar", 184 label: lazy.gNavigatorBundle.GetStringFromName( 185 "crashedpluginsMessage.learnMore" 186 ), 187 }; 188 buttons.push(learnMoreLink); 189 190 let messageString = lazy.gNavigatorBundle.formatStringFromName( 191 "crashedpluginsMessage.title", 192 [report.pluginName] 193 ); 194 notificationBox.appendNotification( 195 "plugin-crashed", 196 { 197 label: messageString, 198 image: iconURL, 199 priority, 200 }, 201 buttons 202 ); 203 } 204 }