2020-05-10 20:55:27 +02:00
|
|
|
/* eslint-disable object-shorthand */
|
|
|
|
|
|
|
|
|
|
// Get various parts of the WebExtension framework that we need.
|
|
|
|
|
var { ExtensionCommon } = ChromeUtils.import(
|
|
|
|
|
"resource://gre/modules/ExtensionCommon.jsm"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// You probably already know what this does.
|
|
|
|
|
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
|
|
|
|
|
|
// ChromeUtils.import() works in experiments for core resource urls as it did
|
|
|
|
|
// in legacy add-ons. However, chrome:// urls that point to add-on resources no
|
|
|
|
|
// longer work, as the "chrome.manifest" file is no longer supported, which
|
|
|
|
|
// defined the root path for each add-on. Instead, ChromeUtils.import() needs
|
|
|
|
|
// a url generated by
|
|
|
|
|
//
|
|
|
|
|
// let url = context.extension.rootURI.resolve("path/to/file.jsm")
|
|
|
|
|
//
|
|
|
|
|
// Instead of taking the extension object from the context, you may generate
|
|
|
|
|
// the extension object from a given add-on ID as shown in the example below.
|
|
|
|
|
// This allows to import a JSM without context, for example inside another JSM.
|
|
|
|
|
//
|
|
|
|
|
var { ExtensionParent } = ChromeUtils.import(
|
|
|
|
|
"resource://gre/modules/ExtensionParent.jsm"
|
|
|
|
|
);
|
|
|
|
|
var extension = ExtensionParent.GlobalManager.getExtension(
|
|
|
|
|
"systray-x@Ximi1970"
|
|
|
|
|
);
|
|
|
|
|
var { windowEvent } = ChromeUtils.import(
|
|
|
|
|
extension.rootURI.resolve("modules/windowEvent.jsm")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// This is the important part. It implements the functions and events defined in schema.json.
|
|
|
|
|
// The variable must have the same name you've been using so far, "myapi" in this case.
|
|
|
|
|
var windowEvent = class extends ExtensionCommon.ExtensionAPI {
|
|
|
|
|
getAPI(context) {
|
|
|
|
|
console.log("windowEvent API started");
|
|
|
|
|
|
|
|
|
|
// To be notified of the extension going away, call callOnClose with any object that has a
|
|
|
|
|
// close function, such as this one.
|
|
|
|
|
context.callOnClose(this);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
// Again, this key must have the same name.
|
|
|
|
|
windowEvent: {
|
2021-08-08 12:05:26 +02:00
|
|
|
setCloseType: function (type) {
|
2020-08-02 16:59:53 +02:00
|
|
|
windowListener.setCloseType(type);
|
|
|
|
|
},
|
|
|
|
|
|
2020-05-10 20:55:27 +02:00
|
|
|
// An event. Most of this is boilerplate you don't need to worry about, just copy it.
|
|
|
|
|
onCloseButtonClick: new ExtensionCommon.EventManager({
|
|
|
|
|
context,
|
|
|
|
|
name: "windowEvent.onCloseButtonClick",
|
|
|
|
|
// In this function we add listeners for any events we want to listen to, and return a
|
|
|
|
|
// function that removes those listeners. To have the event fire in your extension,
|
|
|
|
|
// call fire.async.
|
|
|
|
|
register(fire) {
|
|
|
|
|
function callback(event) {
|
|
|
|
|
return fire.async();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
windowListener.addOnCloseButton(callback);
|
|
|
|
|
return function () {
|
|
|
|
|
windowListener.removeOnCloseButton(callback);
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
}).api(),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close() {
|
|
|
|
|
// This function is called if the extension is disabled or removed, or Thunderbird closes.
|
|
|
|
|
// We registered it with callOnClose, above.
|
|
|
|
|
console.log("windowEvent API closed");
|
|
|
|
|
|
|
|
|
|
// Unload the JSM we imported above. This will cause Thunderbird to forget about the JSM, and
|
|
|
|
|
// load it afresh next time `import` is called. (If you don't call `unload`, Thunderbird will
|
|
|
|
|
// remember this version of the module and continue to use it, even if your extension receives
|
|
|
|
|
// an update.) You should *always* unload JSMs provided by your extension.
|
|
|
|
|
Cu.unload(extension.getURL("modules/windowEvent.jsm"));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// A helpful class for listening to windows opening and closing.
|
|
|
|
|
// (This file had a lowercase E in Thunderbird 65 and earlier.)
|
|
|
|
|
var { ExtensionSupport } = ChromeUtils.import(
|
|
|
|
|
"resource:///modules/ExtensionSupport.jsm"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// This object is just what we're using to listen for toolbar clicks. The implementation isn't
|
|
|
|
|
// what this example is about, but you might be interested as it's a common pattern. We count the
|
|
|
|
|
// number of callbacks waiting for events so that we're only listening if we need to be.
|
|
|
|
|
var windowListener = new (class extends ExtensionCommon.EventEmitter {
|
|
|
|
|
constructor() {
|
|
|
|
|
super();
|
|
|
|
|
this.callbackOnCloseButtonCount = 0;
|
2020-08-03 23:40:03 +02:00
|
|
|
this.callbackOnLoadWindowCount = 0;
|
2020-08-02 16:59:53 +02:00
|
|
|
|
|
|
|
|
this.MESSAGE_CLOSE_TYPE_DEFAULT = 0;
|
|
|
|
|
this.MESSAGE_CLOSE_TYPE_MIN_MAIN_CLOSE_CHILDREN = 1;
|
|
|
|
|
this.MESSAGE_CLOSE_TYPE_MIN_ALL = 2;
|
|
|
|
|
|
|
|
|
|
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_MAIN_CLOSE_CHILDREN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCloseType(closeType) {
|
|
|
|
|
if (closeType === 0) {
|
|
|
|
|
this.closeType = this.MESSAGE_CLOSE_TYPE_DEFAULT;
|
|
|
|
|
} else if (closeType === 1) {
|
|
|
|
|
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_MAIN_CLOSE_CHILDREN;
|
|
|
|
|
} else if (closeType === 2) {
|
|
|
|
|
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_ALL;
|
|
|
|
|
} else console.log("Unknown close type: " + closeType);
|
2020-05-10 20:55:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addOnCloseButton(callback) {
|
|
|
|
|
if (this.callbackOnCloseButtonCount == 0) {
|
|
|
|
|
this.on("close-clicked", callback);
|
|
|
|
|
this.callbackOnCloseButtonCount++;
|
|
|
|
|
|
|
|
|
|
ExtensionSupport.registerWindowListener("closeButtonListener", {
|
|
|
|
|
chromeURLs: [
|
|
|
|
|
"chrome://messenger/content/messenger.xhtml",
|
|
|
|
|
"chrome://messenger/content/messenger.xul",
|
|
|
|
|
],
|
|
|
|
|
onLoadWindow: function (window) {
|
2020-08-03 23:40:03 +02:00
|
|
|
windowListener.callbackOnLoadWindowCount++;
|
2020-08-02 16:59:53 +02:00
|
|
|
if (
|
2020-08-03 23:40:03 +02:00
|
|
|
windowListener.callbackOnLoadWindowCount === 1 ||
|
2020-08-02 21:32:09 +02:00
|
|
|
windowListener.closeType ===
|
|
|
|
|
windowListener.MESSAGE_CLOSE_TYPE_MIN_ALL
|
2020-08-02 16:59:53 +02:00
|
|
|
) {
|
|
|
|
|
window.addEventListener(
|
|
|
|
|
"close",
|
|
|
|
|
windowListener.onCloseButton,
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
windowListener.hijackTitlebarCloseButton(window);
|
|
|
|
|
}
|
2020-05-10 20:55:27 +02:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeOnCloseButton(callback) {
|
|
|
|
|
if (this.callbackOnCloseButtonCount == 1) {
|
|
|
|
|
this.off("close-clicked", callback);
|
|
|
|
|
this.callbackOnCloseButtonCount--;
|
|
|
|
|
|
|
|
|
|
for (let window of ExtensionSupport.openWindows) {
|
|
|
|
|
if (
|
|
|
|
|
[
|
|
|
|
|
"chrome://messenger/content/messenger.xhtml",
|
|
|
|
|
"chrome://messenger/content/messenger.xul",
|
|
|
|
|
].includes(window.location.href)
|
|
|
|
|
) {
|
|
|
|
|
window.removeEventListener(
|
|
|
|
|
"close",
|
|
|
|
|
windowListener.onCloseButton,
|
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ExtensionSupport.unregisterWindowListener("closeButtonListener");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onCloseButton(event) {
|
|
|
|
|
windowListener.emit("close-clicked");
|
|
|
|
|
if (event) event.preventDefault();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hijackTitlebarCloseButton(window) {
|
|
|
|
|
if (
|
|
|
|
|
windowListener.replaceCommand(window, "titlebar-close", function () {
|
|
|
|
|
return windowListener.onCloseButton(null);
|
|
|
|
|
})
|
|
|
|
|
) {
|
|
|
|
|
console.log("replaced command= " + "titlebar-close");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
replaceCommand(window, eltId, gotHidden) {
|
|
|
|
|
let elt = window.document.getElementById(eltId);
|
|
|
|
|
if (!elt) {
|
|
|
|
|
console.log("Element '" + eltId + "' not found. Command not replaced.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let prevent = null;
|
|
|
|
|
if (elt.command) {
|
|
|
|
|
prevent = {
|
|
|
|
|
event: "click",
|
|
|
|
|
func: function (e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
} else if (elt.getAttribute("oncommand")) {
|
|
|
|
|
prevent = {
|
|
|
|
|
event: "command",
|
|
|
|
|
func: function (e) {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
console.warn("Could not replace oncommand on " + eltId);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let callback = function (event) {
|
|
|
|
|
if (event.target.id === eltId) {
|
|
|
|
|
console.debug(prevent.event + " on " + eltId);
|
|
|
|
|
if (gotHidden()) prevent.func(event);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* We put listeners on the "titlebar" parent node, because:
|
|
|
|
|
- we can hardly short-circuit command/oncommand (probably because they are
|
|
|
|
|
registered first)
|
|
|
|
|
- we'd have otherwise to alter "oncommand"/"command" attribute and use
|
|
|
|
|
Function(), which do not pass review nowadays. */
|
|
|
|
|
elt.parentNode.addEventListener(prevent.event, callback, true);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
})();
|