This commit is contained in:
Ximi1970
2023-10-27 20:36:07 +02:00
parent 961ffecf32
commit 571b1beefe
8 changed files with 707 additions and 5 deletions

View File

@@ -1,4 +1,5 @@
var SysTrayX = {
mainWindowId: undefined,
startupState: undefined,
restorePositions: false,
@@ -349,10 +350,31 @@ SysTrayX.Messaging = {
deleteFolderFromFilters(deletedFolder);
},
onCloseButton: function () {
onNewWindow: async function () {
const window = await browser.windows.getCurrent();
console.debug("onNewWindow: " + JSON.stringify( window ) );
},
onCloseButton: async function () {
//console.debug("Minimize all")
SysTrayX.Link.postSysTrayXMessage({ window: "minimized_all" });
const window = await browser.windows.getCurrent();
console.debug("onCloseButton2 Window: " + JSON.stringify( window ) );
if( window.id === SysTrayX.mainWindowId ) {
SysTrayX.Link.postSysTrayXMessage({ window: "minimized_all" });
} else {
// browser.windows.remove( window.id );
browser.windows.update( window.id, {
state: "docked",
// state: "minimized",
});
}
/*
browser.windows.update(browser.windows.WINDOW_ID_CURRENT, {
state: "minimized",
@@ -970,6 +992,9 @@ SysTrayX.Window = {
};
async function start() {
// Setup the link first
SysTrayX.Link.init();
// Set platform
SysTrayX.Info.platformInfo = await browser.runtime
.getPlatformInfo()
@@ -1026,11 +1051,55 @@ async function start() {
browser.windowEvent.setCloseType(Number(SysTrayX.Messaging.closeType));
// Intercept close button?
/*
if (SysTrayX.Messaging.closeType !== "0") {
browser.windowEvent.onCloseButtonClick.addListener(
SysTrayX.Messaging.onCloseButton
);
}
*/
// Get main window id
const window = await browser.windows.getCurrent();
SysTrayX.mainWindowId = window.id;
console.debug( "Main window ID: " + SysTrayX.mainWindowId );
// Get all window ids
const windows = await browser.windows.getAll();
console.debug( "All window IDs: " + JSON.stringify( windows.map( (win) => win.id ) ) );
// Sent it to the companion app
SysTrayX.Link.postSysTrayXMessage( {
windowList: {
main: SysTrayX.mainWindowId,
list: windows.map( (win) => win.id )
}
});
// Get the window id
const id = browser.windowHandler.getWindowId(SysTrayX.mainWindowId);
console.debug("Main window real ID: " + id);
// Get the close type
browser.windowEvent2.setCloseType( Number( SysTrayX.Messaging.closeType ) );
// Intercept close button?
if (SysTrayX.Messaging.closeType !== "0") {
// Intercept new window
browser.windowEvent2.onNewWindow.addListener( SysTrayX.Messaging.onNewWindow );
browser.windowEvent2.onCloseButtonClick.addListener(
SysTrayX.Messaging.onCloseButton
);
}
// Hide the default icon
const hideDefaultIcon = await getHideDefaultIcon();
@@ -1060,9 +1129,6 @@ async function start() {
const getIconPromise = () => new Promise((res) => res(getIcon()));
await getIconPromise();
// Setup the link first
SysTrayX.Link.init();
// Main start
SysTrayX.Messaging.init();
}

159
webext/js/experimental.js Normal file
View File

@@ -0,0 +1,159 @@
/* eslint-disable object-shorthand */
"use strict";
// Using a closure to not leak anything but the API to the outside world.
(function (exports) {
// 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");
// A helpful class for listening to windows opening and closing.
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.
*
* An EventEmitter has the following basic functions:
*
* EventEmitter.on(emitterName, callback)
* Registers a callback for a custom emitter.
*
* EventEmitter.off(emitterName, callback)
* Unregisters a callback for a custom emitter.
*
* EventEmitter.emit(emitterName)
* Emit a custom emitter, all provided parameters will be forwarded to the registered callbacks.
*/
let windowListener;
class WindowListener extends ExtensionCommon.EventEmitter {
constructor(extension) {
super();
this.extension = extension;
this.callbackCount = 0;
}
get listenerId() {
return `experiment_listener_${this.extension.uuid}_${this.extension.instanceId}`;
}
handleEvent(event) {
// Only react to the secondary mouse button.
if (event.button == 0) {
let toolbar = event.target.closest("toolbar");
// Emit "toolbar-clicked" and send toolbar.id, event.clientX, event.clientY to
// the registered callbacks.
windowListener.emit("toolbar-clicked", toolbar.id, event.clientX, event.clientY);
}
}
add(callback) {
// Registering the callback for "toolbar-clicked".
this.on("toolbar-clicked", callback);
this.callbackCount++;
if (this.callbackCount == 1) {
ExtensionSupport.registerWindowListener(this.listenerId, {
chromeURLs: [
"chrome://messenger/content/messenger.xhtml",
"chrome://messenger/content/messenger.xul",
],
onLoadWindow: function (window) {
let toolbox = window.document.getElementById("mail-toolbox");
toolbox.addEventListener("click", windowListener.handleEvent);
},
});
}
}
remove(callback) {
// Un-Registering the callback for "toolbar-clicked".
this.off("toolbar-clicked", callback);
this.callbackCount--;
if (this.callbackCount == 0) {
for (let window of ExtensionSupport.openWindows) {
if ([
"chrome://messenger/content/messenger.xhtml",
"chrome://messenger/content/messenger.xul",
].includes(window.location.href)) {
let toolbox = window.document.getElementById("mail-toolbox");
toolbox.removeEventListener("click", this.handleEvent);
}
}
ExtensionSupport.unregisterWindowListener(this.listenerId);
}
}
};
// This is the important part. It implements the functions and events defined
// in the schema.json. The name must match what you've been using so far,
// "ExampleAPI" in this case.
class ExampleAPI extends ExtensionCommon.ExtensionAPI {
// An alternative to defining a constructor here, is to use the onStartup
// event. However, this causes the API to be instantiated directly after the
// add-on has been loaded, not when the API is first used. Depends on what is
// desired.
constructor(extension) {
super(extension);
windowListener = new WindowListener(extension);
}
getAPI(context) {
return {
// This key must match the class name.
ExampleAPI: {
// A function.
sayHello: async function (name) {
Services.wm.getMostRecentWindow("mail:3pane").alert(name);
},
// An event. Most of this is boilerplate you don't need to worry about, just copy it.
onToolbarClick: new ExtensionCommon.EventManager({
context,
name: "ExampleAPI.onToolbarClick",
// 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, id, x, y) {
return fire.async(id, x, y);
}
windowListener.add(callback);
return function () {
windowListener.remove(callback);
};
},
}).api(),
},
};
}
onShutdown(isAppShutdown) {
// This function is called if the extension is disabled or removed, or Thunderbird closes.
// We usually do not have to do any cleanup, if Thunderbird is shutting down entirely
if (isAppShutdown) {
return;
}
console.log("Goodbye world!");
}
};
// Export the api by assigning in to the exports parameter of the anonymous closure
// function, which is the global this.
exports.ExampleAPI = ExampleAPI;
})(this)

278
webext/js/windowEvent2.js Normal file
View File

@@ -0,0 +1,278 @@
/* eslint-disable object-shorthand */
"use strict";
// Using a closure to not leak anything but the API to the outside world.
(function (exports) {
// 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");
// A helpful class for listening to windows opening and closing.
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.
*
* An EventEmitter has the following basic functions:
*
* EventEmitter.on(emitterName, callback)
* Registers a callback for a custom emitter.
*
* EventEmitter.off(emitterName, callback)
* Unregisters a callback for a custom emitter.
*
* EventEmitter.emit(emitterName)
* Emit a custom emitter, all provided parameters will be forwarded to the registered callbacks.
*/
let windowListener;
class WindowListener extends ExtensionCommon.EventEmitter {
constructor(extension) {
super();
this.extension = extension;
this.onNewWindowCallbackCount = 0;
this.onCloseButtonClickCallbackCount = 0;
this.closeType = this.MESSAGE_CLOSE_TYPE_DEFAULT;
this.oldClose = undefined;
}
get listenerIdNewWindow() {
return `window_event_listener_new_window_${this.extension.uuid}_${this.extension.instanceId}`;
}
get listenerIdCloseButton() {
return `window_event_listener_close_button_${this.extension.uuid}_${this.extension.instanceId}`;
}
setCloseType(closeType) {
if (closeType === 0) {
this.closeType = this.MESSAGE_CLOSE_TYPE_DEFAULT;
} else if (closeType === 1) {
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_MAIN_TRAY_CLOSE_CHILDREN;
} else if (closeType === 2) {
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_ALL_TRAY;
} else if (closeType === 3) {
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_MAIN_CLOSE_CHILDREN;
} else if (closeType === 4) {
this.closeType = this.MESSAGE_CLOSE_TYPE_MIN_ALL;
} else console.log("Unknown close type: " + closeType);
}
addOnNewWindow( callback ) {
// Registering the callback for "new-window".
this.on("new-window", callback);
this.onNewWindowCallbackCount++;
if (this.onNewWindowCallbackCount == 1) {
ExtensionSupport.registerWindowListener( this.listenerIdNewWindow, {
chromeURLs: [
"chrome://messenger/content/messenger.xhtml",
"chrome://messenger/content/messenger.xul",
],
onLoadWindow: function ( window ) {
windowListener.emit( "new-window" );
console.log("New window added");
},
});
}
}
removeOnNewWindow( callback ) {
// Un-Registering the callback for "new-window".
this.off("new-window", callback);
this.onNewWindowCallbackCount--;
if (this.onNewWindowCallbackCount == 0) {
for (let window of ExtensionSupport.openWindows) {
if ( [
"chrome://messenger/content/messenger.xhtml",
"chrome://messenger/content/messenger.xul",
].includes( window.location.href ) ) {
console.log( "New window listener removed" );
}
}
ExtensionSupport.unregisterWindowListener( this.listenerIdNewWindow );
}
}
onCloseButton( event ) {
if ( event ) event.preventDefault();
windowListener.emit( "close-clicked" );
return true;
}
addOnCloseButtonClick( callback, context, window ) {
// Registering the callback for "close-clicked".
this.on( "close-clicked", callback );
this.onCloseButtonClickCallbackCount++;
if (this.onCloseButtonClickCallbackCount == 1) {
ExtensionSupport.registerWindowListener( this.listenerIdCloseButton, {
chromeURLs: [
"chrome://messenger/content/messenger.xhtml",
"chrome://messenger/content/messenger.xul",
],
onLoadWindow: function ( window ) {
window.addEventListener(
"close",
windowListener.onCloseButton,
true
);
windowListener.oldClose = window.close;
window.close = () => windowListener.onCloseButton( null );
// console.debug("Window: " + JSON.stringify(window));
/*
console.debug( "Window id1: " + window.id );
console.debug( "Window id1: " + window.windowId );
let windowId2 = getInnerWindowID(window);
console.debug( "Window id2: " + windowId2 );
// Get a real window from a window ID:
let windowObject = context.extension.windowManager.get(windowId2);
// let windowObject = context.extension.windowManager.get(windowId);
let realWindow = windowObject.window;
console.debug( "WinObj: " + JSON.stringify( windowObject ) );
// Get a window ID from a real window:
const id = context.extension.windowManager.getWrapper(realWindow).id;
console.debug( "WinObj Real: " + id );
// Get all windows: (note this returns a Generator, not an array like the API)
// context.extension.windowManager.getAll();
*/
console.log( "Close listener added" );
},
});
}
}
removeOnCloseButtonClick( callback ) {
// Un-Registering the callback for "close-clicked".
this.off( "close-clicked", callback );
this.onCloseButtonClickCallbackCount--;
if ( this.onCloseButtonClickCallbackCount == 0 ) {
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
);
window.close = windowListener.oldClose;
console.log( "Close listener removed" );
}
}
ExtensionSupport.unregisterWindowListener( this.listenerIdCloseButton );
}
}
};
// This is the important part. It implements the functions and events defined
// in the schema.json. The name must match what you've been using so far,
// "windowEvent2" in this case.
class windowEvent2 extends ExtensionCommon.ExtensionAPI {
// An alternative to defining a constructor here, is to use the onStartup
// event. However, this causes the API to be instantiated directly after the
// add-on has been loaded, not when the API is first used. Depends on what is
// desired.
constructor(extension) {
super(extension);
windowListener = new WindowListener(extension);
}
getAPI(context) {
console.log("windowEvent API started");
return {
// This key must match the class name.
windowEvent2: {
setCloseType: async function (type) {
windowListener.setCloseType(type);
},
// An event. Most of this is boilerplate you don't need to worry about, just copy it.
onNewWindow: new ExtensionCommon.EventManager({
context,
name: "windowEvent2.onNewWindow",
// 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.addOnNewWindow(callback);
return function () {
windowListener.removeOnNewWindow(callback);
};
},
}).api(),
// An event. Most of this is boilerplate you don't need to worry about, just copy it.
onCloseButtonClick: new ExtensionCommon.EventManager({
context,
name: "windowEvent2.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.addOnCloseButtonClick(callback,context);
return function () {
windowListener.removeOnCloseButtonClick(callback);
};
},
}).api(),
},
};
}
onShutdown(isAppShutdown) {
// This function is called if the extension is disabled or removed, or Thunderbird closes.
// We usually do not have to do any cleanup, if Thunderbird is shutting down entirely
if (isAppShutdown) {
return;
}
console.log("windowEvent API closed");
}
};
// Export the api by assigning in to the exports parameter of the anonymous closure
// function, which is the global this.
exports.windowEvent2 = windowEvent2;
})(this)

