Files
tubestation/toolkit/components/normandy/content/AboutPages.jsm
Kris Maglione fd7e9e6a69 Bug 1456035: Part 4 - Convert callers of XPCOMUtils.generateQI to ChromeUtils.generateQI. r=mccr8
This also removes any redundant Ci.nsISupports elements in the interface
lists.

This was done using the following script:

acecb401b7/processors/chromeutils-generateQI.jsm

MozReview-Commit-ID: AIx10P8GpZY
2018-04-22 20:55:06 -07:00

254 lines
8.1 KiB
JavaScript

/* 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";
const Cm = Components.manager;
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(
this, "CleanupManager", "resource://normandy/lib/CleanupManager.jsm",
);
ChromeUtils.defineModuleGetter(
this, "AddonStudies", "resource://normandy/lib/AddonStudies.jsm",
);
ChromeUtils.defineModuleGetter(
this, "RecipeRunner", "resource://normandy/lib/RecipeRunner.jsm",
);
var EXPORTED_SYMBOLS = ["AboutPages"];
const SHIELD_LEARN_MORE_URL_PREF = "app.normandy.shieldLearnMoreUrl";
// Due to bug 1051238 frame scripts are cached forever, so we can't update them
// as a restartless add-on. The Math.random() is the work around for this.
const PROCESS_SCRIPT = (
`resource://normandy-content/shield-content-process.js?${Math.random()}`
);
const FRAME_SCRIPT = (
`resource://normandy-content/shield-content-frame.js?${Math.random()}`
);
/**
* Class for managing an about: page that Normandy provides. Adapted from
* browser/extensions/pocket/content/AboutPocket.jsm.
*
* @implements nsIFactory
* @implements nsIAboutModule
*/
class AboutPage {
constructor({chromeUrl, aboutHost, classId, description, uriFlags}) {
this.chromeUrl = chromeUrl;
this.aboutHost = aboutHost;
this.classId = Components.ID(classId);
this.description = description;
this.uriFlags = uriFlags;
}
getURIFlags() {
return this.uriFlags;
}
newChannel(uri, loadInfo) {
const newURI = Services.io.newURI(this.chromeUrl);
const channel = Services.io.newChannelFromURIWithLoadInfo(newURI, loadInfo);
channel.originalURI = uri;
if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
const principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
channel.owner = principal;
}
return channel;
}
createInstance(outer, iid) {
if (outer !== null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
}
/**
* Register this about: page with XPCOM. This must be called once in each
* process (parent and content) to correctly initialize the page.
*/
register() {
Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
this.classId,
this.description,
`@mozilla.org/network/protocol/about;1?what=${this.aboutHost}`,
this,
);
}
/**
* Unregister this about: page with XPCOM. This must be called before the
* add-on is cleaned up if the page has been registered.
*/
unregister() {
Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(this.classId, this);
}
}
AboutPage.prototype.QueryInterface = ChromeUtils.generateQI([Ci.nsIAboutModule]);
/**
* The module exported by this file.
*/
var AboutPages = {
async init() {
// Load scripts in content processes and tabs
Services.ppmm.loadProcessScript(PROCESS_SCRIPT, true);
Services.mm.loadFrameScript(FRAME_SCRIPT, true);
// Register about: pages and their listeners
this.aboutStudies.register();
this.aboutStudies.registerParentListeners();
CleanupManager.addCleanupHandler(() => {
// Stop loading processs scripts and notify existing scripts to clean up.
Services.ppmm.removeDelayedProcessScript(PROCESS_SCRIPT);
Services.ppmm.broadcastAsyncMessage("Shield:ShuttingDown");
Services.mm.removeDelayedFrameScript(FRAME_SCRIPT);
Services.mm.broadcastAsyncMessage("Shield:ShuttingDown");
// Clean up about pages
this.aboutStudies.unregisterParentListeners();
this.aboutStudies.unregister();
});
},
};
/**
* about:studies page for displaying in-progress and past Shield studies.
* @type {AboutPage}
* @implements {nsIMessageListener}
*/
XPCOMUtils.defineLazyGetter(this.AboutPages, "aboutStudies", () => {
const aboutStudies = new AboutPage({
chromeUrl: "resource://normandy-content/about-studies/about-studies.html",
aboutHost: "studies",
classId: "{6ab96943-a163-482c-9622-4faedc0e827f}",
description: "Shield Study Listing",
uriFlags: (
Ci.nsIAboutModule.ALLOW_SCRIPT
| Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT
| Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD
),
});
// Extra methods for about:study-specific behavior.
Object.assign(aboutStudies, {
/**
* Register listeners for messages from the content processes.
*/
registerParentListeners() {
Services.mm.addMessageListener("Shield:GetStudyList", this);
Services.mm.addMessageListener("Shield:RemoveStudy", this);
Services.mm.addMessageListener("Shield:OpenDataPreferences", this);
Services.mm.addMessageListener("Shield:GetStudiesEnabled", this);
},
/**
* Unregister listeners for messages from the content process.
*/
unregisterParentListeners() {
Services.mm.removeMessageListener("Shield:GetStudyList", this);
Services.mm.removeMessageListener("Shield:RemoveStudy", this);
Services.mm.removeMessageListener("Shield:OpenDataPreferences", this);
Services.mm.removeMessageListener("Shield:GetStudiesEnabled", this);
},
/**
* Dispatch messages from the content process to the appropriate handler.
* @param {Object} message
* See the nsIMessageListener documentation for details about this object.
*/
receiveMessage(message) {
switch (message.name) {
case "Shield:GetStudyList":
this.sendStudyList(message.target);
break;
case "Shield:RemoveStudy":
this.removeStudy(message.data.recipeId, message.data.reason);
break;
case "Shield:OpenDataPreferences":
this.openDataPreferences();
break;
case "Shield:GetStudiesEnabled":
this.sendStudiesEnabled(message.target);
break;
}
},
/**
* Fetch a list of studies from storage and send it to the process that
* requested them.
* @param {<browser>} target
* XUL <browser> element for the tab containing the about:studies page
* that requested a study list.
*/
async sendStudyList(target) {
try {
target.messageManager.sendAsyncMessage("Shield:ReceiveStudyList", {
studies: await AddonStudies.getAll(),
});
} catch (err) {
// The child process might be gone, so no need to throw here.
Cu.reportError(err);
}
},
/**
* Get if studies are enabled and send it to the process that
* requested them. This has to be in the parent process, since
* RecipeRunner is stateful, and can't be interacted with from
* content processes safely.
*
* @param {<browser>} target
* XUL <browser> element for the tab containing the about:studies page
* that requested a study list.
*/
sendStudiesEnabled(target) {
RecipeRunner.checkPrefs();
try {
target.messageManager.sendAsyncMessage("Shield:ReceiveStudiesEnabled", {
studiesEnabled: RecipeRunner.enabled,
});
} catch (err) {
// The child process might be gone, so no need to throw here.
Cu.reportError(err);
}
},
/**
* Disable an active study and remove its add-on.
* @param {String} studyName
*/
async removeStudy(recipeId, reason) {
await AddonStudies.stop(recipeId, reason);
// Update any open tabs with the new study list now that it has changed.
Services.mm.broadcastAsyncMessage("Shield:ReceiveStudyList", {
studies: await AddonStudies.getAll(),
});
},
openDataPreferences() {
const browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
browserWindow.openPreferences("privacy-reports", {origin: "aboutStudies"});
},
getShieldLearnMoreHref() {
return Services.urlFormatter.formatURLPref(SHIELD_LEARN_MORE_URL_PREF);
},
getStudiesEnabled() {
RecipeRunner.checkPrefs();
return RecipeRunner.enabled;
}
});
return aboutStudies;
});