fune/browser/extensions/screenshots/bootstrap.js
Jared Hirsch 768f7a15c5 Temporarily revert Screenshots back to Photon page action until 63 branches (Bug 1466575). r=ianbicking
Screenshots moved from a Photon page action to a pure WebExtension page
action, but this move introduced some UI bugs where the two page actions
behave differently. Since this change isn't needed for 63, seems better
to just temporarily revert.

Differential Revision: https://phabricator.services.mozilla.com/D4162

--HG--
extra : moz-landing-system : lando
2018-08-23 22:15:44 +00:00

283 lines
9.6 KiB
JavaScript

/* globals ADDON_DISABLE Services CustomizableUI LegacyExtensionsUtils AppConstants PageActions */
const ADDON_ID = "screenshots@mozilla.org";
const TELEMETRY_ENABLED_PREF = "datareporting.healthreport.uploadEnabled";
const PREF_BRANCH = "extensions.screenshots.";
const USER_DISABLE_PREF = "extensions.screenshots.disabled";
const UPLOAD_DISABLED_PREF = "extensions.screenshots.upload-disabled";
const HISTORY_ENABLED_PREF = "places.history.enabled";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
ChromeUtils.defineModuleGetter(this, "LegacyExtensionsUtils",
"resource://gre/modules/LegacyExtensionsUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PageActions",
"resource:///modules/PageActions.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
let addonResourceURI;
let appStartupDone;
let appStartupPromise = new Promise((resolve, reject) => {
appStartupDone = resolve;
});
const prefs = Services.prefs;
const prefObserver = {
register() {
prefs.addObserver(PREF_BRANCH, this, false); // eslint-disable-line mozilla/no-useless-parameters
},
unregister() {
prefs.removeObserver(PREF_BRANCH, this, false); // eslint-disable-line mozilla/no-useless-parameters
},
observe(aSubject, aTopic, aData) {
// aSubject is the nsIPrefBranch we're observing (after appropriate QI)
// aData is the name of the pref that's been changed (relative to aSubject)
if (aData === USER_DISABLE_PREF) {
// eslint-disable-next-line promise/catch-or-return
appStartupPromise = appStartupPromise.then(handleStartup);
}
}
};
const appStartupObserver = {
register() {
Services.obs.addObserver(this, "sessionstore-windows-restored", false); // eslint-disable-line mozilla/no-useless-parameters
},
unregister() {
Services.obs.removeObserver(this, "sessionstore-windows-restored", false); // eslint-disable-line mozilla/no-useless-parameters
},
observe() {
appStartupDone();
this.unregister();
}
};
const LibraryButton = {
ITEM_ID: "appMenu-library-screenshots",
init(webExtension) {
this._initialized = true;
const permissionPages = [...webExtension.extension.permissions].filter(p => (/^https?:\/\//i).test(p));
if (permissionPages.length > 1) {
Cu.reportError(new Error("Should not have more than 1 permission page, but got: " + JSON.stringify(permissionPages)));
}
this.PAGE_TO_OPEN = permissionPages.length === 1 ? permissionPages[0].replace(/\*$/, "") : "https://screenshots.firefox.com/";
this.PAGE_TO_OPEN += "shots";
this.ICON_URL = webExtension.extension.getURL("icons/icon-v2.svg");
this.LABEL = webExtension.extension.localizeMessage("libraryLabel");
CustomizableUI.addListener(this);
for (const win of CustomizableUI.windows) {
this.onWindowOpened(win);
}
},
uninit() {
if (!this._initialized) {
return;
}
for (const win of CustomizableUI.windows) {
const item = win.document.getElementById(this.ITEM_ID);
if (item) {
item.remove();
}
}
CustomizableUI.removeListener(this);
this._initialized = false;
},
onWindowOpened(win) {
const libraryViewInsertionPoint = win.document.getElementById("appMenu-library-remotetabs-button");
// If the library view doesn't exist (on non-photon builds, for instance),
// this will be null, and we bail out early.
if (!libraryViewInsertionPoint) {
return;
}
const parent = libraryViewInsertionPoint.parentNode;
const {nextSibling} = libraryViewInsertionPoint;
const item = win.document.createXULElement("toolbarbutton");
item.className = "subviewbutton subviewbutton-iconic";
item.addEventListener("command", () => win.openWebLinkIn(this.PAGE_TO_OPEN, "tab"));
item.id = this.ITEM_ID;
const iconURL = this.ICON_URL;
item.setAttribute("image", iconURL);
item.setAttribute("label", this.LABEL);
parent.insertBefore(item, nextSibling);
},
};
const APP_STARTUP = 1;
const APP_SHUTDOWN = 2;
let addonData, startupReason;
function startup(data, reason) { // eslint-disable-line no-unused-vars
addonData = data;
startupReason = reason;
if (reason === APP_STARTUP) {
appStartupObserver.register();
} else {
appStartupDone();
}
prefObserver.register();
addonResourceURI = data.resourceURI;
// eslint-disable-next-line promise/catch-or-return
appStartupPromise = appStartupPromise.then(handleStartup);
}
function shutdown(data, reason) { // eslint-disable-line no-unused-vars
prefObserver.unregister();
const webExtension = LegacyExtensionsUtils.getEmbeddedExtensionFor({
id: ADDON_ID,
resourceURI: addonResourceURI
});
// Immediately exit if Firefox is exiting, #3323
if (reason === APP_SHUTDOWN) {
stop(webExtension, reason);
return;
}
// Because the prefObserver is unregistered above, this _should_ terminate the promise chain.
appStartupPromise = appStartupPromise.then(() => { stop(webExtension, reason); });
}
function install(data, reason) {} // eslint-disable-line no-unused-vars
function uninstall(data, reason) {} // eslint-disable-line no-unused-vars
function getBoolPref(pref) {
return prefs.getPrefType(pref) && prefs.getBoolPref(pref);
}
function shouldDisable() {
return getBoolPref(USER_DISABLE_PREF);
}
function handleStartup() {
const webExtension = LegacyExtensionsUtils.getEmbeddedExtensionFor({
id: ADDON_ID,
resourceURI: addonResourceURI
});
if (!shouldDisable() && !webExtension.started) {
start(webExtension);
} else if (shouldDisable()) {
stop(webExtension, ADDON_DISABLE);
}
}
function start(webExtension) {
return webExtension.startup(startupReason, addonData).then((api) => {
api.browser.runtime.onMessage.addListener(handleMessage);
LibraryButton.init(webExtension);
initPhotonPageAction(api, webExtension);
}).catch((err) => {
// The startup() promise will be rejected if the webExtension was
// already started (a harmless error), or if initializing the
// WebExtension failed and threw (an important error).
console.error(err);
if (err.message !== "This embedded extension has already been started") {
// TODO: Should we send these errors to Sentry? #2420
}
});
}
function stop(webExtension, reason) {
if (reason !== APP_SHUTDOWN) {
LibraryButton.uninit();
if (photonPageAction) {
photonPageAction.remove();
photonPageAction = null;
}
}
return Promise.resolve(webExtension.shutdown(reason));
}
function handleMessage(msg, sender, sendReply) {
if (!msg) {
return;
}
if (msg.funcName === "isTelemetryEnabled") {
const telemetryEnabled = getBoolPref(TELEMETRY_ENABLED_PREF);
sendReply({type: "success", value: telemetryEnabled});
} else if (msg.funcName === "isUploadDisabled") {
const isESR = AppConstants.MOZ_UPDATE_CHANNEL === "esr";
const uploadDisabled = getBoolPref(UPLOAD_DISABLED_PREF);
sendReply({type: "success", value: uploadDisabled || isESR});
} else if (msg.funcName === "isHistoryEnabled") {
const historyEnabled = getBoolPref(HISTORY_ENABLED_PREF);
sendReply({type: "success", value: historyEnabled});
} else if (msg.funcName === "incrementCount") {
const allowedScalars = ["download", "upload", "copy"];
const scalar = msg.args && msg.args[0] && msg.args[0].scalar;
if (!allowedScalars.includes(scalar)) {
sendReply({type: "error", name: `incrementCount passed an unrecognized scalar ${scalar}`});
} else {
Services.telemetry.scalarAdd(`screenshots.${scalar}`, 1);
sendReply({type: "success", value: true});
}
}
}
let photonPageAction;
// If the current Firefox version supports Photon (57 and later), this sets up
// a Photon page action and removes the UI for the WebExtension browser action.
// Does nothing otherwise. Ideally, in the future, WebExtension page actions
// and Photon page actions would be one in the same, but they aren't right now.
function initPhotonPageAction(api, webExtension) {
const id = "screenshots";
let port = null;
const {tabManager} = webExtension.extension;
// Make the page action.
photonPageAction = PageActions.actionForID(id) || PageActions.addAction(new PageActions.Action({
id,
title: "Take a Screenshot",
iconURL: webExtension.extension.getURL("icons/icon-v2.svg"),
_insertBeforeActionID: null,
onCommand(event, buttonNode) {
if (port) {
const browserWin = buttonNode.ownerGlobal;
const tab = tabManager.getWrapper(browserWin.gBrowser.selectedTab);
port.postMessage({
type: "click",
tab: {id: tab.id, url: tab.url}
});
}
},
}));
// Establish a port to the WebExtension side.
api.browser.runtime.onConnect.addListener((listenerPort) => {
if (listenerPort.name !== "photonPageActionPort") {
return;
}
port = listenerPort;
port.onMessage.addListener((message) => {
switch (message.type) {
case "setProperties":
if (message.title) {
photonPageAction.setTitle(message.title);
}
if (message.iconPath) {
photonPageAction.setIconURL(webExtension.extension.getURL(message.iconPath));
}
break;
default:
console.error("Unrecognized message:", message);
break;
}
});
});
}