summaryrefslogtreecommitdiffstats
path: root/browser/actors/PluginParent.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/actors/PluginParent.sys.mjs')
-rw-r--r--browser/actors/PluginParent.sys.mjs212
1 files changed, 212 insertions, 0 deletions
diff --git a/browser/actors/PluginParent.sys.mjs b/browser/actors/PluginParent.sys.mjs
new file mode 100644
index 0000000000..d3ed150211
--- /dev/null
+++ b/browser/actors/PluginParent.sys.mjs
@@ -0,0 +1,212 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ CrashSubmit: "resource://gre/modules/CrashSubmit.sys.mjs",
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "gNavigatorBundle", function () {
+ const url = "chrome://browser/locale/browser.properties";
+ return Services.strings.createBundle(url);
+});
+
+export const PluginManager = {
+ gmpCrashes: new Map(),
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case "gmp-plugin-crash":
+ this._registerGMPCrash(subject);
+ break;
+ }
+ },
+
+ _registerGMPCrash(subject) {
+ let propertyBag = subject;
+ if (
+ !(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
+ !propertyBag.hasKey("pluginID") ||
+ !propertyBag.hasKey("pluginDumpID") ||
+ !propertyBag.hasKey("pluginName")
+ ) {
+ console.error("PluginManager can not read plugin information.");
+ return;
+ }
+
+ let pluginID = propertyBag.getPropertyAsUint32("pluginID");
+ let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
+ let pluginName = propertyBag.getPropertyAsACString("pluginName");
+ if (pluginDumpID) {
+ this.gmpCrashes.set(pluginID, { pluginDumpID, pluginID, pluginName });
+ }
+
+ // Only the parent process gets the gmp-plugin-crash observer
+ // notification, so we need to inform any content processes that
+ // the GMP has crashed. This then fires PluginCrashed events in
+ // all the relevant windows, which will trigger child actors being
+ // created, which will contact us again, when we'll use the
+ // gmpCrashes collection to respond.
+ if (Services.ppmm) {
+ Services.ppmm.broadcastAsyncMessage("gmp-plugin-crash", {
+ pluginName,
+ pluginID,
+ });
+ }
+ },
+
+ /**
+ * Submit a crash report for a crashed plugin.
+ *
+ * @param pluginCrashID
+ * An object with a pluginID.
+ * @param keyVals
+ * An object whose key-value pairs will be merged
+ * with the ".extra" file submitted with the report.
+ * The properties of htis object will override properties
+ * of the same name in the .extra file.
+ */
+ submitCrashReport(pluginCrashID, keyVals = {}) {
+ let report = this.getCrashReport(pluginCrashID);
+ if (!report) {
+ console.error(
+ `Could not find plugin dump IDs for ${JSON.stringify(pluginCrashID)}.` +
+ `It is possible that a report was already submitted.`
+ );
+ return;
+ }
+
+ let { pluginDumpID } = report;
+ lazy.CrashSubmit.submit(
+ pluginDumpID,
+ lazy.CrashSubmit.SUBMITTED_FROM_CRASH_TAB,
+ {
+ recordSubmission: true,
+ extraExtraKeyVals: keyVals,
+ }
+ );
+
+ this.gmpCrashes.delete(pluginCrashID.pluginID);
+ },
+
+ getCrashReport(pluginCrashID) {
+ return this.gmpCrashes.get(pluginCrashID.pluginID);
+ },
+};
+
+export class PluginParent extends JSWindowActorParent {
+ receiveMessage(msg) {
+ let browser = this.manager.rootFrameLoader.ownerElement;
+ switch (msg.name) {
+ case "PluginContent:ShowPluginCrashedNotification":
+ this.showPluginCrashedNotification(browser, msg.data.pluginCrashID);
+ break;
+
+ default:
+ console.error(
+ "PluginParent did not expect to handle message ",
+ msg.name
+ );
+ break;
+ }
+
+ return null;
+ }
+
+ /**
+ * Shows a plugin-crashed notification bar for a browser that has had a
+ * GMP plugin crash.
+ *
+ * @param browser
+ * The browser to show the notification for.
+ * @param pluginCrashID
+ * The unique-per-process identifier for GMP.
+ */
+ showPluginCrashedNotification(browser, pluginCrashID) {
+ // If there's already an existing notification bar, don't do anything.
+ let notificationBox = browser.getTabBrowser().getNotificationBox(browser);
+ let notification =
+ notificationBox.getNotificationWithValue("plugin-crashed");
+
+ let report = PluginManager.getCrashReport(pluginCrashID);
+ if (notification || !report) {
+ return;
+ }
+
+ // Configure the notification bar
+ let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+ let iconURL = "chrome://global/skin/icons/plugin.svg";
+ let reloadLabel = lazy.gNavigatorBundle.GetStringFromName(
+ "crashedpluginsMessage.reloadButton.label"
+ );
+ let reloadKey = lazy.gNavigatorBundle.GetStringFromName(
+ "crashedpluginsMessage.reloadButton.accesskey"
+ );
+
+ let buttons = [
+ {
+ label: reloadLabel,
+ accessKey: reloadKey,
+ popup: null,
+ callback() {
+ browser.reload();
+ },
+ },
+ ];
+
+ if (AppConstants.MOZ_CRASHREPORTER) {
+ let submitLabel = lazy.gNavigatorBundle.GetStringFromName(
+ "crashedpluginsMessage.submitButton.label"
+ );
+ let submitKey = lazy.gNavigatorBundle.GetStringFromName(
+ "crashedpluginsMessage.submitButton.accesskey"
+ );
+ let submitButton = {
+ label: submitLabel,
+ accessKey: submitKey,
+ popup: null,
+ callback: () => {
+ PluginManager.submitCrashReport(pluginCrashID);
+ },
+ };
+
+ buttons.push(submitButton);
+ }
+
+ let messageString = lazy.gNavigatorBundle.formatStringFromName(
+ "crashedpluginsMessage.title",
+ [report.pluginName]
+ );
+ notification = notificationBox.appendNotification(
+ "plugin-crashed",
+ {
+ label: messageString,
+ image: iconURL,
+ priority,
+ },
+ buttons
+ );
+
+ // Add the "learn more" link.
+ let link = notification.ownerDocument.createXULElement("label", {
+ is: "text-link",
+ });
+ link.setAttribute(
+ "value",
+ lazy.gNavigatorBundle.GetStringFromName("crashedpluginsMessage.learnMore")
+ );
+ let crashurl = Services.urlFormatter.formatURLPref("app.support.baseURL");
+ crashurl += "plugin-crashed-notificationbar";
+ link.href = crashurl;
+ // Append a blank text node to make sure we don't put
+ // the link right next to the end of the message text.
+ notification.messageText.appendChild(new Text(" "));
+ notification.messageText.appendChild(link);
+ }
+}