Merge lp:~adrianiainlam/messagingmenu-extension/thunderbird-68 into lp:messagingmenu-extension
- thunderbird-68
- Merge into trunk
| Status: | Needs review |
|---|---|
| Proposed branch: | lp:~adrianiainlam/messagingmenu-extension/thunderbird-68 |
| Merge into: | lp:messagingmenu-extension |
| Diff against target: |
1690 lines (+1490/-65) 11 files modified
README (+13/-0) api/WindowListener/implementation.js (+1186/-0) api/WindowListener/schema.json (+108/-0) background.js (+78/-0) content/skin/indicator-messages.svg (+15/-0) content/thunderbirdMenu.js (+11/-1) manifest.json (+32/-0) res/modules/LibIndicateBackend.jsm (+10/-9) res/modules/LibMessagingMenuBackend.jsm (+7/-5) res/modules/MessagingMenuModule.jsm (+26/-46) res/modules/utils.jsm (+4/-4) |
| To merge this branch: | bzr merge lp:~adrianiainlam/messagingmenu-extension/thunderbird-68 |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Firefox and Thunderbird extension hackers | Pending | ||
|
Review via email:
|
|||
Commit message
Description of the change
Porting to Thunderbird 68. Made some minimal changes until it seems to work (Desktop notification, messaging icon turns blue, number showing in the launcher icon) in Unity in Ubuntu 18.04 with Thunderbird 68.2.2.
Didn't run the test-messaging-
- 161. By Adrian I. Lam
-
Porting to Thunderbird 78 using WindowListener API
- 162. By Adrian I. Lam
-
Port to Thunderbird 91 (still compatible with TB 78)
- 163. By Adrian I. Lam
-
Remove sender/
return- path/author check. Probably an API change has caused the author field to include
additional information. For example, I am getting<email address hidden> (Display Name)
And the part in the brackets causes a mismatch with the return-path.
I have decided to just remove this check completely, because writing
a regex that correctly extracts the email address, while covering all
corner cases, seems too complicated (cf. <https://stackoverflow. com/a/201378>). Revert setting attentionForAll to true.
Unmerged revisions
- 163. By Adrian I. Lam
-
Remove sender/
return- path/author check. Probably an API change has caused the author field to include
additional information. For example, I am getting<email address hidden> (Display Name)
And the part in the brackets causes a mismatch with the return-path.
I have decided to just remove this check completely, because writing
a regex that correctly extracts the email address, while covering all
corner cases, seems too complicated (cf. <https://stackoverflow. com/a/201378>). Revert setting attentionForAll to true.
- 162. By Adrian I. Lam
-
Port to Thunderbird 91 (still compatible with TB 78)
- 161. By Adrian I. Lam
-
Porting to Thunderbird 78 using WindowListener API
- 160. By Adrian I. Lam
-
Porting to Thunderbird 68.
- Use manifest.json instead of install.rdf
Ref: https://developer. thunderbird. net/add- ons/tb68/ overlays# switch- to-json- manifest - Changed Components.
utils.import to ChromeUtils.import
Ref: https://developer. thunderbird. net/add- ons/tb68/ changes# changed- import- of-javascript- modules - Changed name of mailServices.js
Ref: https://developer. thunderbird. net/add- ons/tb68/ changes# renamed- javascript- modules Also fixed an undefined variable bug.
Preview Diff
| 1 | === added file 'README' |
| 2 | --- README 1970-01-01 00:00:00 +0000 |
| 3 | +++ README 2021-06-05 11:50:28 +0000 |
| 4 | @@ -0,0 +1,13 @@ |
| 5 | +The code is licensed under MPL 1.1/GPL 2.0/LGPL 2.1. |
| 6 | + |
| 7 | +The code in api/WindowListener is taken from https://github.com/thundernest/addon-developer-support/tree/master/wrapper-apis/WindowListener and is licensed under MPL 2.0. |
| 8 | + |
| 9 | +The icon in content/skin/indicator-messages.svg is taken from ubuntu-mono |
| 10 | +(ubuntu-themes), by Canonical Ltd., used under CC-BY-SA 3.0. |
| 11 | + |
| 12 | +Note: I have attempted to port this addon to Thunderbird 68, and subsequently to |
| 13 | +78 and 91. However for the most part I have no idea what I'm doing. Please |
| 14 | +report back if I broke anything. |
| 15 | + |
| 16 | +Reference: https://github.com/thundernest/addon-developer-support/wiki/Using-the-WindowListener-API-to-convert-a-Legacy-Overlay-WebExtension-into-a-MailExtension-for-Thunderbird-78 |
| 17 | + |
| 18 | |
| 19 | === added directory 'api' |
| 20 | === added directory 'api/WindowListener' |
| 21 | === added file 'api/WindowListener/implementation.js' |
| 22 | --- api/WindowListener/implementation.js 1970-01-01 00:00:00 +0000 |
| 23 | +++ api/WindowListener/implementation.js 2021-06-05 11:50:28 +0000 |
| 24 | @@ -0,0 +1,1186 @@ |
| 25 | +/* |
| 26 | + * This file is provided by the addon-developer-support repository at |
| 27 | + * https://github.com/thundernest/addon-developer-support |
| 28 | + * |
| 29 | + * Version: 1.56 |
| 30 | + * |
| 31 | + * Author: John Bieling (john@thunderbird.net) |
| 32 | + * |
| 33 | + * This Source Code Form is subject to the terms of the Mozilla Public |
| 34 | + * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 35 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 36 | + */ |
| 37 | + |
| 38 | +// Import some things we need. |
| 39 | +var { ExtensionCommon } = ChromeUtils.import( |
| 40 | + "resource://gre/modules/ExtensionCommon.jsm" |
| 41 | +); |
| 42 | +var { ExtensionSupport } = ChromeUtils.import( |
| 43 | + "resource:///modules/ExtensionSupport.jsm" |
| 44 | +); |
| 45 | +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
| 46 | + |
| 47 | +var WindowListener = class extends ExtensionCommon.ExtensionAPI { |
| 48 | + log(msg) { |
| 49 | + if (this.debug) console.log("WindowListener API: " + msg); |
| 50 | + } |
| 51 | + |
| 52 | + getThunderbirdVersion() { |
| 53 | + let parts = Services.appinfo.version.split("."); |
| 54 | + return { |
| 55 | + major: parseInt(parts[0]), |
| 56 | + minor: parseInt(parts[1]), |
| 57 | + revision: parts.length > 2 ? parseInt(parts[2]) : 0, |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + getCards(e) { |
| 62 | + // This gets triggered by real events but also manually by providing the outer window. |
| 63 | + // The event is attached to the outer browser, get the inner one. |
| 64 | + let doc; |
| 65 | + |
| 66 | + // 78,86, and 87+ need special handholding. *Yeah*. |
| 67 | + if (this.getThunderbirdVersion().major < 86) { |
| 68 | + let ownerDoc = e.document || e.target.ownerDocument; |
| 69 | + doc = ownerDoc.getElementById("html-view-browser").contentDocument; |
| 70 | + } else if (this.getThunderbirdVersion().major < 87) { |
| 71 | + let ownerDoc = e.document || e.target; |
| 72 | + doc = ownerDoc.getElementById("html-view-browser").contentDocument; |
| 73 | + } else { |
| 74 | + doc = e.document || e.target; |
| 75 | + } |
| 76 | + return doc.querySelectorAll("addon-card"); |
| 77 | + } |
| 78 | + |
| 79 | + // Add pref entry to 68 |
| 80 | + add68PrefsEntry(event) { |
| 81 | + let id = this.menu_addonPrefs_id + "_" + this.uniqueRandomID; |
| 82 | + |
| 83 | + // Get the best size of the icon (16px or bigger) |
| 84 | + let iconSizes = this.extension.manifest.icons |
| 85 | + ? Object.keys(this.extension.manifest.icons) |
| 86 | + : []; |
| 87 | + iconSizes.sort((a, b) => a - b); |
| 88 | + let bestSize = iconSizes.filter((e) => parseInt(e) >= 16).shift(); |
| 89 | + let icon = bestSize ? this.extension.manifest.icons[bestSize] : ""; |
| 90 | + |
| 91 | + let name = this.extension.manifest.name; |
| 92 | + let entry = icon |
| 93 | + ? event.target.ownerGlobal.MozXULElement.parseXULToFragment( |
| 94 | + `<menuitem class="menuitem-iconic" id="${id}" image="${icon}" label="${name}" />` |
| 95 | + ) |
| 96 | + : event.target.ownerGlobal.MozXULElement.parseXULToFragment( |
| 97 | + `<menuitem id="${id}" label="${name}" />` |
| 98 | + ); |
| 99 | + |
| 100 | + event.target.appendChild(entry); |
| 101 | + let noPrefsElem = event.target.querySelector('[disabled="true"]'); |
| 102 | + // using collapse could be undone by core, so we use display none |
| 103 | + // noPrefsElem.setAttribute("collapsed", "true"); |
| 104 | + noPrefsElem.style.display = "none"; |
| 105 | + event.target.ownerGlobal.document |
| 106 | + .getElementById(id) |
| 107 | + .addEventListener("command", this); |
| 108 | + } |
| 109 | + |
| 110 | + // Event handler for the addon manager, to update the state of the options button. |
| 111 | + handleEvent(e) { |
| 112 | + switch (e.type) { |
| 113 | + // 68 add-on options menu showing |
| 114 | + case "popupshowing": |
| 115 | + { |
| 116 | + this.add68PrefsEntry(e); |
| 117 | + } |
| 118 | + break; |
| 119 | + |
| 120 | + // 78/88 add-on options menu/button click |
| 121 | + case "click": |
| 122 | + { |
| 123 | + e.preventDefault(); |
| 124 | + e.stopPropagation(); |
| 125 | + let WL = {}; |
| 126 | + WL.extension = this.extension; |
| 127 | + WL.messenger = this.getMessenger(this.context); |
| 128 | + let w = Services.wm.getMostRecentWindow("mail:3pane"); |
| 129 | + w.openDialog( |
| 130 | + this.pathToOptionsPage, |
| 131 | + "AddonOptions", |
| 132 | + "chrome,resizable,centerscreen", |
| 133 | + WL |
| 134 | + ); |
| 135 | + } |
| 136 | + break; |
| 137 | + |
| 138 | + // 68 add-on options menu command |
| 139 | + case "command": |
| 140 | + { |
| 141 | + let WL = {}; |
| 142 | + WL.extension = this.extension; |
| 143 | + WL.messenger = this.getMessenger(this.context); |
| 144 | + e.target.ownerGlobal.openDialog( |
| 145 | + this.pathToOptionsPage, |
| 146 | + "AddonOptions", |
| 147 | + "chrome,resizable,centerscreen", |
| 148 | + WL |
| 149 | + ); |
| 150 | + } |
| 151 | + break; |
| 152 | + |
| 153 | + // update, ViewChanged and manual call for add-on manager options overlay |
| 154 | + default: { |
| 155 | + let cards = this.getCards(e); |
| 156 | + for (let card of cards) { |
| 157 | + // Setup either the options entry in the menu or the button |
| 158 | + //window.document.getElementById(id).addEventListener("command", function() {window.openDialog(self.pathToOptionsPage, "AddonOptions", "chrome,resizable,centerscreen", WL)}); |
| 159 | + if (card.addon.id == this.extension.id) { |
| 160 | + let optionsMenu = |
| 161 | + (this.getThunderbirdVersion().major > 78 && this.getThunderbirdVersion().major < 88) || |
| 162 | + (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor < 10) || |
| 163 | + (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor == 10 && this.getThunderbirdVersion().revision < 2); |
| 164 | + if (optionsMenu) { |
| 165 | + // Options menu in 78.0-78.10 and 79-87 |
| 166 | + let addonOptionsLegacyEntry = card.querySelector( |
| 167 | + ".extension-options-legacy" |
| 168 | + ); |
| 169 | + if (card.addon.isActive && !addonOptionsLegacyEntry) { |
| 170 | + let addonOptionsEntry = card.querySelector( |
| 171 | + "addon-options panel-list panel-item[action='preferences']" |
| 172 | + ); |
| 173 | + addonOptionsLegacyEntry = card.ownerDocument.createElement( |
| 174 | + "panel-item" |
| 175 | + ); |
| 176 | + addonOptionsLegacyEntry.setAttribute( |
| 177 | + "data-l10n-id", |
| 178 | + "preferences-addon-button" |
| 179 | + ); |
| 180 | + addonOptionsLegacyEntry.classList.add( |
| 181 | + "extension-options-legacy" |
| 182 | + ); |
| 183 | + addonOptionsEntry.parentNode.insertBefore( |
| 184 | + addonOptionsLegacyEntry, |
| 185 | + addonOptionsEntry |
| 186 | + ); |
| 187 | + card |
| 188 | + .querySelector(".extension-options-legacy") |
| 189 | + .addEventListener("click", this); |
| 190 | + } else if (!card.addon.isActive && addonOptionsLegacyEntry) { |
| 191 | + addonOptionsLegacyEntry.remove(); |
| 192 | + } |
| 193 | + } else { |
| 194 | + // Add-on button in 88 |
| 195 | + let addonOptionsButton = card.querySelector( |
| 196 | + ".windowlistener-options-button" |
| 197 | + ); |
| 198 | + if (card.addon.isActive && !addonOptionsButton) { |
| 199 | + addonOptionsButton = card.ownerDocument.createElement("button"); |
| 200 | + addonOptionsButton.classList.add("windowlistener-options-button"); |
| 201 | + addonOptionsButton.classList.add("extension-options-button"); |
| 202 | + card.optionsButton.parentNode.insertBefore( |
| 203 | + addonOptionsButton, |
| 204 | + card.optionsButton |
| 205 | + ); |
| 206 | + card |
| 207 | + .querySelector(".windowlistener-options-button") |
| 208 | + .addEventListener("click", this); |
| 209 | + } else if (!card.addon.isActive && addonOptionsButton) { |
| 210 | + addonOptionsButton.remove(); |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | + } |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + // Some tab/add-on-manager related functions |
| 220 | + getTabMail(window) { |
| 221 | + return window.document.getElementById("tabmail"); |
| 222 | + } |
| 223 | + |
| 224 | + // returns the outer browser, not the nested browser of the add-on manager |
| 225 | + // events must be attached to the outer browser |
| 226 | + getAddonManagerFromTab(tab) { |
| 227 | + if (tab.browser) { |
| 228 | + let win = tab.browser.contentWindow; |
| 229 | + if (win && win.location.href == "about:addons") { |
| 230 | + return win; |
| 231 | + } |
| 232 | + } |
| 233 | + } |
| 234 | + |
| 235 | + getAddonManagerFromWindow(window) { |
| 236 | + let tabMail = this.getTabMail(window); |
| 237 | + for (let tab of tabMail.tabInfo) { |
| 238 | + let win = this.getAddonManagerFromTab(tab); |
| 239 | + if (win) { |
| 240 | + return win; |
| 241 | + } |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + setupAddonManager(managerWindow, forceLoad = false) { |
| 246 | + if (!managerWindow) { |
| 247 | + return; |
| 248 | + } |
| 249 | + if (!( |
| 250 | + managerWindow && |
| 251 | + managerWindow[this.uniqueRandomID] && |
| 252 | + managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners |
| 253 | + )) { |
| 254 | + managerWindow.document.addEventListener("ViewChanged", this); |
| 255 | + managerWindow.document.addEventListener("update", this); |
| 256 | + managerWindow.document.addEventListener("view-loaded", this); |
| 257 | + managerWindow[this.uniqueRandomID] = {}; |
| 258 | + managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners = true; |
| 259 | + } |
| 260 | + if (forceLoad) this.handleEvent(managerWindow); |
| 261 | + } |
| 262 | + |
| 263 | + getMessenger(context) { |
| 264 | + let apis = ["storage", "runtime", "extension", "i18n"]; |
| 265 | + |
| 266 | + function getStorage() { |
| 267 | + let localstorage = null; |
| 268 | + try { |
| 269 | + localstorage = context.apiCan.findAPIPath("storage"); |
| 270 | + localstorage.local.get = (...args) => |
| 271 | + localstorage.local.callMethodInParentProcess("get", args); |
| 272 | + localstorage.local.set = (...args) => |
| 273 | + localstorage.local.callMethodInParentProcess("set", args); |
| 274 | + localstorage.local.remove = (...args) => |
| 275 | + localstorage.local.callMethodInParentProcess("remove", args); |
| 276 | + localstorage.local.clear = (...args) => |
| 277 | + localstorage.local.callMethodInParentProcess("clear", args); |
| 278 | + } catch (e) { |
| 279 | + console.info("Storage permission is missing"); |
| 280 | + } |
| 281 | + return localstorage; |
| 282 | + } |
| 283 | + |
| 284 | + let messenger = {}; |
| 285 | + for (let api of apis) { |
| 286 | + switch (api) { |
| 287 | + case "storage": |
| 288 | + XPCOMUtils.defineLazyGetter(messenger, "storage", () => getStorage()); |
| 289 | + break; |
| 290 | + |
| 291 | + default: |
| 292 | + XPCOMUtils.defineLazyGetter(messenger, api, () => |
| 293 | + context.apiCan.findAPIPath(api) |
| 294 | + ); |
| 295 | + } |
| 296 | + } |
| 297 | + return messenger; |
| 298 | + } |
| 299 | + |
| 300 | + error(msg) { |
| 301 | + if (this.debug) console.error("WindowListener API: " + msg); |
| 302 | + } |
| 303 | + |
| 304 | + // async sleep function using Promise |
| 305 | + async sleep(delay) { |
| 306 | + let timer = Components.classes["@mozilla.org/timer;1"].createInstance( |
| 307 | + Components.interfaces.nsITimer |
| 308 | + ); |
| 309 | + return new Promise(function (resolve, reject) { |
| 310 | + let event = { |
| 311 | + notify: function (timer) { |
| 312 | + resolve(); |
| 313 | + }, |
| 314 | + }; |
| 315 | + timer.initWithCallback( |
| 316 | + event, |
| 317 | + delay, |
| 318 | + Components.interfaces.nsITimer.TYPE_ONE_SHOT |
| 319 | + ); |
| 320 | + }); |
| 321 | + } |
| 322 | + |
| 323 | + getAPI(context) { |
| 324 | + // Track if this is the background/main context |
| 325 | + if (context.viewType != "background") |
| 326 | + throw new Error( |
| 327 | + "The WindowListener API may only be called from the background page." |
| 328 | + ); |
| 329 | + |
| 330 | + this.context = context; |
| 331 | + |
| 332 | + this.uniqueRandomID = "AddOnNS" + context.extension.instanceId; |
| 333 | + this.menu_addonPrefs_id = "addonPrefs"; |
| 334 | + |
| 335 | + this.registeredWindows = {}; |
| 336 | + this.pathToStartupScript = null; |
| 337 | + this.pathToShutdownScript = null; |
| 338 | + this.pathToOptionsPage = null; |
| 339 | + this.chromeHandle = null; |
| 340 | + this.chromeData = null; |
| 341 | + this.resourceData = null; |
| 342 | + this.openWindows = []; |
| 343 | + this.debug = context.extension.addonData.temporarilyInstalled; |
| 344 | + |
| 345 | + const aomStartup = Cc[ |
| 346 | + "@mozilla.org/addons/addon-manager-startup;1" |
| 347 | + ].getService(Ci.amIAddonManagerStartup); |
| 348 | + const resProto = Cc[ |
| 349 | + "@mozilla.org/network/protocol;1?name=resource" |
| 350 | + ].getService(Ci.nsISubstitutingProtocolHandler); |
| 351 | + |
| 352 | + let self = this; |
| 353 | + |
| 354 | + // TabMonitor to detect opening of tabs, to setup the options button in the add-on manager. |
| 355 | + this.tabMonitor = { |
| 356 | + onTabTitleChanged(aTab) {}, |
| 357 | + onTabClosing(aTab) {}, |
| 358 | + onTabPersist(aTab) {}, |
| 359 | + onTabRestored(aTab) {}, |
| 360 | + onTabSwitched(aNewTab, aOldTab) { |
| 361 | + //self.setupAddonManager(self.getAddonManagerFromTab(aNewTab)); |
| 362 | + }, |
| 363 | + async onTabOpened(aTab) { |
| 364 | + if (aTab.browser) { |
| 365 | + if (!aTab.pageLoaded) { |
| 366 | + // await a location change if browser is not loaded yet |
| 367 | + await new Promise((resolve) => { |
| 368 | + let reporterListener = { |
| 369 | + QueryInterface: ChromeUtils.generateQI([ |
| 370 | + "nsIWebProgressListener", |
| 371 | + "nsISupportsWeakReference", |
| 372 | + ]), |
| 373 | + onStateChange() {}, |
| 374 | + onProgressChange() {}, |
| 375 | + onLocationChange( |
| 376 | + /* in nsIWebProgress*/ aWebProgress, |
| 377 | + /* in nsIRequest*/ aRequest, |
| 378 | + /* in nsIURI*/ aLocation |
| 379 | + ) { |
| 380 | + aTab.browser.removeProgressListener(reporterListener); |
| 381 | + resolve(); |
| 382 | + }, |
| 383 | + onStatusChange() {}, |
| 384 | + onSecurityChange() {}, |
| 385 | + onContentBlockingEvent() {}, |
| 386 | + }; |
| 387 | + aTab.browser.addProgressListener(reporterListener); |
| 388 | + }); |
| 389 | + } |
| 390 | + self.setupAddonManager(self.getAddonManagerFromTab(aTab)); |
| 391 | + } |
| 392 | + }, |
| 393 | + }; |
| 394 | + |
| 395 | + return { |
| 396 | + WindowListener: { |
| 397 | + async waitForMasterPassword() { |
| 398 | + // Wait until master password has been entered (if needed) |
| 399 | + while (!Services.logins.isLoggedIn) { |
| 400 | + self.log("Waiting for master password."); |
| 401 | + await self.sleep(1000); |
| 402 | + } |
| 403 | + self.log("Master password has been entered."); |
| 404 | + }, |
| 405 | + |
| 406 | + aDocumentExistsAt(uriString) { |
| 407 | + self.log( |
| 408 | + "Checking if document at <" + |
| 409 | + uriString + |
| 410 | + "> used in registration actually exists." |
| 411 | + ); |
| 412 | + try { |
| 413 | + let uriObject = Services.io.newURI(uriString); |
| 414 | + let content = Cu.readUTF8URI(uriObject); |
| 415 | + } catch (e) { |
| 416 | + Components.utils.reportError(e); |
| 417 | + return false; |
| 418 | + } |
| 419 | + return true; |
| 420 | + }, |
| 421 | + |
| 422 | + registerOptionsPage(optionsUrl) { |
| 423 | + self.pathToOptionsPage = optionsUrl.startsWith("chrome://") |
| 424 | + ? optionsUrl |
| 425 | + : context.extension.rootURI.resolve(optionsUrl); |
| 426 | + }, |
| 427 | + |
| 428 | + registerDefaultPrefs(defaultUrl) { |
| 429 | + let url = context.extension.rootURI.resolve(defaultUrl); |
| 430 | + |
| 431 | + let prefsObj = {}; |
| 432 | + prefsObj.Services = ChromeUtils.import( |
| 433 | + "resource://gre/modules/Services.jsm" |
| 434 | + ).Services; |
| 435 | + prefsObj.pref = function (aName, aDefault) { |
| 436 | + let defaults = Services.prefs.getDefaultBranch(""); |
| 437 | + switch (typeof aDefault) { |
| 438 | + case "string": |
| 439 | + return defaults.setStringPref(aName, aDefault); |
| 440 | + |
| 441 | + case "number": |
| 442 | + return defaults.setIntPref(aName, aDefault); |
| 443 | + |
| 444 | + case "boolean": |
| 445 | + return defaults.setBoolPref(aName, aDefault); |
| 446 | + |
| 447 | + default: |
| 448 | + throw new Error( |
| 449 | + "Preference <" + |
| 450 | + aName + |
| 451 | + "> has an unsupported type <" + |
| 452 | + typeof aDefault + |
| 453 | + ">. Allowed are string, number and boolean." |
| 454 | + ); |
| 455 | + } |
| 456 | + }; |
| 457 | + Services.scriptloader.loadSubScript(url, prefsObj, "UTF-8"); |
| 458 | + }, |
| 459 | + |
| 460 | + registerChromeUrl(data) { |
| 461 | + let chromeData = []; |
| 462 | + let resourceData = []; |
| 463 | + for (let entry of data) { |
| 464 | + if (entry[0] == "resource") resourceData.push(entry); |
| 465 | + else chromeData.push(entry); |
| 466 | + } |
| 467 | + |
| 468 | + if (chromeData.length > 0) { |
| 469 | + const manifestURI = Services.io.newURI( |
| 470 | + "manifest.json", |
| 471 | + null, |
| 472 | + context.extension.rootURI |
| 473 | + ); |
| 474 | + self.chromeHandle = aomStartup.registerChrome( |
| 475 | + manifestURI, |
| 476 | + chromeData |
| 477 | + ); |
| 478 | + } |
| 479 | + |
| 480 | + for (let res of resourceData) { |
| 481 | + // [ "resource", "shortname" , "path" ] |
| 482 | + let uri = Services.io.newURI( |
| 483 | + res[2], |
| 484 | + null, |
| 485 | + context.extension.rootURI |
| 486 | + ); |
| 487 | + resProto.setSubstitutionWithFlags( |
| 488 | + res[1], |
| 489 | + uri, |
| 490 | + resProto.ALLOW_CONTENT_ACCESS |
| 491 | + ); |
| 492 | + } |
| 493 | + |
| 494 | + self.chromeData = chromeData; |
| 495 | + self.resourceData = resourceData; |
| 496 | + }, |
| 497 | + |
| 498 | + registerWindow(windowHref, jsFile) { |
| 499 | + if (self.debug && !this.aDocumentExistsAt(windowHref)) { |
| 500 | + self.error( |
| 501 | + "Attempt to register an injector script for non-existent window: " + |
| 502 | + windowHref |
| 503 | + ); |
| 504 | + return; |
| 505 | + } |
| 506 | + |
| 507 | + if (!self.registeredWindows.hasOwnProperty(windowHref)) { |
| 508 | + // path to JS file can either be chrome:// URL or a relative URL |
| 509 | + let path = jsFile.startsWith("chrome://") |
| 510 | + ? jsFile |
| 511 | + : context.extension.rootURI.resolve(jsFile); |
| 512 | + |
| 513 | + self.registeredWindows[windowHref] = path; |
| 514 | + } else { |
| 515 | + self.error( |
| 516 | + "Window <" + windowHref + "> has already been registered" |
| 517 | + ); |
| 518 | + } |
| 519 | + }, |
| 520 | + |
| 521 | + registerStartupScript(aPath) { |
| 522 | + self.pathToStartupScript = aPath.startsWith("chrome://") |
| 523 | + ? aPath |
| 524 | + : context.extension.rootURI.resolve(aPath); |
| 525 | + }, |
| 526 | + |
| 527 | + registerShutdownScript(aPath) { |
| 528 | + self.pathToShutdownScript = aPath.startsWith("chrome://") |
| 529 | + ? aPath |
| 530 | + : context.extension.rootURI.resolve(aPath); |
| 531 | + }, |
| 532 | + |
| 533 | + openOptionsDialog(windowId) { |
| 534 | + let window = context.extension.windowManager.get(windowId, context) |
| 535 | + .window; |
| 536 | + let WL = {}; |
| 537 | + WL.extension = self.extension; |
| 538 | + WL.messenger = self.getMessenger(self.context); |
| 539 | + window.openDialog( |
| 540 | + self.pathToOptionsPage, |
| 541 | + "AddonOptions", |
| 542 | + "chrome,resizable,centerscreen", |
| 543 | + WL |
| 544 | + ); |
| 545 | + }, |
| 546 | + |
| 547 | + async startListening() { |
| 548 | + // load the registered startup script, if one has been registered |
| 549 | + // (mail3:pane may not have been fully loaded yet) |
| 550 | + if (self.pathToStartupScript) { |
| 551 | + let startupJS = {}; |
| 552 | + startupJS.WL = {}; |
| 553 | + startupJS.WL.extension = self.extension; |
| 554 | + startupJS.WL.messenger = self.getMessenger(self.context); |
| 555 | + try { |
| 556 | + if (self.pathToStartupScript) { |
| 557 | + Services.scriptloader.loadSubScript( |
| 558 | + self.pathToStartupScript, |
| 559 | + startupJS, |
| 560 | + "UTF-8" |
| 561 | + ); |
| 562 | + // delay startup until startup has been finished |
| 563 | + self.log( |
| 564 | + "Waiting for async startup() in <" + |
| 565 | + self.pathToStartupScript + |
| 566 | + "> to finish." |
| 567 | + ); |
| 568 | + if (startupJS.startup) { |
| 569 | + await startupJS.startup(); |
| 570 | + self.log( |
| 571 | + "startup() in <" + self.pathToStartupScript + "> finished" |
| 572 | + ); |
| 573 | + } else { |
| 574 | + self.log( |
| 575 | + "No startup() in <" + self.pathToStartupScript + "> found." |
| 576 | + ); |
| 577 | + } |
| 578 | + } |
| 579 | + } catch (e) { |
| 580 | + Components.utils.reportError(e); |
| 581 | + } |
| 582 | + } |
| 583 | + |
| 584 | + let urls = Object.keys(self.registeredWindows); |
| 585 | + if (urls.length > 0) { |
| 586 | + // Before registering the window listener, check which windows are already open |
| 587 | + self.openWindows = []; |
| 588 | + for (let window of Services.wm.getEnumerator(null)) { |
| 589 | + self.openWindows.push(window); |
| 590 | + } |
| 591 | + |
| 592 | + // Register window listener for all pre-registered windows |
| 593 | + ExtensionSupport.registerWindowListener( |
| 594 | + "injectListener_" + self.uniqueRandomID, |
| 595 | + { |
| 596 | + // React on all windows and manually reduce to the registered |
| 597 | + // windows, so we can do special actions when the main |
| 598 | + // messenger window is opened. |
| 599 | + //chromeURLs: Object.keys(self.registeredWindows), |
| 600 | + async onLoadWindow(window) { |
| 601 | + // Create add-on scope |
| 602 | + window[self.uniqueRandomID] = {}; |
| 603 | + |
| 604 | + // Special action #1: If this is the main messenger window |
| 605 | + if ( |
| 606 | + window.location.href == |
| 607 | + "chrome://messenger/content/messenger.xul" || |
| 608 | + window.location.href == |
| 609 | + "chrome://messenger/content/messenger.xhtml" |
| 610 | + ) { |
| 611 | + if (self.pathToOptionsPage) { |
| 612 | + if (self.getThunderbirdVersion().major < 78) { |
| 613 | + let element_addonPrefs = window.document.getElementById( |
| 614 | + self.menu_addonPrefs_id |
| 615 | + ); |
| 616 | + element_addonPrefs.addEventListener( |
| 617 | + "popupshowing", |
| 618 | + self |
| 619 | + ); |
| 620 | + } else { |
| 621 | + // Setup the options button/menu in the add-on manager, if it is already open. |
| 622 | + self.setupAddonManager( |
| 623 | + self.getAddonManagerFromWindow(window), |
| 624 | + true |
| 625 | + ); |
| 626 | + // Add a tabmonitor, to be able to setup the options button/menu in the add-on manager. |
| 627 | + self |
| 628 | + .getTabMail(window) |
| 629 | + .registerTabMonitor(self.tabMonitor); |
| 630 | + window[self.uniqueRandomID].hasTabMonitor = true; |
| 631 | + } |
| 632 | + } |
| 633 | + } |
| 634 | + |
| 635 | + // Special action #2: If this page contains browser elements |
| 636 | + let browserElements = window.document.getElementsByTagName( |
| 637 | + "browser" |
| 638 | + ); |
| 639 | + if (browserElements.length > 0) { |
| 640 | + //register a MutationObserver |
| 641 | + window[ |
| 642 | + self.uniqueRandomID |
| 643 | + ]._mObserver = new window.MutationObserver(function ( |
| 644 | + mutations |
| 645 | + ) { |
| 646 | + mutations.forEach(async function (mutation) { |
| 647 | + if ( |
| 648 | + mutation.attributeName == "src" && |
| 649 | + self.registeredWindows.hasOwnProperty( |
| 650 | + mutation.target.getAttribute("src") |
| 651 | + ) |
| 652 | + ) { |
| 653 | + // When the MutationObserver callsback, the window is still showing "about:black" and it is going |
| 654 | + // to unload and then load the new page. Any eventListener attached to the window will be removed |
| 655 | + // so we cannot listen for the load event. We have to poll manually to learn when loading has finished. |
| 656 | + // On my system it takes 70ms. |
| 657 | + let loaded = false; |
| 658 | + for (let i = 0; i < 100 && !loaded; i++) { |
| 659 | + await self.sleep(100); |
| 660 | + let targetWindow = |
| 661 | + mutation.target.contentWindow.wrappedJSObject; |
| 662 | + if ( |
| 663 | + targetWindow && |
| 664 | + targetWindow.location.href == |
| 665 | + mutation.target.getAttribute("src") && |
| 666 | + targetWindow.document.readyState == "complete" |
| 667 | + ) { |
| 668 | + loaded = true; |
| 669 | + break; |
| 670 | + } |
| 671 | + } |
| 672 | + if (loaded) { |
| 673 | + let targetWindow = |
| 674 | + mutation.target.contentWindow.wrappedJSObject; |
| 675 | + // Create add-on scope |
| 676 | + targetWindow[self.uniqueRandomID] = {}; |
| 677 | + // Inject with isAddonActivation = false |
| 678 | + self._loadIntoWindow(targetWindow, false); |
| 679 | + } |
| 680 | + } |
| 681 | + }); |
| 682 | + }); |
| 683 | + |
| 684 | + for (let element of browserElements) { |
| 685 | + if ( |
| 686 | + self.registeredWindows.hasOwnProperty( |
| 687 | + element.getAttribute("src") |
| 688 | + ) |
| 689 | + ) { |
| 690 | + let targetWindow = |
| 691 | + element.contentWindow.wrappedJSObject; |
| 692 | + // Create add-on scope |
| 693 | + targetWindow[self.uniqueRandomID] = {}; |
| 694 | + // Inject with isAddonActivation = true |
| 695 | + self._loadIntoWindow(targetWindow, true); |
| 696 | + } else { |
| 697 | + // Window/Browser is not yet fully loaded, postpone injection via MutationObserver |
| 698 | + window[self.uniqueRandomID]._mObserver.observe( |
| 699 | + element, |
| 700 | + { |
| 701 | + attributes: true, |
| 702 | + childList: false, |
| 703 | + characterData: false, |
| 704 | + } |
| 705 | + ); |
| 706 | + } |
| 707 | + } |
| 708 | + } |
| 709 | + |
| 710 | + // Load JS into window |
| 711 | + self._loadIntoWindow( |
| 712 | + window, |
| 713 | + self.openWindows.includes(window) |
| 714 | + ); |
| 715 | + }, |
| 716 | + |
| 717 | + onUnloadWindow(window) { |
| 718 | + // Remove JS from window, window is being closed, addon is not shut down |
| 719 | + self._unloadFromWindow(window, false); |
| 720 | + }, |
| 721 | + } |
| 722 | + ); |
| 723 | + } else { |
| 724 | + self.error("Failed to start listening, no windows registered"); |
| 725 | + } |
| 726 | + }, |
| 727 | + }, |
| 728 | + }; |
| 729 | + } |
| 730 | + |
| 731 | + _loadIntoWindow(window, isAddonActivation) { |
| 732 | + if ( |
| 733 | + window.hasOwnProperty(this.uniqueRandomID) && |
| 734 | + this.registeredWindows.hasOwnProperty(window.location.href) |
| 735 | + ) { |
| 736 | + try { |
| 737 | + let uniqueRandomID = this.uniqueRandomID; |
| 738 | + let extension = this.extension; |
| 739 | + |
| 740 | + // Add reference to window to add-on scope |
| 741 | + window[this.uniqueRandomID].window = window; |
| 742 | + window[this.uniqueRandomID].document = window.document; |
| 743 | + |
| 744 | + // Keep track of toolbarpalettes we are injecting into |
| 745 | + window[this.uniqueRandomID]._toolbarpalettes = {}; |
| 746 | + |
| 747 | + //Create WLDATA object |
| 748 | + window[this.uniqueRandomID].WL = {}; |
| 749 | + window[this.uniqueRandomID].WL.scopeName = this.uniqueRandomID; |
| 750 | + |
| 751 | + // Add helper function to inject CSS to WLDATA object |
| 752 | + window[this.uniqueRandomID].WL.injectCSS = function (cssFile) { |
| 753 | + let element; |
| 754 | + let v = parseInt(Services.appinfo.version.split(".").shift()); |
| 755 | + |
| 756 | + // using createElementNS in TB78 delays the insert process and hides any security violation errors |
| 757 | + if (v > 68) { |
| 758 | + element = window.document.createElement("link"); |
| 759 | + } else { |
| 760 | + let ns = window.document.documentElement.lookupNamespaceURI("html"); |
| 761 | + element = window.document.createElementNS(ns, "link"); |
| 762 | + } |
| 763 | + |
| 764 | + element.setAttribute("wlapi_autoinjected", uniqueRandomID); |
| 765 | + element.setAttribute("rel", "stylesheet"); |
| 766 | + element.setAttribute("href", cssFile); |
| 767 | + return window.document.documentElement.appendChild(element); |
| 768 | + }; |
| 769 | + |
| 770 | + // Add helper function to inject XUL to WLDATA object |
| 771 | + window[this.uniqueRandomID].WL.injectElements = function ( |
| 772 | + xulString, |
| 773 | + dtdFiles = [], |
| 774 | + debug = false |
| 775 | + ) { |
| 776 | + let toolbarsToResolve = []; |
| 777 | + |
| 778 | + function checkElements(stringOfIDs) { |
| 779 | + let arrayOfIDs = stringOfIDs.split(",").map((e) => e.trim()); |
| 780 | + for (let id of arrayOfIDs) { |
| 781 | + let element = window.document.getElementById(id); |
| 782 | + if (element) { |
| 783 | + return element; |
| 784 | + } |
| 785 | + } |
| 786 | + return null; |
| 787 | + } |
| 788 | + |
| 789 | + function localize(entity) { |
| 790 | + let msg = entity.slice("__MSG_".length, -2); |
| 791 | + return extension.localeData.localizeMessage(msg); |
| 792 | + } |
| 793 | + |
| 794 | + function injectChildren(elements, container) { |
| 795 | + if (debug) console.log(elements); |
| 796 | + |
| 797 | + for (let i = 0; i < elements.length; i++) { |
| 798 | + // take care of persists |
| 799 | + const uri = window.document.documentURI; |
| 800 | + for (const persistentNode of elements[i].querySelectorAll( |
| 801 | + "[persist]" |
| 802 | + )) { |
| 803 | + for (const persistentAttribute of persistentNode |
| 804 | + .getAttribute("persist") |
| 805 | + .trim() |
| 806 | + .split(" ")) { |
| 807 | + if ( |
| 808 | + Services.xulStore.hasValue( |
| 809 | + uri, |
| 810 | + persistentNode.id, |
| 811 | + persistentAttribute |
| 812 | + ) |
| 813 | + ) { |
| 814 | + persistentNode.setAttribute( |
| 815 | + persistentAttribute, |
| 816 | + Services.xulStore.getValue( |
| 817 | + uri, |
| 818 | + persistentNode.id, |
| 819 | + persistentAttribute |
| 820 | + ) |
| 821 | + ); |
| 822 | + } |
| 823 | + } |
| 824 | + } |
| 825 | + |
| 826 | + if ( |
| 827 | + elements[i].hasAttribute("insertafter") && |
| 828 | + checkElements(elements[i].getAttribute("insertafter")) |
| 829 | + ) { |
| 830 | + let insertAfterElement = checkElements( |
| 831 | + elements[i].getAttribute("insertafter") |
| 832 | + ); |
| 833 | + |
| 834 | + if (debug) |
| 835 | + console.log( |
| 836 | + elements[i].tagName + |
| 837 | + "#" + |
| 838 | + elements[i].id + |
| 839 | + ": insertafter " + |
| 840 | + insertAfterElement.id |
| 841 | + ); |
| 842 | + if ( |
| 843 | + debug && |
| 844 | + elements[i].id && |
| 845 | + window.document.getElementById(elements[i].id) |
| 846 | + ) { |
| 847 | + console.error( |
| 848 | + "The id <" + |
| 849 | + elements[i].id + |
| 850 | + "> of the injected element already exists in the document!" |
| 851 | + ); |
| 852 | + } |
| 853 | + elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID); |
| 854 | + insertAfterElement.parentNode.insertBefore( |
| 855 | + elements[i], |
| 856 | + insertAfterElement.nextSibling |
| 857 | + ); |
| 858 | + } else if ( |
| 859 | + elements[i].hasAttribute("insertbefore") && |
| 860 | + checkElements(elements[i].getAttribute("insertbefore")) |
| 861 | + ) { |
| 862 | + let insertBeforeElement = checkElements( |
| 863 | + elements[i].getAttribute("insertbefore") |
| 864 | + ); |
| 865 | + |
| 866 | + if (debug) |
| 867 | + console.log( |
| 868 | + elements[i].tagName + |
| 869 | + "#" + |
| 870 | + elements[i].id + |
| 871 | + ": insertbefore " + |
| 872 | + insertBeforeElement.id |
| 873 | + ); |
| 874 | + if ( |
| 875 | + debug && |
| 876 | + elements[i].id && |
| 877 | + window.document.getElementById(elements[i].id) |
| 878 | + ) { |
| 879 | + console.error( |
| 880 | + "The id <" + |
| 881 | + elements[i].id + |
| 882 | + "> of the injected element already exists in the document!" |
| 883 | + ); |
| 884 | + } |
| 885 | + elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID); |
| 886 | + insertBeforeElement.parentNode.insertBefore( |
| 887 | + elements[i], |
| 888 | + insertBeforeElement |
| 889 | + ); |
| 890 | + } else if ( |
| 891 | + elements[i].id && |
| 892 | + window.document.getElementById(elements[i].id) |
| 893 | + ) { |
| 894 | + // existing container match, dive into recursivly |
| 895 | + if (debug) |
| 896 | + console.log( |
| 897 | + elements[i].tagName + |
| 898 | + "#" + |
| 899 | + elements[i].id + |
| 900 | + " is an existing container, injecting into " + |
| 901 | + elements[i].id |
| 902 | + ); |
| 903 | + injectChildren( |
| 904 | + Array.from(elements[i].children), |
| 905 | + window.document.getElementById(elements[i].id) |
| 906 | + ); |
| 907 | + } else if (elements[i].localName === "toolbarpalette") { |
| 908 | + // These vanish from the document but still exist via the palette property |
| 909 | + if (debug) console.log(elements[i].id + " is a toolbarpalette"); |
| 910 | + let boxes = [ |
| 911 | + ...window.document.getElementsByTagName("toolbox"), |
| 912 | + ]; |
| 913 | + let box = boxes.find( |
| 914 | + (box) => box.palette && box.palette.id === elements[i].id |
| 915 | + ); |
| 916 | + let palette = box ? box.palette : null; |
| 917 | + |
| 918 | + if (!palette) { |
| 919 | + if (debug) |
| 920 | + console.log( |
| 921 | + `The palette for ${elements[i].id} could not be found, deferring to later` |
| 922 | + ); |
| 923 | + continue; |
| 924 | + } |
| 925 | + |
| 926 | + if (debug) |
| 927 | + console.log(`The toolbox for ${elements[i].id} is ${box.id}`); |
| 928 | + |
| 929 | + toolbarsToResolve.push(...box.querySelectorAll("toolbar")); |
| 930 | + toolbarsToResolve.push( |
| 931 | + ...window.document.querySelectorAll( |
| 932 | + `toolbar[toolboxid="${box.id}"]` |
| 933 | + ) |
| 934 | + ); |
| 935 | + for (let child of elements[i].children) { |
| 936 | + child.setAttribute("wlapi_autoinjected", uniqueRandomID); |
| 937 | + } |
| 938 | + window[uniqueRandomID]._toolbarpalettes[palette.id] = palette; |
| 939 | + injectChildren(Array.from(elements[i].children), palette); |
| 940 | + } else { |
| 941 | + // append element to the current container |
| 942 | + if (debug) |
| 943 | + console.log( |
| 944 | + elements[i].tagName + |
| 945 | + "#" + |
| 946 | + elements[i].id + |
| 947 | + ": append to " + |
| 948 | + container.id |
| 949 | + ); |
| 950 | + elements[i].setAttribute("wlapi_autoinjected", uniqueRandomID); |
| 951 | + container.appendChild(elements[i]); |
| 952 | + } |
| 953 | + } |
| 954 | + } |
| 955 | + |
| 956 | + if (debug) console.log("Injecting into root document:"); |
| 957 | + let localizedXulString = xulString.replace( |
| 958 | + /__MSG_(.*?)__/g, |
| 959 | + localize |
| 960 | + ); |
| 961 | + injectChildren( |
| 962 | + Array.from( |
| 963 | + window.MozXULElement.parseXULToFragment( |
| 964 | + localizedXulString, |
| 965 | + dtdFiles |
| 966 | + ).children |
| 967 | + ), |
| 968 | + window.document.documentElement |
| 969 | + ); |
| 970 | + |
| 971 | + for (let bar of toolbarsToResolve) { |
| 972 | + let currentset = Services.xulStore.getValue( |
| 973 | + window.location, |
| 974 | + bar.id, |
| 975 | + "currentset" |
| 976 | + ); |
| 977 | + if (currentset) { |
| 978 | + bar.currentSet = currentset; |
| 979 | + } else if (bar.getAttribute("defaultset")) { |
| 980 | + bar.currentSet = bar.getAttribute("defaultset"); |
| 981 | + } |
| 982 | + } |
| 983 | + }; |
| 984 | + |
| 985 | + // Add extension object to WLDATA object |
| 986 | + window[this.uniqueRandomID].WL.extension = this.extension; |
| 987 | + // Add messenger object to WLDATA object |
| 988 | + window[this.uniqueRandomID].WL.messenger = this.getMessenger( |
| 989 | + this.context |
| 990 | + ); |
| 991 | + // Load script into add-on scope |
| 992 | + Services.scriptloader.loadSubScript( |
| 993 | + this.registeredWindows[window.location.href], |
| 994 | + window[this.uniqueRandomID], |
| 995 | + "UTF-8" |
| 996 | + ); |
| 997 | + window[this.uniqueRandomID].onLoad(isAddonActivation); |
| 998 | + } catch (e) { |
| 999 | + Components.utils.reportError(e); |
| 1000 | + } |
| 1001 | + } |
| 1002 | + } |
| 1003 | + |
| 1004 | + _unloadFromWindow(window, isAddonDeactivation) { |
| 1005 | + // unload any contained browser elements |
| 1006 | + if ( |
| 1007 | + window.hasOwnProperty(this.uniqueRandomID) && |
| 1008 | + window[this.uniqueRandomID].hasOwnProperty("_mObserver") |
| 1009 | + ) { |
| 1010 | + window[this.uniqueRandomID]._mObserver.disconnect(); |
| 1011 | + let browserElements = window.document.getElementsByTagName("browser"); |
| 1012 | + for (let element of browserElements) { |
| 1013 | + if (element.contentWindow) { |
| 1014 | + this._unloadFromWindow( |
| 1015 | + element.contentWindow.wrappedJSObject, |
| 1016 | + isAddonDeactivation |
| 1017 | + ); |
| 1018 | + } |
| 1019 | + } |
| 1020 | + } |
| 1021 | + |
| 1022 | + if ( |
| 1023 | + window.hasOwnProperty(this.uniqueRandomID) && |
| 1024 | + this.registeredWindows.hasOwnProperty(window.location.href) |
| 1025 | + ) { |
| 1026 | + // Remove this window from the list of open windows |
| 1027 | + this.openWindows = this.openWindows.filter((e) => e != window); |
| 1028 | + |
| 1029 | + if (window[this.uniqueRandomID].onUnload) { |
| 1030 | + try { |
| 1031 | + // Call onUnload() |
| 1032 | + window[this.uniqueRandomID].onUnload(isAddonDeactivation); |
| 1033 | + } catch (e) { |
| 1034 | + Components.utils.reportError(e); |
| 1035 | + } |
| 1036 | + } |
| 1037 | + |
| 1038 | + // Remove all auto injected objects |
| 1039 | + let elements = Array.from( |
| 1040 | + window.document.querySelectorAll( |
| 1041 | + '[wlapi_autoinjected="' + this.uniqueRandomID + '"]' |
| 1042 | + ) |
| 1043 | + ); |
| 1044 | + for (let element of elements) { |
| 1045 | + element.remove(); |
| 1046 | + } |
| 1047 | + |
| 1048 | + // Remove all autoinjected toolbarpalette items |
| 1049 | + for (const palette of Object.values( |
| 1050 | + window[this.uniqueRandomID]._toolbarpalettes |
| 1051 | + )) { |
| 1052 | + let elements = Array.from( |
| 1053 | + palette.querySelectorAll( |
| 1054 | + '[wlapi_autoinjected="' + this.uniqueRandomID + '"]' |
| 1055 | + ) |
| 1056 | + ); |
| 1057 | + for (let element of elements) { |
| 1058 | + element.remove(); |
| 1059 | + } |
| 1060 | + } |
| 1061 | + } |
| 1062 | + |
| 1063 | + // Remove add-on scope, if it exists |
| 1064 | + if (window.hasOwnProperty(this.uniqueRandomID)) { |
| 1065 | + delete window[this.uniqueRandomID]; |
| 1066 | + } |
| 1067 | + } |
| 1068 | + |
| 1069 | + onShutdown(isAppShutdown) { |
| 1070 | + if (isAppShutdown) { |
| 1071 | + return; // the application gets unloaded anyway |
| 1072 | + } |
| 1073 | + |
| 1074 | + // Unload from all still open windows |
| 1075 | + let urls = Object.keys(this.registeredWindows); |
| 1076 | + if (urls.length > 0) { |
| 1077 | + for (let window of Services.wm.getEnumerator(null)) { |
| 1078 | + //remove our entry in the add-on options menu |
| 1079 | + if ( |
| 1080 | + this.pathToOptionsPage && |
| 1081 | + (window.location.href == "chrome://messenger/content/messenger.xul" || |
| 1082 | + window.location.href == |
| 1083 | + "chrome://messenger/content/messenger.xhtml") |
| 1084 | + ) { |
| 1085 | + if (this.getThunderbirdVersion().major < 78) { |
| 1086 | + let element_addonPrefs = window.document.getElementById( |
| 1087 | + this.menu_addonPrefs_id |
| 1088 | + ); |
| 1089 | + element_addonPrefs.removeEventListener("popupshowing", this); |
| 1090 | + // Remove our entry. |
| 1091 | + let entry = window.document.getElementById( |
| 1092 | + this.menu_addonPrefs_id + "_" + this.uniqueRandomID |
| 1093 | + ); |
| 1094 | + if (entry) entry.remove(); |
| 1095 | + // Do we have to unhide the noPrefsElement? |
| 1096 | + if (element_addonPrefs.children.length == 1) { |
| 1097 | + let noPrefsElem = element_addonPrefs.querySelector( |
| 1098 | + '[disabled="true"]' |
| 1099 | + ); |
| 1100 | + noPrefsElem.style.display = "inline"; |
| 1101 | + } |
| 1102 | + } else { |
| 1103 | + // Remove event listener for addon manager view changes |
| 1104 | + let managerWindow = this.getAddonManagerFromWindow(window); |
| 1105 | + if ( |
| 1106 | + managerWindow && |
| 1107 | + managerWindow[this.uniqueRandomID] && |
| 1108 | + managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners |
| 1109 | + ) { |
| 1110 | + managerWindow.document.removeEventListener("ViewChanged", this); |
| 1111 | + managerWindow.document.removeEventListener("view-loaded", this); |
| 1112 | + managerWindow.document.removeEventListener("update", this); |
| 1113 | + |
| 1114 | + let cards = this.getCards(managerWindow); |
| 1115 | + if (this.getThunderbirdVersion().major < 88) { |
| 1116 | + // Remove options menu in 78-87 |
| 1117 | + for (let card of cards) { |
| 1118 | + let addonOptionsLegacyEntry = card.querySelector( |
| 1119 | + ".extension-options-legacy" |
| 1120 | + ); |
| 1121 | + if (addonOptionsLegacyEntry) addonOptionsLegacyEntry.remove(); |
| 1122 | + } |
| 1123 | + } else { |
| 1124 | + // Remove options button in 88 |
| 1125 | + for (let card of cards) { |
| 1126 | + if (card.addon.id == this.extension.id) { |
| 1127 | + let addonOptionsButton = card.querySelector( |
| 1128 | + ".windowlistener-options-button" |
| 1129 | + ); |
| 1130 | + if (addonOptionsButton) addonOptionsButton.remove(); |
| 1131 | + break; |
| 1132 | + } |
| 1133 | + } |
| 1134 | + } |
| 1135 | + } |
| 1136 | + |
| 1137 | + // Remove tabmonitor |
| 1138 | + if (window[this.uniqueRandomID].hasTabMonitor) { |
| 1139 | + this.getTabMail(window).unregisterTabMonitor(this.tabMonitor); |
| 1140 | + window[this.uniqueRandomID].hasTabMonitor = false; |
| 1141 | + } |
| 1142 | + } |
| 1143 | + } |
| 1144 | + |
| 1145 | + // if it is app shutdown, it is not just an add-on deactivation |
| 1146 | + this._unloadFromWindow(window, !isAppShutdown); |
| 1147 | + } |
| 1148 | + // Stop listening for new windows. |
| 1149 | + ExtensionSupport.unregisterWindowListener( |
| 1150 | + "injectListener_" + this.uniqueRandomID |
| 1151 | + ); |
| 1152 | + } |
| 1153 | + |
| 1154 | + // Load registered shutdown script |
| 1155 | + let shutdownJS = {}; |
| 1156 | + shutdownJS.extension = this.extension; |
| 1157 | + try { |
| 1158 | + if (this.pathToShutdownScript) |
| 1159 | + Services.scriptloader.loadSubScript( |
| 1160 | + this.pathToShutdownScript, |
| 1161 | + shutdownJS, |
| 1162 | + "UTF-8" |
| 1163 | + ); |
| 1164 | + } catch (e) { |
| 1165 | + Components.utils.reportError(e); |
| 1166 | + } |
| 1167 | + |
| 1168 | + // Extract all registered chrome content urls |
| 1169 | + let chromeUrls = []; |
| 1170 | + if (this.chromeData) { |
| 1171 | + for (let chromeEntry of this.chromeData) { |
| 1172 | + if (chromeEntry[0].toLowerCase().trim() == "content") { |
| 1173 | + chromeUrls.push("chrome://" + chromeEntry[1] + "/"); |
| 1174 | + } |
| 1175 | + } |
| 1176 | + } |
| 1177 | + |
| 1178 | + // Unload JSMs of this add-on |
| 1179 | + const rootURI = this.extension.rootURI.spec; |
| 1180 | + for (let module of Cu.loadedModules) { |
| 1181 | + if ( |
| 1182 | + module.startsWith(rootURI) || |
| 1183 | + (module.startsWith("chrome://") && |
| 1184 | + chromeUrls.find((s) => module.startsWith(s))) |
| 1185 | + ) { |
| 1186 | + this.log("Unloading: " + module); |
| 1187 | + Cu.unload(module); |
| 1188 | + } |
| 1189 | + } |
| 1190 | + |
| 1191 | + // Flush all caches |
| 1192 | + Services.obs.notifyObservers(null, "startupcache-invalidate"); |
| 1193 | + this.registeredWindows = {}; |
| 1194 | + |
| 1195 | + if (this.resourceData) { |
| 1196 | + const resProto = Cc[ |
| 1197 | + "@mozilla.org/network/protocol;1?name=resource" |
| 1198 | + ].getService(Ci.nsISubstitutingProtocolHandler); |
| 1199 | + for (let res of this.resourceData) { |
| 1200 | + // [ "resource", "shortname" , "path" ] |
| 1201 | + resProto.setSubstitution(res[1], null); |
| 1202 | + } |
| 1203 | + } |
| 1204 | + |
| 1205 | + if (this.chromeHandle) { |
| 1206 | + this.chromeHandle.destruct(); |
| 1207 | + this.chromeHandle = null; |
| 1208 | + } |
| 1209 | + } |
| 1210 | +}; |
| 1211 | |
| 1212 | === added file 'api/WindowListener/schema.json' |
| 1213 | --- api/WindowListener/schema.json 1970-01-01 00:00:00 +0000 |
| 1214 | +++ api/WindowListener/schema.json 2021-06-05 11:50:28 +0000 |
| 1215 | @@ -0,0 +1,108 @@ |
| 1216 | +[ |
| 1217 | + { |
| 1218 | + "namespace": "WindowListener", |
| 1219 | + "functions": [ |
| 1220 | + { |
| 1221 | + "name": "registerDefaultPrefs", |
| 1222 | + "type": "function", |
| 1223 | + "parameters": [ |
| 1224 | + { |
| 1225 | + "name": "aPath", |
| 1226 | + "type": "string", |
| 1227 | + "description": "Relative path to the default file." |
| 1228 | + } |
| 1229 | + ] |
| 1230 | + }, |
| 1231 | + { |
| 1232 | + "name": "registerOptionsPage", |
| 1233 | + "type": "function", |
| 1234 | + "parameters": [ |
| 1235 | + { |
| 1236 | + "name": "aPath", |
| 1237 | + "type": "string", |
| 1238 | + "description": "Path to the options page, which should be made accessible in the (legacy) Add-On Options menu." |
| 1239 | + } |
| 1240 | + ] |
| 1241 | + }, |
| 1242 | + { |
| 1243 | + "name": "registerChromeUrl", |
| 1244 | + "type": "function", |
| 1245 | + "description": "Register folders which should be available as chrome:// urls (as defined in the legacy chrome.manifest)", |
| 1246 | + "parameters": [ |
| 1247 | + { |
| 1248 | + "name": "data", |
| 1249 | + "type": "array", |
| 1250 | + "items": { |
| 1251 | + "type": "array", |
| 1252 | + "items" : { |
| 1253 | + "type": "string" |
| 1254 | + } |
| 1255 | + }, |
| 1256 | + "description": "Array of manifest url definitions (content, locale, resource)" |
| 1257 | + } |
| 1258 | + ] |
| 1259 | + }, |
| 1260 | + { |
| 1261 | + "name": "waitForMasterPassword", |
| 1262 | + "type": "function", |
| 1263 | + "async": true, |
| 1264 | + "parameters": [] |
| 1265 | + }, |
| 1266 | + { |
| 1267 | + "name": "openOptionsDialog", |
| 1268 | + "type": "function", |
| 1269 | + "parameters": [ |
| 1270 | + { |
| 1271 | + "name": "windowId", |
| 1272 | + "type": "integer", |
| 1273 | + "description": "Id of the window the dialog should be opened from." |
| 1274 | + } |
| 1275 | + ] |
| 1276 | + }, |
| 1277 | + { |
| 1278 | + "name": "startListening", |
| 1279 | + "type": "function", |
| 1280 | + "async": true, |
| 1281 | + "parameters": [] |
| 1282 | + }, |
| 1283 | + { |
| 1284 | + "name": "registerWindow", |
| 1285 | + "type": "function", |
| 1286 | + "parameters": [ |
| 1287 | + { |
| 1288 | + "name": "windowHref", |
| 1289 | + "type": "string", |
| 1290 | + "description": "Url of the window, which should be listen for." |
| 1291 | + }, |
| 1292 | + { |
| 1293 | + "name": "jsFile", |
| 1294 | + "type": "string", |
| 1295 | + "description": "Path to the JavaScript file, which should be loaded into the window." |
| 1296 | + } |
| 1297 | + ] |
| 1298 | + }, |
| 1299 | + { |
| 1300 | + "name": "registerStartupScript", |
| 1301 | + "type": "function", |
| 1302 | + "parameters": [ |
| 1303 | + { |
| 1304 | + "name": "aPath", |
| 1305 | + "type": "string", |
| 1306 | + "description": "Path to a JavaScript file, which should be executed on add-on startup. The script will be executed after the main application window has been sucessfully loaded." |
| 1307 | + } |
| 1308 | + ] |
| 1309 | + }, |
| 1310 | + { |
| 1311 | + "name": "registerShutdownScript", |
| 1312 | + "type": "function", |
| 1313 | + "parameters": [ |
| 1314 | + { |
| 1315 | + "name": "aPath", |
| 1316 | + "type": "string", |
| 1317 | + "description": "Path to a JavaScript file, which should be executed on add-on shutdown." |
| 1318 | + } |
| 1319 | + ] |
| 1320 | + } |
| 1321 | + ] |
| 1322 | + } |
| 1323 | +] |
| 1324 | |
| 1325 | === added file 'background.js' |
| 1326 | --- background.js 1970-01-01 00:00:00 +0000 |
| 1327 | +++ background.js 2021-06-05 11:50:28 +0000 |
| 1328 | @@ -0,0 +1,78 @@ |
| 1329 | +(async () => { |
| 1330 | + |
| 1331 | + messenger.WindowListener.registerDefaultPrefs("defaults/preferences/messagingmenu.js"); |
| 1332 | + |
| 1333 | + messenger.WindowListener.registerChromeUrl([ |
| 1334 | + ["resource", "messagingmenu", "res/"], |
| 1335 | + ["content", "messagingmenu", "content/"], |
| 1336 | + ["locale", "messagingmenu", "af", "locale/af/"], |
| 1337 | + ["locale", "messagingmenu", "ar", "locale/ar/"], |
| 1338 | + ["locale", "messagingmenu", "ast", "locale/ast/"], |
| 1339 | + ["locale", "messagingmenu", "be", "locale/be/"], |
| 1340 | + ["locale", "messagingmenu", "bg", "locale/bg/"], |
| 1341 | + ["locale", "messagingmenu", "bn", "locale/bn/"], |
| 1342 | + ["locale", "messagingmenu", "br", "locale/br/"], |
| 1343 | + ["locale", "messagingmenu", "bs", "locale/bs/"], |
| 1344 | + ["locale", "messagingmenu", "ca", "locale/ca/"], |
| 1345 | + ["locale", "messagingmenu", "cs", "locale/cs/"], |
| 1346 | + ["locale", "messagingmenu", "cy", "locale/cy/"], |
| 1347 | + ["locale", "messagingmenu", "da", "locale/da/"], |
| 1348 | + ["locale", "messagingmenu", "de", "locale/de/"], |
| 1349 | + ["locale", "messagingmenu", "el", "locale/el/"], |
| 1350 | + ["locale", "messagingmenu", "en-GB", "locale/en-GB/"], |
| 1351 | + ["locale", "messagingmenu", "en-US", "locale/en-US/"], |
| 1352 | + ["locale", "messagingmenu", "eo", "locale/eo/"], |
| 1353 | + ["locale", "messagingmenu", "es", "locale/es/"], |
| 1354 | + ["locale", "messagingmenu", "et", "locale/et/"], |
| 1355 | + ["locale", "messagingmenu", "eu", "locale/eu/"], |
| 1356 | + ["locale", "messagingmenu", "fi", "locale/fi/"], |
| 1357 | + ["locale", "messagingmenu", "fr", "locale/fr/"], |
| 1358 | + ["locale", "messagingmenu", "gd", "locale/gd/"], |
| 1359 | + ["locale", "messagingmenu", "gl", "locale/gl/"], |
| 1360 | + ["locale", "messagingmenu", "he", "locale/he/"], |
| 1361 | + ["locale", "messagingmenu", "hi-IN", "locale/hi-IN/"], |
| 1362 | + ["locale", "messagingmenu", "hr", "locale/hr/"], |
| 1363 | + ["locale", "messagingmenu", "hu", "locale/hu/"], |
| 1364 | + ["locale", "messagingmenu", "hy-AM", "locale/hy-AM/"], |
| 1365 | + ["locale", "messagingmenu", "id", "locale/id/"], |
| 1366 | + ["locale", "messagingmenu", "is", "locale/is/"], |
| 1367 | + ["locale", "messagingmenu", "it", "locale/it/"], |
| 1368 | + ["locale", "messagingmenu", "ja", "locale/ja/"], |
| 1369 | + ["locale", "messagingmenu", "kk", "locale/kk/"], |
| 1370 | + ["locale", "messagingmenu", "kn", "locale/kn/"], |
| 1371 | + ["locale", "messagingmenu", "ko", "locale/ko/"], |
| 1372 | + ["locale", "messagingmenu", "ku", "locale/ku/"], |
| 1373 | + ["locale", "messagingmenu", "lt", "locale/lt/"], |
| 1374 | + ["locale", "messagingmenu", "lv", "locale/lv/"], |
| 1375 | + ["locale", "messagingmenu", "mk", "locale/mk/"], |
| 1376 | + ["locale", "messagingmenu", "ml", "locale/ml/"], |
| 1377 | + ["locale", "messagingmenu", "mr", "locale/mr/"], |
| 1378 | + ["locale", "messagingmenu", "nb-NO", "locale/nb-NO/"], |
| 1379 | + ["locale", "messagingmenu", "nl", "locale/nl/"], |
| 1380 | + ["locale", "messagingmenu", "nn-NO", "locale/nn-NO/"], |
| 1381 | + ["locale", "messagingmenu", "pl", "locale/pl/"], |
| 1382 | + ["locale", "messagingmenu", "pt", "locale/pt/"], |
| 1383 | + ["locale", "messagingmenu", "pt-BR", "locale/pt-BR/"], |
| 1384 | + ["locale", "messagingmenu", "ro", "locale/ro/"], |
| 1385 | + ["locale", "messagingmenu", "ru", "locale/ru/"], |
| 1386 | + ["locale", "messagingmenu", "si", "locale/si/"], |
| 1387 | + ["locale", "messagingmenu", "sk", "locale/sk/"], |
| 1388 | + ["locale", "messagingmenu", "sq", "locale/sq/"], |
| 1389 | + ["locale", "messagingmenu", "sr", "locale/sr/"], |
| 1390 | + ["locale", "messagingmenu", "sv", "locale/sv/"], |
| 1391 | + ["locale", "messagingmenu", "ta", "locale/ta/"], |
| 1392 | + ["locale", "messagingmenu", "te", "locale/te/"], |
| 1393 | + ["locale", "messagingmenu", "th", "locale/th/"], |
| 1394 | + ["locale", "messagingmenu", "tr", "locale/tr/"], |
| 1395 | + ["locale", "messagingmenu", "uk", "locale/uk/"], |
| 1396 | + ["locale", "messagingmenu", "zh-CN", "locale/zh-CN/"], |
| 1397 | + ["locale", "messagingmenu", "zh-TW", "locale/zh-TW/"] |
| 1398 | + ]); |
| 1399 | + |
| 1400 | + messenger.WindowListener.registerWindow( |
| 1401 | + "chrome://messenger/content/messenger.xhtml", |
| 1402 | + "chrome://messagingmenu/content/thunderbirdMenu.js"); |
| 1403 | + |
| 1404 | + messenger.WindowListener.startListening(); |
| 1405 | + |
| 1406 | +})(); |
| 1407 | |
| 1408 | === removed file 'build.sh' |
| 1409 | === removed file 'chrome.manifest' |
| 1410 | === removed file 'config_build.sh' |
| 1411 | === removed file 'content/prefOverlay.xul' |
| 1412 | === removed file 'content/prefs.js' |
| 1413 | === added directory 'content/skin' |
| 1414 | === added file 'content/skin/indicator-messages.svg' |
| 1415 | --- content/skin/indicator-messages.svg 1970-01-01 00:00:00 +0000 |
| 1416 | +++ content/skin/indicator-messages.svg 2021-06-05 11:50:28 +0000 |
| 1417 | @@ -0,0 +1,15 @@ |
| 1418 | +<?xml version="1.0" encoding="UTF-8"?> |
| 1419 | +<!-- Created with Inkscape (http://www.inkscape.org/) --> |
| 1420 | +<svg id="svg3229" width="24" height="24" version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> |
| 1421 | + <metadata id="metadata9"> |
| 1422 | + <rdf:RDF> |
| 1423 | + <cc:Work rdf:about=""> |
| 1424 | + <dc:format>image/svg+xml</dc:format> |
| 1425 | + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> |
| 1426 | + <dc:title/> |
| 1427 | + </cc:Work> |
| 1428 | + </rdf:RDF> |
| 1429 | + </metadata> |
| 1430 | + <path id="rect3170-2" d="m4.305 7.0221c-0.60941 0-1.305 0.7602-1.305 1.3142v9.2987c0 0.73098 0.7889 1.365 1.4535 1.365h15.049c0.79734 0 1.4978-0.67784 1.4978-1.3761v-9.1594c0-0.77554-0.68926-1.4646-1.4093-1.4646l-15.286 0.022122zm0.53871 1.1341 6.8438 6.8438h0.59375l6.875-6.8438 0.6875 0.6875-4.0938 4.1562 3.0938 3.1562-0.6875 0.6875-3.1562-3.1562-2.2812 2.3125h-1.4375l-2.2812-2.3125-3.1562 3.1875-0.6875-0.71875 3.125-3.1562-4.125-4.1562 0.6875-0.6875z" style="opacity:.3"/> |
| 1431 | + <path id="rect3170" d="m4.305 6.0221c-0.60941 0-1.305 0.7602-1.305 1.3142v9.2987c0 0.73098 0.7889 1.365 1.4535 1.365h15.049c0.79734 0 1.4978-0.67784 1.4978-1.3761v-9.1594c0-0.77554-0.68926-1.4646-1.4093-1.4646l-15.286 0.022122zm0.53871 1.1341 6.8438 6.8438h0.59375l6.875-6.8438 0.6875 0.6875-4.0938 4.1562 3.0938 3.1562-0.6875 0.6875-3.1562-3.1562-2.2812 2.3125h-1.4375l-2.2812-2.3125-3.1562 3.1875-0.6875-0.71875 3.125-3.1562-4.125-4.1562 0.6875-0.6875z" style="fill:#dfdbd2"/> |
| 1432 | +</svg> |
| 1433 | |
| 1434 | === modified file 'content/thunderbirdMenu.js' |
| 1435 | --- content/thunderbirdMenu.js 2012-08-24 13:05:59 +0000 |
| 1436 | +++ content/thunderbirdMenu.js 2021-06-05 11:50:28 +0000 |
| 1437 | @@ -38,8 +38,18 @@ |
| 1438 | |
| 1439 | (function() { |
| 1440 | try { |
| 1441 | - Components.utils.import("resource://messagingmenu/modules/MessagingMenuModule.jsm"); |
| 1442 | + var { MessagingMenu } = ChromeUtils.import("resource://messagingmenu/modules/MessagingMenuModule.jsm"); |
| 1443 | } catch(e) { |
| 1444 | Components.utils.reportError(e); |
| 1445 | } |
| 1446 | })(); |
| 1447 | + |
| 1448 | + |
| 1449 | +function onLoad(activatedWhileWindowOpen) { |
| 1450 | + |
| 1451 | +} |
| 1452 | + |
| 1453 | +function onUnload(deactivatedWhileWindowOpen) { |
| 1454 | + |
| 1455 | +} |
| 1456 | + |
| 1457 | |
| 1458 | === removed file 'content/thunderbirdOverlay.xul' |
| 1459 | === removed file 'install.rdf' |
| 1460 | === added file 'manifest.json' |
| 1461 | --- manifest.json 1970-01-01 00:00:00 +0000 |
| 1462 | +++ manifest.json 2021-06-05 11:50:28 +0000 |
| 1463 | @@ -0,0 +1,32 @@ |
| 1464 | +{ |
| 1465 | + "manifest_version": 2, |
| 1466 | + "applications": { |
| 1467 | + "gecko": { |
| 1468 | + "id": "messagingmenu@mozilla.com", |
| 1469 | + "strict_min_version": "78.0" |
| 1470 | + } |
| 1471 | + }, |
| 1472 | + "name": "Messaging Menu and Unity Launcher integration", |
| 1473 | + "description": "This extension integrates Thunderbird into the Ubuntu Unity messaging menu and launcher", |
| 1474 | + "author": "Mike Conley, Chris Coulson, and Adrian Lam", |
| 1475 | + "version": "1.5", |
| 1476 | + "experiment_apis": { |
| 1477 | + "WindowListener": { |
| 1478 | + "schema": "api/WindowListener/schema.json", |
| 1479 | + "parent": { |
| 1480 | + "scopes": ["addon_parent"], |
| 1481 | + "paths": [["WindowListener"]], |
| 1482 | + "script": "api/WindowListener/implementation.js" |
| 1483 | + } |
| 1484 | + } |
| 1485 | + }, |
| 1486 | + "background": { |
| 1487 | + "scripts": [ |
| 1488 | + "background.js" |
| 1489 | + ] |
| 1490 | + }, |
| 1491 | + "icons": { |
| 1492 | + "16": "content/skin/indicator-messages.svg" |
| 1493 | + } |
| 1494 | +} |
| 1495 | + |
| 1496 | |
| 1497 | === modified file 'res/modules/LibIndicateBackend.jsm' |
| 1498 | --- res/modules/LibIndicateBackend.jsm 2018-06-05 21:53:06 +0000 |
| 1499 | +++ res/modules/LibIndicateBackend.jsm 2021-06-05 11:50:28 +0000 |
| 1500 | @@ -41,15 +41,16 @@ |
| 1501 | |
| 1502 | var EXPORTED_SYMBOLS = [ "IndicateBackend" ]; |
| 1503 | |
| 1504 | -Cu.import("resource://gre/modules/ctypes.jsm"); |
| 1505 | -Cu.import("resource://gre/modules/Services.jsm"); |
| 1506 | -Cu.import("resource://gre/modules/NetUtil.jsm"); |
| 1507 | -Cu.import("resource://gre/modules/FileUtils.jsm"); |
| 1508 | -Cu.import("resource://messagingmenu/modules/utils.jsm"); |
| 1509 | -Cu.import("resource://messagingmenu/libs/glib.jsm"); |
| 1510 | -Cu.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1511 | -Cu.import("resource://messagingmenu/libs/dbusmenu.jsm"); |
| 1512 | -Cu.import("resource://messagingmenu/libs/indicate.jsm"); |
| 1513 | +var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); |
| 1514 | +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
| 1515 | +var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); |
| 1516 | +var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); |
| 1517 | +var { CTypesUtils, addLogger, SimpleObjectWrapper } = |
| 1518 | + ChromeUtils.import("resource://messagingmenu/modules/utils.jsm"); |
| 1519 | +var { glib } = ChromeUtils.import("resource://messagingmenu/libs/glib.jsm"); |
| 1520 | +var { gobject } = ChromeUtils.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1521 | +var { dbusmenu } = ChromeUtils.import("resource://messagingmenu/libs/dbusmenu.jsm"); |
| 1522 | +var { indicate } = ChromeUtils.import("resource://messagingmenu/libs/indicate.jsm"); |
| 1523 | |
| 1524 | addLogger(this, "backend.libindicate"); |
| 1525 | |
| 1526 | |
| 1527 | === modified file 'res/modules/LibMessagingMenuBackend.jsm' |
| 1528 | --- res/modules/LibMessagingMenuBackend.jsm 2012-10-15 00:08:19 +0000 |
| 1529 | +++ res/modules/LibMessagingMenuBackend.jsm 2021-06-05 11:50:28 +0000 |
| 1530 | @@ -41,11 +41,13 @@ |
| 1531 | |
| 1532 | var EXPORTED_SYMBOLS = [ "MessagingMenuBackend" ]; |
| 1533 | |
| 1534 | -Cu.import("resource://gre/modules/ctypes.jsm"); |
| 1535 | -Cu.import("resource://messagingmenu/modules/utils.jsm"); |
| 1536 | -Cu.import("resource://messagingmenu/libs/glib.jsm"); |
| 1537 | -Cu.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1538 | -Cu.import("resource://messagingmenu/libs/messagingmenu.jsm"); |
| 1539 | +var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); |
| 1540 | +var { CTypesUtils, addLogger, SimpleObjectWrapper } = |
| 1541 | + ChromeUtils.import("resource://messagingmenu/modules/utils.jsm"); |
| 1542 | +var { glib } = ChromeUtils.import("resource://messagingmenu/libs/glib.jsm"); |
| 1543 | +var { gobject } = ChromeUtils.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1544 | +var { messagingmenu } = |
| 1545 | + ChromeUtils.import("resource://messagingmenu/libs/messagingmenu.jsm"); |
| 1546 | |
| 1547 | addLogger(this, "backend.libmessagingmenu"); |
| 1548 | |
| 1549 | |
| 1550 | === modified file 'res/modules/MessagingMenuModule.jsm' |
| 1551 | --- res/modules/MessagingMenuModule.jsm 2018-06-05 21:53:06 +0000 |
| 1552 | +++ res/modules/MessagingMenuModule.jsm 2021-06-05 11:50:28 +0000 |
| 1553 | @@ -41,23 +41,26 @@ |
| 1554 | |
| 1555 | const Cc = Components.classes; |
| 1556 | const Ci = Components.interfaces; |
| 1557 | -const Cu = Components.utils; |
| 1558 | |
| 1559 | -Cu.import("resource://gre/modules/ctypes.jsm"); |
| 1560 | -Cu.import("resource://gre/modules/Services.jsm"); |
| 1561 | -Cu.import("resource://gre/modules/FileUtils.jsm"); |
| 1562 | -Cu.import("resource://gre/modules/AddonManager.jsm"); |
| 1563 | -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
| 1564 | -Cu.import("resource:///modules/mailServices.js"); |
| 1565 | -Cu.import("resource:///modules/iteratorUtils.jsm"); |
| 1566 | -Cu.import("resource:///modules/gloda/mimemsg.js"); |
| 1567 | -Cu.import("resource://messagingmenu/modules/utils.jsm"); |
| 1568 | -Cu.import("resource://messagingmenu/libs/glib.jsm"); |
| 1569 | -Cu.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1570 | -Cu.import("resource://messagingmenu/libs/gdk.jsm"); |
| 1571 | -Cu.import("resource://messagingmenu/libs/unity.jsm"); |
| 1572 | -Cu.import("resource://messagingmenu/modules/LibMessagingMenuBackend.jsm"); |
| 1573 | -Cu.import("resource://messagingmenu/modules/LibIndicateBackend.jsm"); |
| 1574 | +var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); |
| 1575 | +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
| 1576 | +var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm"); |
| 1577 | +var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); |
| 1578 | +var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); |
| 1579 | +var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm"); |
| 1580 | +var { MsgHdrToMimeMessage, MimeMessage, MimeContainer, |
| 1581 | + MimeBody, MimeUnknown, MimeMessageAttachment } = |
| 1582 | + ChromeUtils.import("resource:///modules/gloda/MimeMessage.jsm"); |
| 1583 | +var { CTypesUtils, addLogger, SimpleObjectWrapper } = |
| 1584 | + ChromeUtils.import("resource://messagingmenu/modules/utils.jsm"); |
| 1585 | +var { glib } = ChromeUtils.import("resource://messagingmenu/libs/glib.jsm"); |
| 1586 | +var { gobject } = ChromeUtils.import("resource://messagingmenu/libs/gobject.jsm"); |
| 1587 | +var { gdk } = ChromeUtils.import("resource://messagingmenu/libs/gdk.jsm"); |
| 1588 | +var { unity } = ChromeUtils.import("resource://messagingmenu/libs/unity.jsm"); |
| 1589 | +var { MessagingMenuBackend } = |
| 1590 | + ChromeUtils.import("resource://messagingmenu/modules/LibMessagingMenuBackend.jsm"); |
| 1591 | +var { IndicateBackend } = |
| 1592 | + ChromeUtils.import("resource://messagingmenu/modules/LibIndicateBackend.jsm"); |
| 1593 | |
| 1594 | addLogger(this); |
| 1595 | |
| 1596 | @@ -216,11 +219,10 @@ |
| 1597 | |
| 1598 | function hasMultipleAccounts() { |
| 1599 | let count = 0; |
| 1600 | - // We don't want to just call Count() on the account nsISupportsArray, as we |
| 1601 | + // We don't want to just call Count() on the account array, as we |
| 1602 | // want to filter out accounts with "none" as the incoming server type |
| 1603 | // (eg, for Local Folders) |
| 1604 | - for (let account of fixIterator(MailServices.accounts.accounts, |
| 1605 | - Ci.nsIMsgAccount)) { |
| 1606 | + for (let account of MailServices.accounts.accounts) { |
| 1607 | if (account.incomingServer.type != "none") { |
| 1608 | count++ |
| 1609 | } |
| 1610 | @@ -411,36 +413,13 @@ |
| 1611 | return aMimeMsg.has("list-id"); |
| 1612 | }, "Mailing list message"); |
| 1613 | |
| 1614 | - state.dontRequestAttentionIfTrue(function() { |
| 1615 | - if (!aMimeMsg.has("return-path")) |
| 1616 | - return false; |
| 1617 | - |
| 1618 | - let re = /.*<([^>]*)>/; |
| 1619 | - let rp = aMimeMsg.get("return-path").replace(re, "$1"); |
| 1620 | - let from = aItemHeader.author.replace(re, "$1"); |
| 1621 | - |
| 1622 | - return rp != from; |
| 1623 | - }, "Possible automated message (Return-Path != From)"); |
| 1624 | - |
| 1625 | - state.dontRequestAttentionIfTrue(function() { |
| 1626 | - if (!aMimeMsg.has("sender")) |
| 1627 | - return false; |
| 1628 | - |
| 1629 | - let re = /.*<([^>]*)>/; |
| 1630 | - let sender = aMimeMsg.get("sender").replace(re, "$1"); |
| 1631 | - let from = aItemHeader.author.replace(re, "$1"); |
| 1632 | - |
| 1633 | - return sender.toLowerCase() != from.toLowerCase(); |
| 1634 | - }, "Possible automated message (Sender != From)"); |
| 1635 | - |
| 1636 | state.requestAttentionIfTrue(function() { |
| 1637 | let recipients = aItemHeader.recipients.split(","); |
| 1638 | let re = /.*<([^>]*)>/; // Convert "Foo <bar>" in to "bar" |
| 1639 | for (let i in recipients) { |
| 1640 | let recipient = recipients[i].replace(re, "$1").toLowerCase(); |
| 1641 | |
| 1642 | - for (let id of fixIterator(MailServices.accounts.allIdentities, |
| 1643 | - Ci.nsIMsgIdentity)) { |
| 1644 | + for (let id of MailServices.accounts.allIdentities) { |
| 1645 | if (recipient.indexOf(id.email.toLowerCase()) != -1) |
| 1646 | return true; |
| 1647 | } |
| 1648 | @@ -568,10 +547,10 @@ |
| 1649 | this.isInbox()) { |
| 1650 | this.label = this._folder.server.prettyName; |
| 1651 | } else if (hasMultipleAccounts()) { |
| 1652 | - this.label = this._folder.prettiestName + |
| 1653 | + this.label = this._folder.prettyName + |
| 1654 | " (" + this._folder.server.prettyName + ")"; |
| 1655 | } else { |
| 1656 | - this.label = this._folder.prettiestName; |
| 1657 | + this.label = this._folder.prettyName; |
| 1658 | } |
| 1659 | }, |
| 1660 | |
| 1661 | @@ -788,7 +767,8 @@ |
| 1662 | let inboxOnly = gPrefs.getBoolPref(kPrefInboxOnly, false); |
| 1663 | let accumulator = 0; |
| 1664 | for (let url in this.mIndicators) { |
| 1665 | - if (!this.mIndicators[url].isInbox() && inboxOnly) { |
| 1666 | + let indicator = this.mIndicators[url]; |
| 1667 | + if (!indicator.isInbox() && inboxOnly) { |
| 1668 | continue; |
| 1669 | } |
| 1670 | |
| 1671 | |
| 1672 | === modified file 'res/modules/utils.jsm' |
| 1673 | --- res/modules/utils.jsm 2018-06-05 21:53:06 +0000 |
| 1674 | +++ res/modules/utils.jsm 2021-06-05 11:50:28 +0000 |
| 1675 | @@ -2,12 +2,12 @@ |
| 1676 | |
| 1677 | const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; |
| 1678 | |
| 1679 | -Cu.import("resource://gre/modules/Services.jsm"); |
| 1680 | -Cu.import("resource://gre/modules/AddonManager.jsm"); |
| 1681 | -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
| 1682 | +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
| 1683 | +var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); |
| 1684 | +var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); |
| 1685 | |
| 1686 | XPCOMUtils.defineLazyGetter(this, "ctypes", function() { |
| 1687 | - Cu.import("resource://gre/modules/ctypes.jsm"); |
| 1688 | + var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm"); |
| 1689 | return ctypes; |
| 1690 | }); |
| 1691 |