View File

@@ -0,0 +1,88 @@
/* eslint-disable object-shorthand */
"use strict";
// Using a closure to not leak anything but the API to the outside world.
(function (exports) {
// 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");
// A helpful class for listening to windows opening and closing.
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.
*
* An EventEmitter has the following basic functions:
*
* EventEmitter.on(emitterName, callback)
* Registers a callback for a custom emitter.
*
* EventEmitter.off(emitterName, callback)
* Unregisters a callback for a custom emitter.
*
* EventEmitter.emit(emitterName)
* Emit a custom emitter, all provided parameters will be forwarded to the registered callbacks.
*/
let windowListener;
class WindowListener extends ExtensionCommon.EventEmitter {
constructor(extension) {
super();
this.extension = extension;
this.callbackCount = 0;
}
}
// This is the important part. It implements the functions and events defined
// in the schema.json. The name must match what you've been using so far,
// "windowEvent2" in this case.
class windowHandler extends ExtensionCommon.ExtensionAPI {
// An alternative to defining a constructor here, is to use the onStartup
// event. However, this causes the API to be instantiated directly after the
// add-on has been loaded, not when the API is first used. Depends on what is
// desired.
constructor(extension) {
super(extension);
windowListener = new WindowListener(extension);
}
getAPI(context) {
console.log("windowHandler API started");
return {
// This key must match the class name.
windowHandler: {
getWindowId: async function (windowId) {
// Get a real window from a window ID:
let windowObject = context.extension.windowManager.get(windowId);
let realWindow = windowObject.window;
// Get a window ID from a real window:
let id = context.extension.windowManager.getWrapper(realWindow).id;
return id;
}
}
};
}
close() {
console.log("windowHandler API closed");
}
};
// Export the api by assigning in to the exports parameter of the anonymous closure
// function, which is the global this.
exports.windowHandler = windowHandler;
})(this)

