rename from browser/modules/PluginContent.jsm
rename to browser/actors/PluginChild.jsm
--- a/browser/modules/PluginContent.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -1,66 +1,63 @@
/* 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/. */
"use strict";
-var EXPORTED_SYMBOLS = [ "PluginContent" ];
+var EXPORTED_SYMBOLS = ["PluginChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
const url = "chrome://browser/locale/browser.properties";
return Services.strings.createBundle(url);
});
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
-var PluginContent = function(global) {
- this.init(global);
-};
-
const OVERLAY_DISPLAY = {
HIDDEN: 0, // The overlay will be transparent
BLANK: 1, // The overlay will be just a grey box
TINY: 2, // The overlay with a 16x16 plugin icon
REDUCED: 3, // The overlay with a 32x32 plugin icon
NOTEXT: 4, // The overlay with a 48x48 plugin icon and the close button
FULL: 5, // The full overlay: 48x48 plugin icon, close button and label
};
-PluginContent.prototype = {
- init(global) {
- this.global = global;
- // Need to hold onto the content window or else it'll get destroyed
- this.content = this.global.content;
+class PluginChild extends ActorChild {
+ constructor(mm) {
+ super(mm);
+
// Cache of plugin actions for the current page.
this.pluginData = new Map();
// Cache of plugin crash information sent from the parent
this.pluginCrashData = new Map();
- global.addEventListener("pagehide", this, true);
- global.addEventListener("pageshow", this, true);
- },
+ this.mm.addEventListener("pagehide", this, true);
+ this.mm.addEventListener("pageshow", this, true);
+ }
receiveMessage(msg) {
switch (msg.name) {
case "BrowserPlugins:ActivatePlugins":
this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
break;
case "BrowserPlugins:NotificationShown":
setTimeout(() => this.updateNotificationUI(), 0);
break;
case "BrowserPlugins:ContextMenuCommand":
- let contextMenu = this.global.contextMenu;
+ let contextMenu = this.mm.contextMenu;
switch (msg.data.command) {
case "play":
this._showClickToPlayNotification(contextMenu.getTarget(msg, "plugin"), true);
break;
case "hide":
this.hideClickToPlayOverlay(contextMenu.getTarget(msg, "plugin"));
break;
@@ -80,61 +77,61 @@ PluginContent.prototype = {
});
break;
case "BrowserPlugins:Test:ClearCrashData":
// This message should ONLY ever be sent by automated tests.
if (Services.prefs.getBoolPref("plugins.testmode")) {
this.pluginCrashData.clear();
}
}
- },
+ }
- observe: function observe(aSubject, aTopic, aData) {
+ observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "decoder-doctor-notification":
let data = JSON.parse(aData);
let type = data.type.toLowerCase();
if (type == "cannot-play" &&
this.haveShownNotification &&
aSubject.top.document == this.content.document &&
data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
- this.global.content.pluginRequiresReload = true;
+ this.content.pluginRequiresReload = true;
this.updateNotificationUI(this.content.document);
}
}
- },
+ }
onPageShow(event) {
// Ignore events that aren't from the main document.
if (!this.content || event.target != this.content.document) {
return;
}
// The PluginClickToPlay events are not fired when navigating using the
// BF cache. |persisted| is true when the page is loaded from the
// BF cache, so this code reshows the notification if necessary.
if (event.persisted) {
this.reshowClickToPlayNotification();
}
- },
+ }
onPageHide(event) {
// Ignore events that aren't from the main document.
if (!this.content || event.target != this.content.document) {
return;
}
this.clearPluginCaches();
this.haveShownNotification = false;
- },
+ }
getPluginUI(plugin, anonid) {
return plugin.ownerDocument.
getAnonymousElementByAttribute(plugin, "anonid", anonid);
- },
+ }
_getPluginInfo(pluginElement) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
let tagMimetype;
let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
let pluginTag = null;
@@ -173,17 +170,17 @@ PluginContent.prototype = {
return { mimetype: tagMimetype,
pluginName,
pluginTag,
permissionString,
fallbackType,
blocklistState,
};
- },
+ }
/**
* _getPluginInfoForTag is called when iterating the plugins for a document,
* and what we get from nsIDOMWindowUtils is an nsIPluginTag, and not an
* nsIObjectLoadingContent. This only should happen if the plugin is
* click-to-play (see bug 1186948).
*/
_getPluginInfoForTag(pluginTag, tagMimetype) {
@@ -222,27 +219,27 @@ PluginContent.prototype = {
permissionString,
// Since we should only have entered _getPluginInfoForTag when
// examining a click-to-play plugin, we can safely hard-code
// this fallback type, since we don't actually have an
// nsIObjectLoadingContent to check.
fallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
blocklistState,
};
- },
+ }
/**
* Update the visibility of the plugin overlay.
*/
setVisibility(plugin, overlay, overlayDisplayState) {
overlay.classList.toggle("visible", overlayDisplayState != OVERLAY_DISPLAY.HIDDEN);
if (overlayDisplayState != OVERLAY_DISPLAY.HIDDEN) {
overlay.removeAttribute("dismissed");
}
- },
+ }
/**
* Adjust the style in which the overlay will be displayed. It might be adjusted
* based on its size, or if there's some other element covering all corners of
* the overlay.
*
* This function will handle adjusting the style of the overlay, but will
* not handle hiding it. That is done by setVisibility with the return value
@@ -333,17 +330,17 @@ PluginContent.prototype = {
let el = cwu.elementFromPoint(x, y, true, true);
if (el === plugin) {
return overlayDisplay;
}
}
overlay.setAttribute("sizing", "blank");
return OVERLAY_DISPLAY.BLANK;
- },
+ }
addLinkClickCallback(linkNode, callbackName /* callbackArgs...*/) {
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
let self = this;
let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
linkNode.addEventListener("click",
function(evt) {
if (!evt.isTrusted)
@@ -363,17 +360,17 @@ PluginContent.prototype = {
evt.preventDefault();
if (callbackArgs.length == 0)
callbackArgs = [ evt ];
evt.preventDefault();
(self[callbackName]).apply(self, callbackArgs);
}
},
true);
- },
+ }
// Helper to get the binding handler type from a plugin object
_getBindingType(plugin) {
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
return null;
switch (plugin.pluginFallbackType) {
case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
@@ -390,17 +387,17 @@ PluginContent.prototype = {
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
return "PluginVulnerableUpdatable";
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
return "PluginVulnerableNoUpdate";
default:
// Not all states map to a handler
return null;
}
- },
+ }
handleEvent(event) {
let eventType = event.type;
if (eventType == "pagehide") {
this.onPageHide(event);
return;
}
@@ -533,22 +530,22 @@ PluginContent.prototype = {
overlay.setAttribute("dismissed", "true");
}
}, true);
}
if (shouldShowNotification) {
this._showClickToPlayNotification(plugin, false);
}
- },
+ }
isKnownPlugin(objLoadingContent) {
return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
- },
+ }
canActivatePlugin(objLoadingContent) {
// if this isn't a known plugin, we can't activate it
// (this also guards pluginHost.getPermissionStringForType against
// unexpected input)
if (!this.isKnownPlugin(objLoadingContent))
return false;
@@ -559,32 +556,32 @@ PluginContent.prototype = {
let isFallbackTypeValid =
objLoadingContent.pluginFallbackType >= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
return !objLoadingContent.activated &&
pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
isFallbackTypeValid;
- },
+ }
hideClickToPlayOverlay(plugin) {
let overlay = this.getPluginUI(plugin, "main");
if (overlay) {
overlay.classList.remove("visible");
}
- },
+ }
// Forward a link click callback to the chrome process.
forwardCallback(name, pluginTag) {
- this.global.sendAsyncMessage("PluginContent:LinkClickCallback",
+ this.mm.sendAsyncMessage("PluginContent:LinkClickCallback",
{ name, pluginTag });
- },
+ }
- submitReport: function submitReport(plugin) {
+ submitReport(plugin) {
if (!AppConstants.MOZ_CRASHREPORTER) {
return;
}
if (!plugin) {
Cu.reportError("Attempted to submit crash report without an associated plugin.");
return;
}
if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
@@ -597,23 +594,23 @@ PluginContent.prototype = {
let submitURLOptIn = this.getPluginUI(plugin, "submitURLOptIn").checked;
let keyVals = {};
let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
if (userComment)
keyVals.PluginUserComment = userComment;
if (submitURLOptIn)
keyVals.PluginContentURL = plugin.ownerDocument.URL;
- this.global.sendAsyncMessage("PluginContent:SubmitReport",
+ this.mm.sendAsyncMessage("PluginContent:SubmitReport",
{ runID, keyVals, submitURLOptIn });
- },
+ }
reloadPage() {
- this.global.content.location.reload();
- },
+ this.content.location.reload();
+ }
// Event listener for click-to-play plugins.
_handleClickToPlayEvent(plugin) {
let doc = plugin.ownerDocument;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
// guard against giving pluginHost.getPermissionStringForType a type
// not associated with any known plugin
@@ -631,54 +628,54 @@ PluginContent.prototype = {
overlay.classList.remove("visible");
}
return;
}
if (overlay) {
overlay.addEventListener("click", this, true);
}
- },
+ }
onOverlayClick(event) {
let document = event.target.ownerDocument;
let plugin = document.getBindingParent(event.target);
let overlay = this.getPluginUI(plugin, "main");
// Have to check that the target is not the link to update the plugin
if (!(ChromeUtils.getClassName(event.originalTarget) === "HTMLAnchorElement") &&
(event.originalTarget.getAttribute("anonid") != "closeIcon") &&
!overlay.hasAttribute("dismissed") &&
event.button == 0 &&
event.isTrusted) {
this._showClickToPlayNotification(plugin, true);
event.stopPropagation();
event.preventDefault();
}
- },
+ }
reshowClickToPlayNotification() {
- let contentWindow = this.global.content;
+ let contentWindow = this.content;
let cwu = contentWindow.windowUtils;
let plugins = cwu.plugins;
for (let plugin of plugins) {
let overlay = this.getPluginUI(plugin, "main");
if (overlay)
overlay.removeEventListener("click", this, true);
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (this.canActivatePlugin(objLoadingContent))
this._handleClickToPlayEvent(plugin);
}
this._showClickToPlayNotification(null, false);
- },
+ }
/**
* Activate the plugins that the user has specified.
*/
activatePlugins(pluginInfo, newState) {
- let contentWindow = this.global.content;
+ let contentWindow = this.content;
let cwu = contentWindow.windowUtils;
let plugins = cwu.plugins;
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginFound = false;
for (let plugin of plugins) {
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (!this.isKnownPlugin(plugin)) {
@@ -705,17 +702,17 @@ PluginContent.prototype = {
// If there are no instances of the plugin on the page any more or if we've
// noted that the content needs to be reloaded due to replacing HLS, what the
// user probably needs is for us to allow and then refresh.
if (newState != "block" && newState != "blockalways" && newState != "continueblocking" &&
(!pluginFound || contentWindow.pluginRequiresReload)) {
this.reloadPage();
}
this.updateNotificationUI();
- },
+ }
_showClickToPlayNotification(plugin, showNow) {
let plugins = [];
// If plugin is null, that means the user has navigated back to a page with
// plugins, and we need to collect all the plugins.
if (plugin === null) {
let contentWindow = this.content;
@@ -763,22 +760,22 @@ PluginContent.prototype = {
pluginInfo.pluginPermissionType = undefined;
}
this.pluginData.set(pluginInfo.permissionString, pluginInfo);
}
this.haveShownNotification = true;
- this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
+ this.mm.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
plugins: [...this.pluginData.values()],
showNow,
location,
}, null, principal);
- },
+ }
/**
* Updates the "hidden plugin" notification bar UI.
*
* @param document (optional)
* Specify the document that is causing the update.
* This is useful when the document is possibly no longer
* the current loaded document (for example, if we're
@@ -846,35 +843,35 @@ PluginContent.prototype = {
if (actions.size == 0) {
break;
}
}
}
// If there are any items remaining in `actions` now, they are hidden
// plugins that need a notification bar.
- this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
+ this.mm.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
haveInsecure,
actions: [...actions.values()],
location,
}, null, principal);
- },
+ }
removeNotification(name) {
- this.global.sendAsyncMessage("PluginContent:RemoveNotification", { name });
- },
+ this.mm.sendAsyncMessage("PluginContent:RemoveNotification", { name });
+ }
clearPluginCaches() {
this.pluginData.clear();
this.pluginCrashData.clear();
- },
+ }
hideNotificationBar(name) {
- this.global.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
- },
+ this.mm.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
+ }
/**
* Determines whether or not the crashed plugin is contained within current
* full screen DOM element.
* @param fullScreenElement (DOM element)
* The DOM element that is currently full screen, or null.
* @param domElement
* The DOM element which contains the crashed plugin, or the crashed plugin
@@ -905,17 +902,17 @@ PluginContent.prototype = {
if (fullScreenElement.contains(domElement)) {
return true;
}
let parentIframe = domElement.ownerGlobal.frameElement;
if (parentIframe) {
return this.isWithinFullScreenElement(fullScreenElement, parentIframe);
}
return false;
- },
+ }
/**
* The PluginCrashed event handler. Note that the PluginCrashed event is
* fired for both NPAPI and Gecko Media plugins. In the latter case, the
* target of the event is the document that the GMP is being used in.
*/
onPluginCrashed(target, aEvent) {
if (!(aEvent instanceof this.content.PluginCrashedEvent))
@@ -949,24 +946,24 @@ PluginContent.prototype = {
this.pluginCrashData.delete(target.runID);
}
this.setCrashedNPAPIPluginState({
plugin: target,
state: crashData.state,
message: crashData.message,
});
- },
+ }
NPAPIPluginProcessCrashed({pluginName, runID, state}) {
let message =
gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
[pluginName], 1);
- let contentWindow = this.global.content;
+ let contentWindow = this.content;
let cwu = contentWindow.windowUtils;
let plugins = cwu.plugins;
for (let plugin of plugins) {
if (plugin instanceof Ci.nsIObjectLoadingContent &&
plugin.runID == runID) {
// The parent has told us that the plugin process has died.
// It's possible that this content process hasn't yet noticed,
@@ -989,17 +986,17 @@ PluginContent.prototype = {
instances: new WeakSet(),
});
}
let crashData = this.pluginCrashData.get(runID);
crashData.instances.add(plugin);
}
}
}
- },
+ }
setCrashedNPAPIPluginState({plugin, state, message}) {
// Force a layout flush so the binding is attached.
plugin.clientTop;
let overlay = this.getPluginUI(plugin, "main");
let statusDiv = this.getPluginUI(plugin, "submitStatus");
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
@@ -1048,39 +1045,39 @@ PluginContent.prototype = {
// Notify others that the crash reporter UI is now ready.
// Currently, this event is only used by tests.
let winUtils = this.content.windowUtils;
let event = new this.content.CustomEvent("PluginCrashReporterDisplayed", {bubbles: true});
winUtils.dispatchEventToChromeOnly(plugin, event);
} else if (!doc.mozNoPluginCrashedNotification) {
// If another plugin on the page was large enough to show our UI, we don't
// want to show a notification bar.
- this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+ this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
{ messageString: message, pluginID: runID });
// Remove the notification when the page is reloaded.
doc.defaultView.top.addEventListener("unload", event => {
this.hideNotificationBar("plugin-crashed");
});
}
- },
+ }
NPAPIPluginCrashReportSubmitted({ runID, state }) {
this.pluginCrashData.delete(runID);
- let contentWindow = this.global.content;
+ let contentWindow = this.content;
let cwu = contentWindow.windowUtils;
let plugins = cwu.plugins;
for (let plugin of plugins) {
if (plugin instanceof Ci.nsIObjectLoadingContent &&
plugin.runID == runID) {
let statusDiv = this.getPluginUI(plugin, "submitStatus");
statusDiv.setAttribute("status", state);
}
}
- },
+ }
GMPCrashed(aEvent) {
let target = aEvent.target;
let pluginName = aEvent.pluginName;
let gmpPlugin = aEvent.gmpPlugin;
let pluginID = aEvent.pluginID;
let doc = target.document;
@@ -1088,17 +1085,17 @@ PluginContent.prototype = {
// TODO: Throw exception? How did we get here?
return;
}
let messageString =
gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
[pluginName], 1);
- this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+ this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
{ messageString, pluginID });
// Remove the notification when the page is reloaded.
doc.defaultView.top.addEventListener("unload", event => {
this.hideNotificationBar("plugin-crashed");
});
- },
-};
+ }
+}
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -17,17 +17,16 @@ var global = this;
XPCOMUtils.defineLazyModuleGetters(this, {
BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
- PluginContent: "resource:///modules/PluginContent.jsm",
FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
NetErrorContent: "resource:///modules/NetErrorContent.jsm",
PageMetadata: "resource://gre/modules/PageMetadata.jsm",
WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
ContextMenuChild: "resource:///modules/ContextMenuChild.jsm",
});
XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
@@ -153,79 +152,16 @@ this.AboutNetAndCertErrorListener = {
NetErrorContent.handleEvent(global, aEvent);
},
};
AboutNetAndCertErrorListener.init(this);
new ContentLinkHandler(this);
ContentMetaHandler.init(this);
-var PluginContentStub = {
- EVENTS: [
- "PluginCrashed",
- "PluginOutdated",
- "PluginInstantiated",
- "PluginRemoved",
- "HiddenPlugin",
- ],
-
- MESSAGES: [
- "BrowserPlugins:ActivatePlugins",
- "BrowserPlugins:NotificationShown",
- "BrowserPlugins:ContextMenuCommand",
- "BrowserPlugins:NPAPIPluginProcessCrashed",
- "BrowserPlugins:CrashReportSubmitted",
- "BrowserPlugins:Test:ClearCrashData",
- ],
-
- _pluginContent: null,
- get pluginContent() {
- if (!this._pluginContent) {
- this._pluginContent = new PluginContent(global);
- }
- return this._pluginContent;
- },
-
- init() {
- addEventListener("unload", this);
-
- addEventListener("PluginBindingAttached", this, true, true);
-
- for (let event of this.EVENTS) {
- addEventListener(event, this, true);
- }
- for (let msg of this.MESSAGES) {
- addMessageListener(msg, this);
- }
- Services.obs.addObserver(this, "decoder-doctor-notification");
- this.init = null;
- },
-
- uninit() {
- Services.obs.removeObserver(this, "decoder-doctor-notification");
- },
-
- observe(subject, topic, data) {
- return this.pluginContent.observe(subject, topic, data);
- },
-
- handleEvent(event) {
- if (event.type === "unload") {
- return this.uninit();
- }
- return this.pluginContent.handleEvent(event);
- },
-
- receiveMessage(msg) {
- return this.pluginContent.receiveMessage(msg);
- },
-};
-
-PluginContentStub.init();
-
// This is a temporary hack to prevent regressions (bug 1471327).
void content;
addEventListener("DOMWindowFocus", function(event) {
sendAsyncMessage("DOMWindowFocus", {});
}, false);
// We use this shim so that ContentWebRTC.jsm will not be loaded until