Hello,
I completed the items
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
and attach debdiff against the package in stable
Am 11.08.23 um 18:53 schrieb Jonathan Wiltshire:
Control: tag -1 moreinfo On Thu, Aug 10, 2023 at 04:13:22PM +0200, Mechtilde Stehmann wrote:[ Checklist ] [] *all* changes are documented in the d/changelog [X] I reviewed all changes and I approve them [] attach debdiff against the package in (old)stable [] the issue is verified as fixed in unstablePlease complete these items first. Thanks,
After this is ok, I will do some other packages for the ne version of Thunderbird.
Kind regards -- Mechtilde Stehmann ## Debian Developer ## PGP encryption welcome ## F0E3 7F3D C87A 4998 2899 39E7 F287 7BBA 141A AD7F
diffstat for quicktext-5.2 quicktext-5.6
api/WindowListener/CHANGELOG.md | 27
api/WindowListener/implementation.js | 1181 +++++++++++++++++++++++++++---
api/WindowListener/schema.json | 6
chrome/content/modules/wzQuicktext.jsm | 38
chrome/content/modules/wzQuicktextVar.jsm | 8
chrome/content/quicktext.js | 10
chrome/content/settings.js | 11
chrome/locale/ja/quicktext.dtd | 40 -
debian/changelog | 16
debian/control | 4
manifest.json | 4
11 files changed, 1192 insertions(+), 153 deletions(-)
diff -Nru quicktext-5.2/api/WindowListener/CHANGELOG.md quicktext-5.6/api/WindowListener/CHANGELOG.md
--- quicktext-5.2/api/WindowListener/CHANGELOG.md 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/api/WindowListener/CHANGELOG.md 2023-06-25 12:48:31.000000000 +0200
@@ -1,3 +1,30 @@
+Version: 1.60
+-------------
+- explicitly set hasAddonManagerEventListeners flag to false on uninstall
+
+Version: 1.59
+-------------
+- store hasAddonManagerEventListeners flag in add-on scope instead on the global
+ window again, and clear it upon add-on removal
+
+Version: 1.58
+-------------
+- hard fork WindowListener v1.57 implementation and continue to serve it for
+ Thunderbird 111 and older
+- WindowListener v1.58 supports injection into nested browsers of the new
+ mailTab front end of Thunderbird Supernova and allows "about:message" and
+ "about:3pane" to be valid injection targets. More information can be found here:
+ https://developer.thunderbird.net/thunderbird-development/codebase-overview/mail-front-end
+
+Version: 1.57
+-------------
+- fix race condition which could prevent the AOM tab to be monkey patched correctly
+
+Version: 1.56
+-------------
+- be precise on which revision the wrench symbol should be displayed, instead of
+ the options button
+
Version: 1.54
-------------
- fix "ownerDoc.getElementById() is undefined" bug
diff -Nru quicktext-5.2/api/WindowListener/implementation.js quicktext-5.6/api/WindowListener/implementation.js
--- quicktext-5.2/api/WindowListener/implementation.js 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/api/WindowListener/implementation.js 2023-06-25 12:48:31.000000000 +0200
@@ -2,7 +2,7 @@
* This file is provided by the addon-developer-support repository at
* https://github.com/thundernest/addon-developer-support
*
- * Version: 1.54
+ * Version: 1.60
*
* Author: John Bieling (john@thunderbird.net)
*
@@ -20,17 +20,17 @@
);
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var WindowListener = class extends ExtensionCommon.ExtensionAPI {
- log(msg) {
- if (this.debug) console.log("WindowListener API: " + msg);
+function getThunderbirdVersion() {
+ let parts = Services.appinfo.version.split(".");
+ return {
+ major: parseInt(parts[0]),
+ minor: parseInt(parts[1]),
}
+}
- getThunderbirdVersion() {
- let parts = Services.appinfo.version.split(".");
- return {
- major: parseInt(parts[0]),
- minor: parseInt(parts[1]),
- }
+var WindowListener_102 = class extends ExtensionCommon.ExtensionAPI {
+ log(msg) {
+ if (this.debug) console.log("WindowListener API: " + msg);
}
getCards(e) {
@@ -39,10 +39,10 @@
let doc;
// 78,86, and 87+ need special handholding. *Yeah*.
- if (this.getThunderbirdVersion().major < 86) {
+ if (getThunderbirdVersion().major < 86) {
let ownerDoc = e.document || e.target.ownerDocument;
doc = ownerDoc.getElementById("html-view-browser").contentDocument;
- } else if (this.getThunderbirdVersion().major < 87) {
+ } else if (getThunderbirdVersion().major < 87) {
let ownerDoc = e.document || e.target;
doc = ownerDoc.getElementById("html-view-browser").contentDocument;
} else {
@@ -66,11 +66,11 @@
let name = this.extension.manifest.name;
let entry = icon
? event.target.ownerGlobal.MozXULElement.parseXULToFragment(
- `<menuitem class="menuitem-iconic" id="${id}" image="${icon}" label="${name}" />`
- )
+ `<menuitem class="menuitem-iconic" id="${id}" image="${icon}" label="${name}" />`
+ )
: event.target.ownerGlobal.MozXULElement.parseXULToFragment(
- `<menuitem id="${id}" label="${name}" />`
- );
+ `<menuitem id="${id}" label="${name}" />`
+ );
event.target.appendChild(entry);
let noPrefsElem = event.target.querySelector('[disabled="true"]');
@@ -132,9 +132,10 @@
// Setup either the options entry in the menu or the button
//window.document.getElementById(id).addEventListener("command", function() {window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", WL)});
if (card.addon.id == this.extension.id) {
- let optionsMenu =
- (this.getThunderbirdVersion().major > 78 && this.getThunderbirdVersion().major < 88) ||
- (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor < 10);
+ let optionsMenu =
+ (getThunderbirdVersion().major > 78 && getThunderbirdVersion().major < 88) ||
+ (getThunderbirdVersion().major == 78 && getThunderbirdVersion().minor < 10) ||
+ (getThunderbirdVersion().major == 78 && getThunderbirdVersion().minor == 10 && getThunderbirdVersion().revision < 2);
if (optionsMenu) {
// Options menu in 78.0-78.10 and 79-87
let addonOptionsLegacyEntry = card.querySelector(
@@ -198,7 +199,7 @@
// returns the outer browser, not the nested browser of the add-on manager
// events must be attached to the outer browser
getAddonManagerFromTab(tab) {
- if (tab.browser) {
+ if (tab.browser && tab.mode.name == "contentTab") {
let win = tab.browser.contentWindow;
if (win && win.location.href == "about:addons") {
return win;
@@ -209,9 +210,28 @@
getAddonManagerFromWindow(window) {
let tabMail = this.getTabMail(window);
for (let tab of tabMail.tabInfo) {
- let win = this.getAddonManagerFromTab(tab);
- if (win) {
- return win;
+ let managerWindow = this.getAddonManagerFromTab(tab);
+ if (managerWindow) {
+ return managerWindow;
+ }
+ }
+ }
+
+ async getAddonManagerFromWindowWaitForLoad(window) {
+ let { setTimeout } = Services.wm.getMostRecentWindow("mail:3pane");
+
+ let tabMail = this.getTabMail(window);
+ for (let tab of tabMail.tabInfo) {
+ if (tab.browser && tab.mode.name == "contentTab") {
+ // Instead of registering a load observer, wait until its loaded. Not nice,
+ // but gets aroud a lot of edge cases.
+ while(!tab.pageLoaded) {
+ await new Promise(r => setTimeout(r, 150));
+ }
+ let managerWindow = this.getAddonManagerFromTab(tab);
+ if (managerWindow) {
+ return managerWindow;
+ }
}
}
}
@@ -227,7 +247,7 @@
)) {
managerWindow.document.addEventListener("ViewChanged", this);
managerWindow.document.addEventListener("update", this);
- managerWindow.document.addEventListener("view-loaded", this);
+ managerWindow.document.addEventListener("view-loaded", this);
managerWindow[this.uniqueRandomID] = {};
managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true;
}
@@ -327,41 +347,20 @@
// TabMonitor to detect opening of tabs, to setup the options button in the add-on manager.
this.tabMonitor = {
- onTabTitleChanged(aTab) {},
- onTabClosing(aTab) {},
- onTabPersist(aTab) {},
- onTabRestored(aTab) {},
- onTabSwitched(aNewTab, aOldTab) {
- //self.setupAddonManager(self.getAddonManagerFromTab(aNewTab));
- },
- async onTabOpened(aTab) {
- if (aTab.browser) {
- if (!aTab.pageLoaded) {
- // await a location change if browser is not loaded yet
- await new Promise((resolve) => {
- let reporterListener = {
- QueryInterface: ChromeUtils.generateQI([
- "nsIWebProgressListener",
- "nsISupportsWeakReference",
- ]),
- onStateChange() {},
- onProgressChange() {},
- onLocationChange(
- /* in nsIWebProgress*/ aWebProgress,
- /* in nsIRequest*/ aRequest,
- /* in nsIURI*/ aLocation
- ) {
- aTab.browser.removeProgressListener(reporterListener);
- resolve();
- },
- onStatusChange() {},
- onSecurityChange() {},
- onContentBlockingEvent() {},
- };
- aTab.browser.addProgressListener(reporterListener);
- });
+ onTabTitleChanged(tab) { },
+ onTabClosing(tab) { },
+ onTabPersist(tab) { },
+ onTabRestored(tab) { },
+ onTabSwitched(aNewTab, aOldTab) { },
+ async onTabOpened(tab) {
+ if (tab.browser && tab.mode.name == "contentTab") {
+ let { setTimeout } = Services.wm.getMostRecentWindow("mail:3pane");
+ // Instead of registering a load observer, wait until its loaded. Not nice,
+ // but gets aroud a lot of edge cases.
+ while(!tab.pageLoaded) {
+ await new Promise(r => setTimeout(r, 150));
}
- self.setupAddonManager(self.getAddonManagerFromTab(aTab));
+ self.setupAddonManager(self.getAddonManagerFromTab(tab));
}
},
};
@@ -380,8 +379,8 @@
aDocumentExistsAt(uriString) {
self.log(
"Checking if document at <" +
- uriString +
- "> used in registration actually exists."
+ uriString +
+ "> used in registration actually exists."
);
try {
let uriObject = Services.io.newURI(uriString);
@@ -421,10 +420,10 @@
default:
throw new Error(
"Preference <" +
- aName +
- "> has an unsupported type <" +
- typeof aDefault +
- ">. Allowed are string, number and boolean."
+ aName +
+ "> has an unsupported type <" +
+ typeof aDefault +
+ ">. Allowed are string, number and boolean."
);
}
};
@@ -473,7 +472,7 @@
if (self.debug && !this.aDocumentExistsAt(windowHref)) {
self.error(
"Attempt to register an injector script for non-existent window: " +
- windowHref
+ windowHref
);
return;
}
@@ -536,8 +535,8 @@
// delay startup until startup has been finished
self.log(
"Waiting for async startup() in <" +
- self.pathToStartupScript +
- "> to finish."
+ self.pathToStartupScript +
+ "> to finish."
);
if (startupJS.startup) {
await startupJS.startup();
@@ -578,12 +577,12 @@
// Special action #1: If this is the main messenger window
if (
window.location.href ==
- "chrome://messenger/content/messenger.xul" ||
+ "chrome://messenger/content/messenger.xul" ||
window.location.href ==
- "chrome://messenger/content/messenger.xhtml"
+ "chrome://messenger/content/messenger.xhtml"
) {
if (self.pathToOptionsPage) {
- if (self.getThunderbirdVersion().major < 78) {
+ if (getThunderbirdVersion().major < 78) {
let element_addonPrefs = window.document.getElementById(
self.menu_addonPrefs_id
);
@@ -592,16 +591,14 @@
self
);
} else {
- // Setup the options button/menu in the add-on manager, if it is already open.
- self.setupAddonManager(
- self.getAddonManagerFromWindow(window),
- true
- );
// Add a tabmonitor, to be able to setup the options button/menu in the add-on manager.
self
.getTabMail(window)
.registerTabMonitor(self.tabMonitor);
window[self.uniqueRandomID].hasTabMonitor = true;
+ // Setup the options button/menu in the add-on manager, if it is already open.
+ let managerWindow = await self.getAddonManagerFromWindowWaitForLoad(window);
+ self.setupAddonManager(managerWindow, true);
}
}
}
@@ -636,7 +633,7 @@
if (
targetWindow &&
targetWindow.location.href ==
- mutation.target.getAttribute("src") &&
+ mutation.target.getAttribute("src") &&
targetWindow.document.readyState == "complete"
) {
loaded = true;
@@ -808,10 +805,10 @@
if (debug)
console.log(
elements[i].tagName +
- "#" +
- elements[i].id +
- ": insertafter " +
- insertAfterElement.id
+ "#" +
+ elements[i].id +
+ ": insertafter " +
+ insertAfterElement.id
);
if (
debug &&
@@ -820,8 +817,8 @@
) {
console.error(
"The id <" +
- elements[i].id +
- "> of the injected element already exists in the document!"
+ elements[i].id +
+ "> of the injected element already exists in the document!"
);
}
elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
@@ -840,10 +837,10 @@
if (debug)
console.log(
elements[i].tagName +
- "#" +
- elements[i].id +
- ": insertbefore " +
- insertBeforeElement.id
+ "#" +
+ elements[i].id +
+ ": insertbefore " +
+ insertBeforeElement.id
);
if (
debug &&
@@ -852,8 +849,8 @@
) {
console.error(
"The id <" +
- elements[i].id +
- "> of the injected element already exists in the document!"
+ elements[i].id +
+ "> of the injected element already exists in the document!"
);
}
elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
@@ -869,10 +866,10 @@
if (debug)
console.log(
elements[i].tagName +
- "#" +
- elements[i].id +
- " is an existing container, injecting into " +
- elements[i].id
+ "#" +
+ elements[i].id +
+ " is an existing container, injecting into " +
+ elements[i].id
);
injectChildren(
Array.from(elements[i].children),
@@ -916,10 +913,10 @@
if (debug)
console.log(
elements[i].tagName +
- "#" +
- elements[i].id +
- ": append to " +
- container.id
+ "#" +
+ elements[i].id +
+ ": append to " +
+ container.id
);
elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
container.appendChild(elements[i]);
@@ -1044,7 +1041,7 @@
if (isAppShutdown) {
return; // the application gets unloaded anyway
}
-
+
// Unload from all still open windows
let urls = Object.keys(this.registeredWindows);
if (urls.length > 0) {
@@ -1054,9 +1051,9 @@
this.pathToOptionsPage &&
(window.location.href == "chrome://messenger/content/messenger.xul" ||
window.location.href ==
- "chrome://messenger/content/messenger.xhtml")
+ "chrome://messenger/content/messenger.xhtml")
) {
- if (this.getThunderbirdVersion().major < 78) {
+ if (getThunderbirdVersion().major < 78) {
let element_addonPrefs = window.document.getElementById(
this.menu_addonPrefs_id
);
@@ -1084,9 +1081,10 @@
managerWindow.document.removeEventListener("ViewChanged", this);
managerWindow.document.removeEventListener("view-loaded", this);
managerWindow.document.removeEventListener("update", this);
+ managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = false;
let cards = this.getCards(managerWindow);
- if (this.getThunderbirdVersion().major < 88) {
+ if (getThunderbirdVersion().major < 88) {
// Remove options menu in 78-87
for (let card of cards) {
let addonOptionsLegacyEntry = card.querySelector(
@@ -1182,3 +1180,986 @@
}
}
};
+
+var WindowListener_115 = class extends ExtensionCommon.ExtensionAPI {
+ log(msg) {
+ if (this.debug) console.log("WindowListener API: " + msg);
+ }
+
+ getCards(e) {
+ // This gets triggered by real events but also manually by providing the outer window.
+ // The event is attached to the outer browser, get the inner one.
+ let doc = e.document || e.target;
+ return doc.querySelectorAll("addon-card");
+ }
+
+
+ // Event handler for the addon manager, to update the state of the options button.
+ handleEvent(e) {
+ switch (e.type) {
+ case "click": {
+ e.preventDefault();
+ e.stopPropagation();
+ let WL = {};
+ WL.extension = this.extension;
+ WL.messenger = this.getMessenger(this.context);
+ let w = Services.wm.getMostRecentWindow("mail:3pane");
+ w.openDialog(
+ this.pathToOptionsPage,
+ "AddonOptions",
+ "chrome,resizable,centerscreen",
+ WL
+ );
+ }
+ break;
+
+ // update, ViewChanged and manual call for add-on manager options overlay
+ default: {
+ let cards = this.getCards(e);
+ for (let card of cards) {
+ // Setup either the options entry in the menu or the button
+ if (card.addon.id == this.extension.id) {
+ // Add-on button
+ let addonOptionsButton = card.querySelector(
+ ".windowlistener-options-button"
+ );
+ if (card.addon.isActive && !addonOptionsButton) {
+ let origAddonOptionsButton = card.querySelector(".extension-options-button")
+ origAddonOptionsButton.setAttribute("hidden", "true");
+
+ addonOptionsButton = card.ownerDocument.createElement("button");
+ addonOptionsButton.classList.add("windowlistener-options-button");
+ addonOptionsButton.classList.add("extension-options-button");
+ card.optionsButton.parentNode.insertBefore(
+ addonOptionsButton,
+ card.optionsButton
+ );
+ card
+ .querySelector(".windowlistener-options-button")
+ .addEventListener("click", this);
+ } else if (!card.addon.isActive && addonOptionsButton) {
+ addonOptionsButton.remove();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Some tab/add-on-manager related functions
+ getTabMail(window) {
+ return window.document.getElementById("tabmail");
+ }
+
+ // returns the outer browser, not the nested browser of the add-on manager
+ // events must be attached to the outer browser
+ getAddonManagerFromTab(tab) {
+ if (tab.browser) {
+ let win = tab.browser.contentWindow;
+ if (win && win.location.href == "about:addons") {
+ return win;
+ }
+ }
+ }
+
+ getAddonManagerFromWindow(window) {
+ let tabMail = this.getTabMail(window);
+ for (let tab of tabMail.tabInfo) {
+ let win = this.getAddonManagerFromTab(tab);
+ if (win) {
+ return win;
+ }
+ }
+ }
+
+ setupAddonManager(managerWindow, forceLoad = false) {
+ if (!managerWindow) {
+ return;
+ }
+ if (!this.pathToOptionsPage) {
+ return;
+ }
+ if (
+ managerWindow &&
+ managerWindow[this.uniqueRandomID] &&
+ managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
+ ) {
+ return;
+ }
+
+ managerWindow.document.addEventListener("ViewChanged", this);
+ managerWindow.document.addEventListener("update", this);
+ managerWindow.document.addEventListener("view-loaded", this);
+ managerWindow[this.uniqueRandomID] = {};
+ managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true;
+ if (forceLoad) {
+ this.handleEvent(managerWindow);
+ }
+ }
+
+ getMessenger(context) {
+ let apis = ["storage", "runtime", "extension", "i18n"];
+
+ function getStorage() {
+ let localstorage = null;
+ try {
+ localstorage = context.apiCan.findAPIPath("storage");
+ localstorage.local.get = (...args) =>
+ localstorage.local.callMethodInParentProcess("get", args);
+ localstorage.local.set = (...args) =>
+ localstorage.local.callMethodInParentProcess("set", args);
+ localstorage.local.remove = (...args) =>
+ localstorage.local.callMethodInParentProcess("remove", args);
+ localstorage.local.clear = (...args) =>
+ localstorage.local.callMethodInParentProcess("clear", args);
+ } catch (e) {
+ console.info("Storage permission is missing");
+ }
+ return localstorage;
+ }
+
+ let messenger = {};
+ for (let api of apis) {
+ switch (api) {
+ case "storage":
+ XPCOMUtils.defineLazyGetter(messenger, "storage", () => getStorage());
+ break;
+
+ default:
+ XPCOMUtils.defineLazyGetter(messenger, api, () =>
+ context.apiCan.findAPIPath(api)
+ );
+ }
+ }
+ return messenger;
+ }
+
+ error(msg) {
+ if (this.debug) console.error("WindowListener API: " + msg);
+ }
+
+ // async sleep function using Promise
+ async sleep(delay) {
+ let timer = Components.classes["@mozilla.org/timer;1"].createInstance(
+ Components.interfaces.nsITimer
+ );
+ return new Promise(function (resolve, reject) {
+ let event = {
+ notify: function (timer) {
+ resolve();
+ },
+ };
+ timer.initWithCallback(
+ event,
+ delay,
+ Components.interfaces.nsITimer.TYPE_ONE_SHOT
+ );
+ });
+ }
+
+ getAPI(context) {
+ // Track if this is the background/main context
+ if (context.viewType != "background")
+ throw new Error(
+ "The WindowListener API may only be called from the background page."
+ );
+
+ this.context = context;
+
+ this.uniqueRandomID = "AddOnNS" + context.extension.instanceId;
+ this.menu_addonPrefs_id = "addonPrefs";
+
+ this.registeredWindows = {};
+ this.pathToStartupScript = null;
+ this.pathToShutdownScript = null;
+ this.pathToOptionsPage = null;
+ this.chromeHandle = null;
+ this.chromeData = null;
+ this.resourceData = null;
+ this.openWindows = [];
+ this.debug = context.extension.addonData.temporarilyInstalled;
+
+ const aomStartup = Cc[
+ "@mozilla.org/addons/addon-manager-startup;1"
+ ].getService(Ci.amIAddonManagerStartup);
+ const resProto = Cc[
+ "@mozilla.org/network/protocol;1?name=resource"
+ ].getService(Ci.nsISubstitutingProtocolHandler);
+
+ let self = this;
+
+ // TabMonitor to detect opening of tabs, to setup the options button in the add-on manager.
+ this.tabMonitor = {
+ onTabTitleChanged(aTab) { },
+ onTabClosing(aTab) { },
+ onTabPersist(aTab) { },
+ onTabRestored(aTab) { },
+ onTabSwitched(aNewTab, aOldTab) {
+ //self.setupAddonManager(self.getAddonManagerFromTab(aNewTab));
+ },
+ async onTabOpened(aTab) {
+ if (aTab.browser) {
+ if (!aTab.pageLoaded) {
+ // await a location change if browser is not loaded yet
+ await new Promise((resolve) => {
+ let reporterListener = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ onStateChange() { },
+ onProgressChange() { },
+ onLocationChange(
+ /* in nsIWebProgress*/ aWebProgress,
+ /* in nsIRequest*/ aRequest,
+ /* in nsIURI*/ aLocation
+ ) {
+ aTab.browser.removeProgressListener(reporterListener);
+ resolve();
+ },
+ onStatusChange() { },
+ onSecurityChange() { },
+ onContentBlockingEvent() { },
+ };
+ aTab.browser.addProgressListener(reporterListener);
+ });
+ }
+ self.setupAddonManager(self.getAddonManagerFromTab(aTab));
+ self._loadIntoWindow(aTab.browser.contentWindow, false);
+ }
+
+ if (aTab.chromeBrowser) {
+ self._loadIntoWindow(aTab.chromeBrowser.contentWindow, false);
+ }
+ },
+ };
+
+ return {
+ WindowListener: {
+ async waitForMasterPassword() {
+ // Wait until master password has been entered (if needed)
+ while (!Services.logins.isLoggedIn) {
+ self.log("Waiting for master password.");
+ await self.sleep(1000);
+ }
+ self.log("Master password has been entered.");
+ },
+
+ aDocumentExistsAt(uriString) {
+ // No sane way yet to detect if about:urls exists, maybe lookup the definition?
+ if (uriString.startsWith("about:")) {
+ return true;
+ }
+
+ self.log(
+ "Checking if document at <" +
+ uriString +
+ "> used in registration actually exists."
+ );
+ try {
+ let uriObject = Services.io.newURI(uriString);
+ let content = Cu.readUTF8URI(uriObject);
+ } catch (e) {
+ Components.utils.reportError(e);
+ return false;
+ }
+ return true;
+ },
+
+ registerOptionsPage(optionsUrl) {
+ self.pathToOptionsPage = optionsUrl.startsWith("chrome://")
+ ? optionsUrl
+ : context.extension.rootURI.resolve(optionsUrl);
+ },
+
+ registerDefaultPrefs(defaultUrl) {
+ let url = context.extension.rootURI.resolve(defaultUrl);
+
+ let prefsObj = {};
+ prefsObj.Services = ChromeUtils.import(
+ "resource://gre/modules/Services.jsm"
+ ).Services;
+ prefsObj.pref = function (aName, aDefault) {
+ let defaults = Services.prefs.getDefaultBranch("");
+ switch (typeof aDefault) {
+ case "string":
+ return defaults.setStringPref(aName, aDefault);
+
+ case "number":
+ return defaults.setIntPref(aName, aDefault);
+
+ case "boolean":
+ return defaults.setBoolPref(aName, aDefault);
+
+ default:
+ throw new Error(
+ "Preference <" +
+ aName +
+ "> has an unsupported type <" +
+ typeof aDefault +
+ ">. Allowed are string, number and boolean."
+ );
+ }
+ };
+ Services.scriptloader.loadSubScript(url, prefsObj, "UTF-8");
+ },
+
+ registerChromeUrl(data) {
+ let chromeData = [];
+ let resourceData = [];
+ for (let entry of data) {
+ if (entry[0] == "resource") resourceData.push(entry);
+ else chromeData.push(entry);
+ }
+
+ if (chromeData.length > 0) {
+ const manifestURI = Services.io.newURI(
+ "manifest.json",
+ null,
+ context.extension.rootURI
+ );
+ self.chromeHandle = aomStartup.registerChrome(
+ manifestURI,
+ chromeData
+ );
+ }
+
+ for (let res of resourceData) {
+ // [ "resource", "shortname" , "path" ]
+ let uri = Services.io.newURI(
+ res[2],
+ null,
+ context.extension.rootURI
+ );
+ resProto.setSubstitutionWithFlags(
+ res[1],
+ uri,
+ resProto.ALLOW_CONTENT_ACCESS
+ );
+ }
+
+ self.chromeData = chromeData;
+ self.resourceData = resourceData;
+ },
+
+ registerWindow(windowHref, jsFile) {
+ if (self.debug && !this.aDocumentExistsAt(windowHref)) {
+ self.error(
+ "Attempt to register an injector script for non-existent window: " +
+ windowHref
+ );
+ return;
+ }
+
+ if (!self.registeredWindows.hasOwnProperty(windowHref)) {
+ // path to JS file can either be chrome:// URL or a relative URL
+ let path = jsFile.startsWith("chrome://")
+ ? jsFile
+ : context.extension.rootURI.resolve(jsFile);
+
+ self.registeredWindows[windowHref] = path;
+ } else {
+ self.error(
+ "Window <" + windowHref + "> has already been registered"
+ );
+ }
+ },
+
+ registerStartupScript(aPath) {
+ self.pathToStartupScript = aPath.startsWith("chrome://")
+ ? aPath
+ : context.extension.rootURI.resolve(aPath);
+ },
+
+ registerShutdownScript(aPath) {
+ self.pathToShutdownScript = aPath.startsWith("chrome://")
+ ? aPath
+ : context.extension.rootURI.resolve(aPath);
+ },
+
+ openOptionsDialog(windowId) {
+ let window = context.extension.windowManager.get(windowId, context)
+ .window;
+ let WL = {};
+ WL.extension = self.extension;
+ WL.messenger = self.getMessenger(self.context);
+ window.openDialog(
+ self.pathToOptionsPage,
+ "AddonOptions",
+ "chrome,resizable,centerscreen",
+ WL
+ );
+ },
+
+ async startListening() {
+ // load the registered startup script, if one has been registered
+ // (mail3:pane may not have been fully loaded yet)
+ if (self.pathToStartupScript) {
+ let startupJS = {};
+ startupJS.WL = {};
+ startupJS.WL.extension = self.extension;
+ startupJS.WL.messenger = self.getMessenger(self.context);
+ try {
+ if (self.pathToStartupScript) {
+ Services.scriptloader.loadSubScript(
+ self.pathToStartupScript,
+ startupJS,
+ "UTF-8"
+ );
+ // delay startup until startup has been finished
+ self.log(
+ "Waiting for async startup() in <" +
+ self.pathToStartupScript +
+ "> to finish."
+ );
+ if (startupJS.startup) {
+ await startupJS.startup();
+ self.log(
+ "startup() in <" + self.pathToStartupScript + "> finished"
+ );
+ } else {
+ self.log(
+ "No startup() in <" + self.pathToStartupScript + "> found."
+ );
+ }
+ }
+ } catch (e) {
+ Components.utils.reportError(e);
+ }
+ }
+
+ let urls = Object.keys(self.registeredWindows);
+ if (urls.length > 0) {
+ // Before registering the window listener, check which windows are already open
+ self.openWindows = [];
+ for (let window of Services.wm.getEnumerator(null)) {
+ self.openWindows.push(window);
+ }
+
+ // Register window listener for all pre-registered windows
+ ExtensionSupport.registerWindowListener(
+ "injectListener_" + self.uniqueRandomID,
+ {
+ // React on all windows and manually reduce to the registered
+ // windows, so we can do special actions when the main
+ // messenger window is opened.
+ //chromeURLs: Object.keys(self.registeredWindows),
+ async onLoadWindow(window) {
+ // Load JS into window
+ await self._loadIntoWindow(
+ window,
+ self.openWindows.includes(window)
+ );
+ },
+
+ onUnloadWindow(window) {
+ // Remove JS from window, window is being closed, addon is not shut down
+ self._unloadFromWindow(window, false);
+ },
+ }
+ );
+ } else {
+ self.error("Failed to start listening, no windows registered");
+ }
+ },
+ },
+ };
+ }
+
+ _loadIntoNestedBrowsers(window, isAddonActivation) {
+ let elements = [];
+ elements = elements.concat(...window.document.getElementsByTagName("browser"));
+ elements = elements.concat(...window.document.getElementsByTagName("xul:browser"));
+ for (let element of elements) {
+ this._loadIntoWindow(element.contentWindow, isAddonActivation);
+ }
+
+ }
+
+ async _loadIntoWindow(window, isAddonActivation) {
+ const fullyLoaded = async window => {
+ for (let i = 0; i < 10; i++) {
+ await this.sleep(100);
+ if (
+ window &&
+ window.location.href != "about:blank" &&
+ window.document.readyState == "complete"
+ ) {
+ return;
+ }
+ }
+ }
+
+ await fullyLoaded(window);
+ if (!window || window.hasOwnProperty(this.uniqueRandomID)) {
+ return;
+ }
+
+ // Special action if this is the main messenger window.
+ if (window.location.href == "chrome://messenger/content/messenger.xhtml") {
+ // Add a tab monitor. The tabMonitor checks newly opened tabs and injects us.
+ this.getTabMail(window).registerTabMonitor(this.tabMonitor);
+ window[this.uniqueRandomID] = {};
+ window[this.uniqueRandomID].hasTabMonitor = true;
+
+ // Setup the options button/menu in the add-on manager, if it is already open.
+ this.setupAddonManager(this.getAddonManagerFromWindow(window), true);
+ }
+
+ // Load into nested browsers
+ this._loadIntoNestedBrowsers(window, isAddonActivation);
+
+ if (this.registeredWindows.hasOwnProperty(window.location.href)) {
+ if (!window.hasOwnProperty(this.uniqueRandomID)) {
+ window[this.uniqueRandomID] = {};
+ }
+
+ try {
+ let uniqueRandomID = this.uniqueRandomID;
+ let extension = this.extension;
+
+ // Add reference to window to add-on scope
+ window[this.uniqueRandomID].window = window;
+ window[this.uniqueRandomID].document = window.document;
+
+ // Keep track of toolbarpalettes we are injecting into
+ window[this.uniqueRandomID]._toolbarpalettes = {};
+
+ //Create WLDATA object
+ window[this.uniqueRandomID].WL = {};
+ window[this.uniqueRandomID].WL.scopeName = this.uniqueRandomID;
+
+ // Add helper function to inject CSS to WLDATA object
+ window[this.uniqueRandomID].WL.injectCSS = function (cssFile) {
+ let element;
+ let v = parseInt(Services.appinfo.version.split(".").shift());
+
+ // using createElementNS in TB78 delays the insert process and hides any security violation errors
+ if (v > 68) {
+ element = window.document.createElement("link");
+ } else {
+ let ns = window.document.documentElement.lookupNamespaceURI("html");
+ element = window.document.createElementNS(ns, "link");
+ }
+
+ element.setAttribute("wlapi_autoinjected", uniqueRandomID);
+ element.setAttribute("rel", "stylesheet");
+ element.setAttribute("href", cssFile);
+ return window.document.documentElement.appendChild(element);
+ };
+
+ // Add helper function to inject XUL to WLDATA object
+ window[this.uniqueRandomID].WL.injectElements = function (
+ xulString,
+ dtdFiles = [],
+ debug = false
+ ) {
+ let toolbarsToResolve = [];
+
+ function checkElements(stringOfIDs) {
+ let arrayOfIDs = stringOfIDs.split(",").map((e) => e.trim());
+ for (let id of arrayOfIDs) {
+ let element = window.document.getElementById(id);
+ if (element) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ function localize(entity) {
+ let msg = entity.slice("__MSG_".length, -2);
+ return extension.localeData.localizeMessage(msg);
+ }
+
+ function injectChildren(elements, container) {
+ if (debug) console.log(elements);
+
+ for (let i = 0; i < elements.length; i++) {
+ // take care of persists
+ const uri = window.document.documentURI;
+ for (const persistentNode of elements[i].querySelectorAll(
+ "[persist]"
+ )) {
+ for (const persistentAttribute of persistentNode
+ .getAttribute("persist")
+ .trim()
+ .split(" ")) {
+ if (
+ Services.xulStore.hasValue(
+ uri,
+ persistentNode.id,
+ persistentAttribute
+ )
+ ) {
+ persistentNode.setAttribute(
+ persistentAttribute,
+ Services.xulStore.getValue(
+ uri,
+ persistentNode.id,
+ persistentAttribute
+ )
+ );
+ }
+ }
+ }
+
+ if (
+ elements[i].hasAttribute("insertafter") &&
+ checkElements(elements[i].getAttribute("insertafter"))
+ ) {
+ let insertAfterElement = checkElements(
+ elements[i].getAttribute("insertafter")
+ );
+
+ if (debug)
+ console.log(
+ elements[i].tagName +
+ "#" +
+ elements[i].id +
+ ": insertafter " +
+ insertAfterElement.id
+ );
+ if (
+ debug &&
+ elements[i].id &&
+ window.document.getElementById(elements[i].id)
+ ) {
+ console.error(
+ "The id <" +
+ elements[i].id +
+ "> of the injected element already exists in the document!"
+ );
+ }
+ elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
+ insertAfterElement.parentNode.insertBefore(
+ elements[i],
+ insertAfterElement.nextSibling
+ );
+ } else if (
+ elements[i].hasAttribute("insertbefore") &&
+ checkElements(elements[i].getAttribute("insertbefore"))
+ ) {
+ let insertBeforeElement = checkElements(
+ elements[i].getAttribute("insertbefore")
+ );
+
+ if (debug)
+ console.log(
+ elements[i].tagName +
+ "#" +
+ elements[i].id +
+ ": insertbefore " +
+ insertBeforeElement.id
+ );
+ if (
+ debug &&
+ elements[i].id &&
+ window.document.getElementById(elements[i].id)
+ ) {
+ console.error(
+ "The id <" +
+ elements[i].id +
+ "> of the injected element already exists in the document!"
+ );
+ }
+ elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
+ insertBeforeElement.parentNode.insertBefore(
+ elements[i],
+ insertBeforeElement
+ );
+ } else if (
+ elements[i].id &&
+ window.document.getElementById(elements[i].id)
+ ) {
+ // existing container match, dive into recursivly
+ if (debug)
+ console.log(
+ elements[i].tagName +
+ "#" +
+ elements[i].id +
+ " is an existing container, injecting into " +
+ elements[i].id
+ );
+ injectChildren(
+ Array.from(elements[i].children),
+ window.document.getElementById(elements[i].id)
+ );
+ } else if (elements[i].localName === "toolbarpalette") {
+ // These vanish from the document but still exist via the palette property
+ if (debug) console.log(elements[i].id + " is a toolbarpalette");
+ let boxes = [
+ ...window.document.getElementsByTagName("toolbox"),
+ ];
+ let box = boxes.find(
+ (box) => box.palette && box.palette.id === elements[i].id
+ );
+ let palette = box ? box.palette : null;
+
+ if (!palette) {
+ if (debug)
+ console.log(
+ `The palette for ${elements[i].id} could not be found, deferring to later`
+ );
+ continue;
+ }
+
+ if (debug)
+ console.log(`The toolbox for ${elements[i].id} is ${box.id}`);
+
+ toolbarsToResolve.push(...box.querySelectorAll("toolbar"));
+ toolbarsToResolve.push(
+ ...window.document.querySelectorAll(
+ `toolbar[toolboxid="${box.id}"]`
+ )
+ );
+ for (let child of elements[i].children) {
+ child.setAttribute("wlapi_autoinjected", uniqueRandomID);
+ }
+ window[uniqueRandomID]._toolbarpalettes[palette.id] = palette;
+ injectChildren(Array.from(elements[i].children), palette);
+ } else {
+ // append element to the current container
+ if (debug)
+ console.log(
+ elements[i].tagName +
+ "#" +
+ elements[i].id +
+ ": append to " +
+ container.id
+ );
+ elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID);
+ container.appendChild(elements[i]);
+ }
+ }
+ }
+
+ if (debug) console.log("Injecting into root document:");
+ let localizedXulString = xulString.replace(
+ /__MSG_(.*?)__/g,
+ localize
+ );
+ injectChildren(
+ Array.from(
+ window.MozXULElement.parseXULToFragment(
+ localizedXulString,
+ dtdFiles
+ ).children
+ ),
+ window.document.documentElement
+ );
+
+ for (let bar of toolbarsToResolve) {
+ let currentset = Services.xulStore.getValue(
+ window.location,
+ bar.id,
+ "currentset"
+ );
+ if (currentset) {
+ bar.currentSet = currentset;
+ } else if (bar.getAttribute("defaultset")) {
+ bar.currentSet = bar.getAttribute("defaultset");
+ }
+ }
+ };
+
+ // Add extension object to WLDATA object
+ window[this.uniqueRandomID].WL.extension = this.extension;
+ // Add messenger object to WLDATA object
+ window[this.uniqueRandomID].WL.messenger = this.getMessenger(
+ this.context
+ );
+ // Load script into add-on scope
+ Services.scriptloader.loadSubScript(
+ this.registeredWindows[window.location.href],
+ window[this.uniqueRandomID],
+ "UTF-8"
+ );
+ window[this.uniqueRandomID].onLoad(isAddonActivation);
+ } catch (e) {
+ Components.utils.reportError(e);
+ }
+ }
+ }
+
+ _unloadFromWindow(window, isAddonDeactivation) {
+ // Unload any contained browser elements.
+ let elements = [];
+ elements = elements.concat(...window.document.getElementsByTagName("browser"));
+ elements = elements.concat(...window.document.getElementsByTagName("xul:browser"));
+ for (let element of elements) {
+ if (element.contentWindow) {
+ this._unloadFromWindow(
+ element.contentWindow,
+ isAddonDeactivation
+ );
+ }
+ }
+
+ if (
+ window.hasOwnProperty(this.uniqueRandomID) &&
+ this.registeredWindows.hasOwnProperty(window.location.href)
+ ) {
+ // Remove this window from the list of open windows
+ this.openWindows = this.openWindows.filter((e) => e != window);
+
+ if (window[this.uniqueRandomID].onUnload) {
+ try {
+ // Call onUnload()
+ window[this.uniqueRandomID].onUnload(isAddonDeactivation);
+ } catch (e) {
+ Components.utils.reportError(e);
+ }
+ }
+
+ // Remove all auto injected objects
+ let elements = Array.from(
+ window.document.querySelectorAll(
+ '[wlapi_autoinjected="' + this.uniqueRandomID + '"]'
+ )
+ );
+ for (let element of elements) {
+ element.remove();
+ }
+
+ // Remove all autoinjected toolbarpalette items
+ for (const palette of Object.values(
+ window[this.uniqueRandomID]._toolbarpalettes
+ )) {
+ let elements = Array.from(
+ palette.querySelectorAll(
+ '[wlapi_autoinjected="' + this.uniqueRandomID + '"]'
+ )
+ );
+ for (let element of elements) {
+ element.remove();
+ }
+ }
+ }
+
+ // Remove add-on scope, if it exists
+ if (window.hasOwnProperty(this.uniqueRandomID)) {
+ delete window[this.uniqueRandomID];
+ }
+ }
+
+ onShutdown(isAppShutdown) {
+ if (isAppShutdown) {
+ return; // the application gets unloaded anyway
+ }
+
+ // Unload from all still open windows
+ let urls = Object.keys(this.registeredWindows);
+ if (urls.length > 0) {
+ for (let window of Services.wm.getEnumerator(null)) {
+ //remove our entry in the add-on options menu
+ if (window.location.href == "chrome://messenger/content/messenger.xhtml") {
+ // Remove event listener for addon manager view changes
+ let managerWindow = this.getAddonManagerFromWindow(window);
+ if (
+ managerWindow &&
+ managerWindow[this.uniqueRandomID] &&
+ managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners
+ ) {
+ managerWindow.document.removeEventListener("ViewChanged", this);
+ managerWindow.document.removeEventListener("view-loaded", this);
+ managerWindow.document.removeEventListener("update", this);
+ managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = false;
+
+ let buttons = managerWindow.document.getElementsByClassName("extension-options-button");
+ for (let button of buttons) {
+ button.removeAttribute("hidden");
+ }
+ let cards = this.getCards(managerWindow);
+ // Remove options button in 88+
+ for (let card of cards) {
+ if (card.addon.id == this.extension.id) {
+ let origAddonOptionsButton = card.querySelector(".extension-options-button")
+ origAddonOptionsButton.removeAttribute("hidden");
+
+ let addonOptionsButton = card.querySelector(
+ ".windowlistener-options-button"
+ );
+ if (addonOptionsButton) addonOptionsButton.remove();
+ break;
+ }
+ }
+ }
+
+ // Remove tabmonitor
+ if (window[this.uniqueRandomID].hasTabMonitor) {
+ this.getTabMail(window).unregisterTabMonitor(this.tabMonitor);
+ window[this.uniqueRandomID].hasTabMonitor = false;
+ }
+ }
+
+ // if it is app shutdown, it is not just an add-on deactivation
+ this._unloadFromWindow(window, !isAppShutdown);
+ }
+ // Stop listening for new windows.
+ ExtensionSupport.unregisterWindowListener(
+ "injectListener_" + this.uniqueRandomID
+ );
+ }
+
+ // Load registered shutdown script
+ let shutdownJS = {};
+ shutdownJS.extension = this.extension;
+ try {
+ if (this.pathToShutdownScript)
+ Services.scriptloader.loadSubScript(
+ this.pathToShutdownScript,
+ shutdownJS,
+ "UTF-8"
+ );
+ } catch (e) {
+ Components.utils.reportError(e);
+ }
+
+ // Extract all registered chrome content urls
+ let chromeUrls = [];
+ if (this.chromeData) {
+ for (let chromeEntry of this.chromeData) {
+ if (chromeEntry[0].toLowerCase().trim() == "content") {
+ chromeUrls.push("chrome://" + chromeEntry[1] + "/");
+ }
+ }
+ }
+
+ // Unload JSMs of this add-on
+ const rootURI = this.extension.rootURI.spec;
+ for (let module of Cu.loadedModules) {
+ if (
+ module.startsWith(rootURI) ||
+ (module.startsWith("chrome://") &&
+ chromeUrls.find((s) => module.startsWith(s)))
+ ) {
+ this.log("Unloading: " + module);
+ Cu.unload(module);
+ }
+ }
+
+ // Flush all caches
+ Services.obs.notifyObservers(null, "startupcache-invalidate");
+ this.registeredWindows = {};
+
+ if (this.resourceData) {
+ const resProto = Cc[
+ "@mozilla.org/network/protocol;1?name=resource"
+ ].getService(Ci.nsISubstitutingProtocolHandler);
+ for (let res of this.resourceData) {
+ // [ "resource", "shortname" , "path" ]
+ resProto.setSubstitution(res[1], null);
+ }
+ }
+
+ if (this.chromeHandle) {
+ this.chromeHandle.destruct();
+ this.chromeHandle = null;
+ }
+ }
+};
+
+var WindowListener = getThunderbirdVersion().major < 111
+ ? WindowListener_102
+ : WindowListener_115;
diff -Nru quicktext-5.2/api/WindowListener/schema.json quicktext-5.6/api/WindowListener/schema.json
--- quicktext-5.2/api/WindowListener/schema.json 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/api/WindowListener/schema.json 2023-06-25 12:48:31.000000000 +0200
@@ -1,6 +1,6 @@
[
{
- "namespace": "WindowListener",
+ "namespace": "WindowListener",
"functions": [
{
"name": "registerDefaultPrefs",
@@ -34,7 +34,7 @@
"type": "array",
"items": {
"type": "array",
- "items" : {
+ "items": {
"type": "string"
}
},
@@ -105,4 +105,4 @@
}
]
}
-]
+]
\ Kein Zeilenumbruch am Dateiende.
diff -Nru quicktext-5.2/chrome/content/modules/wzQuicktext.jsm quicktext-5.6/chrome/content/modules/wzQuicktext.jsm
--- quicktext-5.2/chrome/content/modules/wzQuicktext.jsm 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/chrome/content/modules/wzQuicktext.jsm 2023-06-25 12:48:31.000000000 +0200
@@ -1,7 +1,6 @@
var { wzQuicktextGroup } = ChromeUtils.import("chrome://quicktext/content/modules/wzQuicktextGroup.jsm");
var { wzQuicktextTemplate } = ChromeUtils.import("chrome://quicktext/content/modules/wzQuicktextTemplate.jsm");
var { wzQuicktextScript } = ChromeUtils.import("chrome://quicktext/content/modules/wzQuicktextScript.jsm");
-var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var EXPORTED_SYMBOLS = ["gQuicktext"];
@@ -139,8 +138,7 @@
this.mSettingsLoaded = true;
- var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo).QueryInterface(Components.interfaces.nsIXULRuntime);
- this.mOS = appInfo.OS;
+ this.mOS = Services.appinfo.OS;
this.mGroup = [];
this.mTexts = [];
@@ -643,8 +641,7 @@
{
//MDN states, instead of checking if dir exists, just create it and
//catch error on exist (but it does not even throw)
- await OS.File.makeDir(aFile.parent.path);
- await OS.File.writeAtomic(aFile.path, aData, {tmpPath: aFile.path + ".tmp"});
+ await IOUtils.writeUTF8(aFile.path, aData);
}
,
pickFile: async function(aWindow, aType, aMode, aTitle)
@@ -752,7 +749,7 @@
{
// Only export scripts which have not been auto imported.
if (this.mScripts[i].type == 0) {
- buffer += "\t<script>\n\t\t<name><![CDATA["+ this.removeIllegalChars(this.mScripts[i].name) +"]]></name>\n\t\t<body><![CDATA["+ this.removeIllegalChars(this.mScripts[i].script) +"]]></body>\n\t</script>\n";
+ buffer += "\t<script>\n\t\t<name><![CDATA["+ this.removeIllegalCharsCDATA(this.mScripts[i].name) +"]]></name>\n\t\t<body><![CDATA["+ this.removeIllegalCharsCDATA(this.mScripts[i].script) +"]]></body>\n\t</script>\n";
}
}
buffer += "</quicktext>";
@@ -773,15 +770,15 @@
for (var j = 0; j < this.mTexts[i].length; j++)
{
var text = this.mTexts[i][j];
- buffer += "\t\t\t<text shortcut=\""+ this.removeIllegalChars(text.shortcut) +"\" type=\""+ this.removeIllegalChars(text.type) +"\">\n\t\t\t\t<name><![CDATA["+ this.removeIllegalChars(text.name) +"]]></name>\n";
+ buffer += "\t\t\t<text shortcut=\""+ this.removeIllegalChars(text.shortcut) +"\" type=\""+ this.removeIllegalChars(text.type) +"\">\n\t\t\t\t<name><![CDATA["+ this.removeIllegalCharsCDATA(text.name) +"]]></name>\n";
if (text.keyword != "")
- buffer += "\t\t\t\t<keyword><![CDATA["+ this.removeIllegalChars(text.keyword) +"]]></keyword>\n";
+ buffer += "\t\t\t\t<keyword><![CDATA["+ this.removeIllegalCharsCDATA(text.keyword) +"]]></keyword>\n";
if (text.subject != "")
- buffer += "\t\t\t\t<subject><![CDATA["+ this.removeIllegalChars(text.subject) +"]]></subject>\n";
+ buffer += "\t\t\t\t<subject><![CDATA["+ this.removeIllegalCharsCDATA(text.subject) +"]]></subject>\n";
if (text.text != "")
- buffer += "\t\t\t\t<body><![CDATA["+ this.removeIllegalChars(text.text) +"]]></body>\n";
+ buffer += "\t\t\t\t<body><![CDATA["+ this.removeIllegalCharsCDATA(text.text) +"]]></body>\n";
if (text.attachments != "")
- buffer += "\t\t\t\t<attachments><![CDATA["+ this.removeIllegalChars(text.attachments) +"]]></attachments>\n";
+ buffer += "\t\t\t\t<attachments><![CDATA["+ this.removeIllegalCharsCDATA(text.attachments) +"]]></attachments>\n";
// There seems to be no use to write dynamically gathered header informations from the last use of a template to the file
@@ -1014,7 +1011,16 @@
{
var tagElem = aElem.getElementsByTagName(aTag);
if (tagElem.length > 0)
- return tagElem[0].firstChild.nodeValue;
+ {
+ // can't be used anymore as sometimes there are several CDATA entries - see removeIllegalCharsCDATA
+ // return tagElem[0].firstChild.nodeValue;
+
+ var result = '';
+ for (const child of tagElem[0].childNodes) {
+ result = result + child.nodeValue;
+ }
+ return result;
+ }
return "";
}
@@ -1024,7 +1030,13 @@
return aStr.replace(new RegExp("["+ kIllegalChars +"]", 'g'), '');
}
,
-
+ removeIllegalCharsCDATA: function(aStr)
+ {
+ // https://stackoverflow.com/questions/223652/is-there-a-way-to-escape-a-cdata-end-token-in-xml
+ // replace ']]>' by ']]]]><![CDATA[>', need a regex replace to replace every occurence
+ return aStr.replace(new RegExp("["+ kIllegalChars +"]", 'g'), '').replace(/\]\]>/g, ']]]]><![CDATA[>');
+ }
+,
/*
* OBSERVERS
*/
diff -Nru quicktext-5.2/chrome/content/modules/wzQuicktextVar.jsm quicktext-5.6/chrome/content/modules/wzQuicktextVar.jsm
--- quicktext-5.2/chrome/content/modules/wzQuicktextVar.jsm 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/chrome/content/modules/wzQuicktextVar.jsm 2023-06-25 12:48:31.000000000 +0200
@@ -328,6 +328,8 @@
{
if (aVariables[0] == "full")
value.push(data[i][0] +" ("+ this.niceFileSize(data[i][1]) +")");
+ else if (aVariables[0] == "modified")
+ value.push(data[i][2])
else
value.push(data[i][0]);
}
@@ -637,7 +639,7 @@
try {
var file = fileHandler.getFileFromURLSpec(attachment.url);
if (file.exists())
- this.mData['ATT'].data.push([attachment.name, file.fileSize]);
+ this.mData['ATT'].data.push([attachment.name, file.fileSize, file.lastModifiedTime]);
}
catch(e)
{
@@ -660,7 +662,7 @@
return this.mData['INPUT'].data;
// There are two types of input select and text.
- var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
+ var promptService = Services.prompt;
if (aVariables[1] == 'select')
{
var checkValue = {};
@@ -939,7 +941,7 @@
};
this.mWindow.Recipients2CompFields(this.mWindow.gMsgCompose.compFields);
- let emailAddresses = MailServices.headerParser.parseEncodedHeader(this.mWindow.gMsgCompose.compFields.to);
+ let emailAddresses = MailServices.headerParser.parseEncodedHeaderW(this.mWindow.gMsgCompose.compFields.to);
if (emailAddresses.length > 0)
{
diff -Nru quicktext-5.2/chrome/content/quicktext.js quicktext-5.6/chrome/content/quicktext.js
--- quicktext-5.2/chrome/content/quicktext.js 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/chrome/content/quicktext.js 2023-06-25 12:48:31.000000000 +0200
@@ -460,7 +460,7 @@
tmpRecipientHeaders[header] = [];
// Create an array of emailaddresses for this header that allready added
- let tmpEmailAddresses = MailServices.headerParser.parseEncodedHeader(gMsgCompose.compFields[convertHeaderToParse[header]]);
+ let tmpEmailAddresses = MailServices.headerParser.parseEncodedHeaderW(gMsgCompose.compFields[convertHeaderToParse[header]]);
let emailAddresses = [];
for (let i = 0; i < tmpEmailAddresses.length; i++)
emailAddresses.push(tmpEmailAddresses[i].email);
@@ -469,7 +469,7 @@
for (var i = 0; i < recipientHeaders[header].length; i++)
{
// Get the mailaddresses of all the addresses
- let insertedAddresses = MailServices.headerParser.parseEncodedHeader(recipientHeaders[header][i]);
+ let insertedAddresses = MailServices.headerParser.parseEncodedHeaderW(recipientHeaders[header][i]);
for (var j = 0; j < insertedAddresses.length; j++)
{
if (insertedAddresses[j].email && !emailAddresses.includes(insertedAddresses[j].email))
@@ -715,7 +715,11 @@
,
editorKeyPress: async function(e)
{
- if (e.code == gQuicktext.keywordKey)
+ const alternatives = {
+ "Enter" : ["NumpadEnter"]
+ }
+
+ if (e.code == gQuicktext.keywordKey || alternatives[gQuicktext.keywordKey]?.includes(e.code))
{
var editor = GetCurrentEditor();
var selection = editor.selection;
diff -Nru quicktext-5.2/chrome/content/settings.js quicktext-5.6/chrome/content/settings.js
--- quicktext-5.2/chrome/content/settings.js 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/chrome/content/settings.js 2023-06-25 12:48:31.000000000 +0200
@@ -1,6 +1,5 @@
var { gQuicktext } = ChromeUtils.import("chrome://quicktext/content/modules/wzQuicktext.jsm");
var { quicktextUtils } = ChromeUtils.import("chrome://quicktext/content/modules/utils.jsm");
-var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
var quicktext = {
mChangesMade: false,
@@ -21,11 +20,10 @@
this.mLoaded = true;
// add OS as attribute to outer dialog
- document.getElementById('quicktextSettingsWindow').setAttribute("OS", OS.Constants.Sys.Name);
- console.log("Adding attribute 'OS' = '"+ OS.Constants.Sys.Name +"' to settings dialog element.");
+ document.getElementById('quicktextSettingsWindow').setAttribute("OS", Services.appinfo.OS);
+ console.log("Adding attribute 'OS' = '"+ Services.appinfo.OS +"' to settings dialog element.");
- var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo).QueryInterface(Components.interfaces.nsIXULRuntime);
- this.mOS = appInfo.OS;
+ this.mOS = Services.appinfo.OS;
gQuicktext.addObserver(this);
var hasLoadedBefore = !(await gQuicktext.loadSettings(false));
@@ -80,8 +78,7 @@
if (this.mChangesMade)
{
- promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Components.interfaces.nsIPromptService);
+ promptService = Services.prompt;
if (promptService)
{
result = promptService.confirmEx(window,
diff -Nru quicktext-5.2/chrome/locale/ja/quicktext.dtd quicktext-5.6/chrome/locale/ja/quicktext.dtd
--- quicktext-5.2/chrome/locale/ja/quicktext.dtd 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/chrome/locale/ja/quicktext.dtd 2023-06-25 12:48:31.000000000 +0200
@@ -3,26 +3,26 @@
<!ENTITY quicktext.settings.title "Quicktext の設定">
<!ENTITY quicktext.settings.label "設定">
-<!ENTITY quicktext.accesskey.settings "定">
+<!ENTITY quicktext.accesskey.settings "n">
<!ENTITY quicktext.resetcounter.label "カウンターをリセット">
-<!ENTITY quicktext.accesskey.resetCounter "タ">
+<!ENTITY quicktext.accesskey.resetCounter "r">
<!ENTITY quicktext.file.label "ファイル">
-<!ENTITY quicktext.accesskey.file "イ">
+<!ENTITY quicktext.accesskey.file "f">
<!ENTITY quicktext.goToHomepage.label "ホームページへ移動">
<!ENTITY quicktext.accesskey.goToHomepage "h">
<!ENTITY quicktext.help.label "ヘルプ">
-<!ENTITY quicktext.accesskey.help "ヘ">
+<!ENTITY quicktext.accesskey.help "p">
<!ENTITY quicktext.close.label "閉じる">
-<!ENTITY quicktext.accesskey.close "閉">
+<!ENTITY quicktext.accesskey.close "c">
<!ENTITY quicktext.save.label "保存">
-<!ENTITY quicktext.accesskey.save "存">
+<!ENTITY quicktext.accesskey.save "s">
<!ENTITY quicktext.general.label "全般">
-<!ENTITY quicktext.accesskey.general "全">
+<!ENTITY quicktext.accesskey.general "g">
<!ENTITY quicktext.templates.label "テンプレート">
-<!ENTITY quicktext.accesskey.templates "テ">
+<!ENTITY quicktext.accesskey.templates "t">
<!ENTITY quicktext.scripts.label "スクリプト">
-<!ENTITY quicktext.accesskey.scripts "ス">
+<!ENTITY quicktext.accesskey.scripts "s">
<!ENTITY quicktext.rightClickSetting.label "コンテキストメニューに Quicktext を表示">
<!ENTITY quicktext.collapseSetting.label "グループにテンプレートが1つしか含まれていない時は、グループとして表示しない">
@@ -48,25 +48,25 @@
<!ENTITY quicktext.sharingTemplates.label "共有テンプレート">
<!ENTITY quicktext.sharingScripts.label "共有スクリプト">
<!ENTITY quicktext.export.label "エクスポート">
-<!ENTITY quicktext.accesskey.export "エ">
-<!ENTITY quicktext.accesskeyTemplate.export "ク">
-<!ENTITY quicktext.accesskeyScripts.export "ス">
+<!ENTITY quicktext.accesskey.export "e">
+<!ENTITY quicktext.accesskeyTemplate.export "e">
+<!ENTITY quicktext.accesskeyScripts.export "x">
<!ENTITY quicktext.import.label "インポート">
-<!ENTITY quicktext.accesskey.import "ン">
-<!ENTITY quicktext.accesskeyTemplate.import "ポ">
-<!ENTITY quicktext.accesskeyScripts.import "ト">
+<!ENTITY quicktext.accesskey.import "i">
+<!ENTITY quicktext.accesskeyTemplate.import "i">
+<!ENTITY quicktext.accesskeyScripts.import "m">
<!ENTITY quicktext.title.label "タイトル">
<!ENTITY quicktext.addGroup.label "グループを追加">
-<!ENTITY quicktext.accesskey.addGroup "グ">
+<!ENTITY quicktext.accesskey.addGroup "p">
<!ENTITY quicktext.addTemplate.label "テンプレートを追加">
-<!ENTITY quicktext.accesskey.addTemplate "テ">
+<!ENTITY quicktext.accesskey.addTemplate "l">
<!ENTITY quicktext.addScript.label "スクリプトを追加">
-<!ENTITY quicktext.accesskey.addScript "ス">
+<!ENTITY quicktext.accesskey.addScript "a">
<!ENTITY quicktext.getScript.label "Community Scripts">
-<!ENTITY quicktext.accesskey.communityScripts "s">
+<!ENTITY quicktext.accesskey.communityScripts "c">
<!ENTITY quicktext.remove.label "削除">
-<!ENTITY quicktext.accesskey.remove "削">
+<!ENTITY quicktext.accesskey.remove "d">
<!ENTITY quicktext.shortcut.label "ショートカット">
<!ENTITY quicktext.group.label "グループ">
diff -Nru quicktext-5.2/debian/changelog quicktext-5.6/debian/changelog
--- quicktext-5.2/debian/changelog 2022-09-25 13:17:44.000000000 +0200
+++ quicktext-5.6/debian/changelog 2023-08-10 15:53:35.000000000 +0200
@@ -1,3 +1,19 @@
+quicktext (5.6-1~deb12u1) bookworm; urgency=medium
+
+ * Rebuild for bookworm
+ + for the updated thunderbird to 115.*
+
+ -- Mechtilde Stehmann <mechtilde@debian.org> Thu, 10 Aug 2023 15:53:35 +0200
+
+quicktext (5.6-1) unstable; urgency=medium
+
+ [ Mechtilde ]
+ * [a859d0b] New upstream version 5.6
+ * [0e8df4f] Bumped standard version - no changes needed
+ * [9bc840e] Bumped min version of thunderbird
+
+ -- Mechtilde Stehmann <mechtilde@debian.org> Thu, 10 Aug 2023 08:52:27 +0200
+
quicktext (5.2-1) unstable; urgency=medium
[ Mechtilde ]
diff -Nru quicktext-5.2/debian/control quicktext-5.6/debian/control
--- quicktext-5.2/debian/control 2022-09-25 13:11:21.000000000 +0200
+++ quicktext-5.6/debian/control 2023-08-10 08:47:43.000000000 +0200
@@ -5,7 +5,7 @@
Uploaders: Mechtilde Stehmann <mechtilde@debian.org>
Build-Depends: debhelper-compat (=13)
, zip
-Standards-Version: 4.6.1
+Standards-Version: 4.6.2
Rules-Requires-Root: no
Vcs-Git: https://salsa.debian.org/webext-team/quicktext.git
Vcs-Browser: https://salsa.debian.org/webext-team/quicktext
@@ -14,7 +14,7 @@
Package: webext-quicktext
Architecture: all
Depends: ${misc:Depends}
- , thunderbird (>= 1:102.2)
+ , thunderbird (>= 1:115.1)
Description: Create templates for Thunderbird
Quicktext is an extension for Thunderbird that lets you create templates that
can be easily inserted into your own emails. Using Thunderbird, Quicktext is
diff -Nru quicktext-5.2/manifest.json quicktext-5.6/manifest.json
--- quicktext-5.2/manifest.json 2022-07-04 10:54:15.000000000 +0200
+++ quicktext-5.6/manifest.json 2023-06-25 12:48:31.000000000 +0200
@@ -4,11 +4,11 @@
"gecko": {
"id": "{8845E3B3-E8FB-40E2-95E9-EC40294818C4}",
"strict_min_version": "100.0",
- "strict_max_version": "102.*"
+ "strict_max_version": "115.*"
}
},
"name": "Quicktext",
- "version": "5.2",
+ "version": "5.6",
"author": "John Bieling",
"homepage_url": "https://github.com/jobisoft/quicktext",
"default_locale": "en-US",
Attachment:
OpenPGP_signature.asc
Description: OpenPGP digital signature