View File

@@ -39,6 +39,22 @@
"paths": [["windowEvent"]],
"script": "js/windowEvent.js"
}
},
"windowEvent2": {
"schema": "schema_windowEvent2.json",
"parent": {
"scopes": ["addon_parent"],
"paths": [["windowEvent2"]],
"script": "js/windowEvent2.js"
}
},
"windowHandler": {
"schema": "schema_windowHandler.json",
"parent": {
"scopes": ["addon_parent"],
"paths": [["windowHandler"]],
"script": "js/windowHandler.js"
}
}
}
}

View File

@@ -0,0 +1,46 @@
[
{
"namespace": "ExampleAPI",
"functions": [
{
"name": "sayHello",
"type": "function",
"description": "Says hello to the user.",
"async": true,
"parameters": [
{
"name": "name",
"type": "string",
"description": "Who to say hello to."
}
]
}
],
"events": [
{
"name": "onToolbarClick",
"type": "function",
"description": "Fires when the user clicks the secondary mouse button anywhere on the toolbar in the main window.",
"parameters": [
{
"name": "toolbarId",
"type": "string",
"description": "The ID of the toolbar the user clicked."
},
{
"type": "integer",
"name": "x",
"minimum": 0,
"description": "The X position of the mouse when the user clicked."
},
{
"type": "integer",
"name": "y",
"minimum": 0,
"description": "The Y position of the mouse when the user clicked."
}
]
}
]
}
]

View File

@@ -0,0 +1,35 @@
[
{
"namespace": "windowEvent2",
"functions": [
{
"name": "setCloseType",
"type": "function",
"description": "Set the close type.",
"async": true,
"parameters": [
{
"type": "integer",
"name": "type",
"minimum": 0,
"maximum": 4
}
]
}
],
"events": [
{
"name": "onNewWindow",
"type": "function",
"description": "Fires when the user opens a new window.",
"parameters": []
},
{
"name": "onCloseButtonClick",
"type": "function",
"description": "Fires when the user clicks on the close button of a window.",
"parameters": []
}
]
}
]

View File

@@ -0,0 +1,14 @@
[
{
"namespace": "windowHandler",
"functions": [
{
"name": "getWindowId",
"type": "function",
"description": "Get the window Id",
"async": true,
"parameters": []
}
]
}
]