pax_global_header00006660000000000000000000000064141044340640014512gustar00rootroot0000000000000052 comment=d8b67fc5ca9be71813530c2fb476ca8656e8df2b allow-html-temp-6.3.7/000077500000000000000000000000001410443406400145525ustar00rootroot00000000000000allow-html-temp-6.3.7/.gitignore000066400000000000000000000001621410443406400165410ustar00rootroot00000000000000# Windows # thumbnails Thumbs.db # Mac OS X # metadata .DS_Store # thumbnails ._* # Visual Studio Code .vscode/ allow-html-temp-6.3.7/LICENSE000066400000000000000000000405251410443406400155650ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.allow-html-temp-6.3.7/README.md000066400000000000000000000034761410443406400160430ustar00rootroot00000000000000***Allow HTML Temp*** -------- ### Features * Allows to have HTML temporarily allowed in the currently displayed message by only one click. When switching to another message, it'll be shown automatically again in plain text or simple html mode (according to your default mode). ### Known issues * It's not possible to use the provided functions to easily print, forward or answer with HTML temporarily allowed. It seems there is no possibility to implement these repeatedly requested features by an addon. It would be necessary to change Thunderbird core functions, to get these things working. If you are interested in getting the whole functionality build in Thunderbirds core, then contribute to the RFE [Bug 1598857](https://bugzilla.mozilla.org/show_bug.cgi?id=1598857). ### Installation 1. [Download Allow HTML Temp from the official Thunderbird add-on page](https://addons.thunderbird.net/addon/allow-html-temp/) 2. [Installing an Add-on in Thunderbird](https://support.mozilla.org/kb/installing-addon-thunderbird) ### Contribution You are welcomed to contribute to this project by: * adding or improving the localizations of AttachmentExtractor Continued via email to me or a post in german Thunderbird forums [Thunderbird Mail DE](https://www.thunderbird-mail.de/forum/board/81-hilfe-und-fehlermeldungen-zu-thunders-add-ons/) or just create an [issue](https://gitlab.com/ThunderbirdMailDE/allow-html-temp/issues/) here on GitLab * creating [issues](https://gitlab.com/ThunderbirdMailDE/allow-html-temp/issues/) about problems * creating [issues](https://gitlab.com/ThunderbirdMailDE/allow-html-temp/issues/) about possible improvements ### Coders * Alexander Ihrig (Original Author and Maintainer) ### Translators * ### License [Mozilla Public License version 2.0](https://gitlab.com/ThunderbirdMailDE/allow-html-temp/LICENSE)allow-html-temp-6.3.7/_locales/000077500000000000000000000000001410443406400163335ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/ar/000077500000000000000000000000001410443406400167355ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/ar/messages.json000066400000000000000000000002561410443406400214420ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "إسمح مؤقتا HTML في الرسائل المختارة" } } allow-html-temp-6.3.7/_locales/cs/000077500000000000000000000000001410443406400167405ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/cs/messages.json000066400000000000000000000002411410443406400214370ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Povoluje dočasně HTML ve vybraných zprávách" } } allow-html-temp-6.3.7/_locales/da/000077500000000000000000000000001410443406400167175ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/da/messages.json000066400000000000000000000002361410443406400214220ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Midlertidig tillad HTML i en valgt meddelelse" } } allow-html-temp-6.3.7/_locales/de/000077500000000000000000000000001410443406400167235ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/de/messages.json000066400000000000000000000002551410443406400214270ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Erlaubt HTML per Klick temporär für die aktuelle Nachricht" } } allow-html-temp-6.3.7/_locales/el/000077500000000000000000000000001410443406400167335ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/el/messages.json000066400000000000000000000003261410443406400214360ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Επιτρέπει προσωρινά τη χρήση HTML σε επιλεγμένα μηνύματα" } } allow-html-temp-6.3.7/_locales/en/000077500000000000000000000000001410443406400167355ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/en/messages.json000066400000000000000000000002351410443406400214370ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Allow temporarily HTML in a selected message" } } allow-html-temp-6.3.7/_locales/es/000077500000000000000000000000001410443406400167425ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/es/messages.json000066400000000000000000000002601410443406400214420ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Activa temporalmente el código HTML en el mensaje seleccionado" } } allow-html-temp-6.3.7/_locales/fr/000077500000000000000000000000001410443406400167425ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/fr/messages.json000066400000000000000000000002551410443406400214460ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Autorise temporairement l'HTML dans le message sélectionné" } } allow-html-temp-6.3.7/_locales/he/000077500000000000000000000000001410443406400167275ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/he/messages.json000066400000000000000000000002361410443406400214320ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "אפשר זמנית HTML במסר הנבחר." } } allow-html-temp-6.3.7/_locales/hr/000077500000000000000000000000001410443406400167445ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/hr/messages.json000066400000000000000000000002351410443406400214460ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Privremeno dopustite HTML u odabranoj poruci" } } allow-html-temp-6.3.7/_locales/it/000077500000000000000000000000001410443406400167475ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/it/messages.json000066400000000000000000000003071410443406400214510ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Permette di attivare temporaneamente la visualizzazione HTML nel messaggio selezionato" } } allow-html-temp-6.3.7/_locales/ja/000077500000000000000000000000001410443406400167255ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/ja/messages.json000066400000000000000000000002741410443406400214320ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "選択したメッセージを一時的に HTML 形式で表示します。" } } allow-html-temp-6.3.7/_locales/ms/000077500000000000000000000000001410443406400167525ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/ms/messages.json000066400000000000000000000002341410443406400214530ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Benarkan HTML sementara dalam mesej dipilih" } } allow-html-temp-6.3.7/_locales/nl/000077500000000000000000000000001410443406400167445ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/nl/messages.json000066400000000000000000000002451410443406400214470ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Tijdelijk HTML toestaan in het geselecteerde bericht" } } allow-html-temp-6.3.7/_locales/pl/000077500000000000000000000000001410443406400167465ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/pl/messages.json000066400000000000000000000002551410443406400214520ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Zezwól na tymczasowe użycie HTML w zaznaczonej wiadomości" } } allow-html-temp-6.3.7/_locales/pt_BR/000077500000000000000000000000001410443406400173415ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/pt_BR/messages.json000066400000000000000000000002411410443406400220400ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Permite HTML temporário na mensagem selecionada" } } allow-html-temp-6.3.7/_locales/ru/000077500000000000000000000000001410443406400167615ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/ru/messages.json000066400000000000000000000003341410443406400214630ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Временно разрешает HTML-форматирование выбранных сообщений" } } allow-html-temp-6.3.7/_locales/sk/000077500000000000000000000000001410443406400167505ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/sk/messages.json000066400000000000000000000002721410443406400214530ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Umožňuje dočasné prepnutie vybranej textovej správy do formátu HTML" } } allow-html-temp-6.3.7/_locales/sr/000077500000000000000000000000001410443406400167575ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/sr/messages.json000066400000000000000000000003001410443406400214520ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Привремено дозвољава HTML у изабраној поруци" } } allow-html-temp-6.3.7/_locales/sv/000077500000000000000000000000001410443406400167635ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/sv/messages.json000066400000000000000000000002421410443406400214630ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Tillåt HTML temporärt i ett markerat meddelande" } } allow-html-temp-6.3.7/_locales/tr/000077500000000000000000000000001410443406400167605ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/tr/messages.json000066400000000000000000000002371410443406400214640ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Seçili mesajda geçici olarak HTML'e izin ver" } } allow-html-temp-6.3.7/_locales/uk/000077500000000000000000000000001410443406400167525ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/uk/messages.json000066400000000000000000000003101410443406400214460ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "Тимчасово дозволяє HTML у вибраному повідомленні" } } allow-html-temp-6.3.7/_locales/zh/000077500000000000000000000000001410443406400167545ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/zh/messages.json000066400000000000000000000002271410443406400214570ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "临时允许选中的消息执行 HTML" } } allow-html-temp-6.3.7/_locales/zh_TW/000077500000000000000000000000001410443406400173665ustar00rootroot00000000000000allow-html-temp-6.3.7/_locales/zh_TW/messages.json000066400000000000000000000002241410443406400220660ustar00rootroot00000000000000{ "extensionName": { "message": "Allow HTML Temp" }, "extensionDescription": { "message": "在選中的HTML中臨時允許HTML" } } allow-html-temp-6.3.7/api/000077500000000000000000000000001410443406400153235ustar00rootroot00000000000000allow-html-temp-6.3.7/api/WindowListener/000077500000000000000000000000001410443406400203005ustar00rootroot00000000000000allow-html-temp-6.3.7/api/WindowListener/CHANGELOG.md000066400000000000000000000057471410443406400221260ustar00rootroot00000000000000Version: 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 Version: 1.53 ------------- - fix "tab.browser is undefined" bug Version: 1.52 ------------- - clear cache only if add-on is uninstalled/updated, not on app shutdown Version: 1.51 ------------- - use wrench button for options for TB78.10 Version: 1.50 ------------- - use built-in CSS rules to fix options button for dark themes (thanks to Thunder) - fix some occasions where options button was not added Version: 1.49 ------------- - fixed missing eventListener for Beta + Daily Version: 1.48 ------------- - moved notifyTools into its own NotifyTools API. Version: 1.39 ------------- - fix for 68 Version: 1.36 ------------- - fix for beta 87 Version: 1.35 ------------- - add support for options button/menu in add-on manager and fix 68 double menu entry Version: 1.34 ------------- - fix error in unload Version: 1.33 ------------- - fix for e10s Version: 1.30 ------------- - replace setCharPref by setStringPref to cope with UTF-8 encoding Version: 1.29 ------------- - open options window centered Version: 1.28 ------------- - do not crash on missing icon Version: 1.27 ------------- - add openOptionsDialog() Version: 1.26 ------------- - pass WL object to legacy preference window Version: 1.25 ------------- - adding waitForMasterPassword Version: 1.24 ------------- - automatically localize i18n locale strings in injectElements() Version: 1.22 ------------- - to reduce confusions, only check built-in URLs as add-on URLs cannot be resolved if a temp installed add-on has bin zipped Version: 1.21 ------------- - print debug messages only if add-ons are installed temporarily from the add-on debug page - add checks to registered windows and scripts, if they actually exists Version: 1.20 ------------- - fix long delay before customize window opens - fix non working removal of palette items Version: 1.19 ------------- - add support for ToolbarPalette Version: 1.18 ------------- - execute shutdown script also during global app shutdown (fixed) Version: 1.17 ------------- - execute shutdown script also during global app shutdown Version: 1.16 ------------- - support for persist Version: 1.15 ------------- - make (undocumented) startup() async Version: 1.14 ------------- - support resource urls Version: 1.12 ------------- - no longer allow to enforce custom "namespace" - no longer call it namespace but uniqueRandomID / scopeName - expose special objects as the global WL object - autoremove injected elements after onUnload has ben executed Version: 1.9 ------------- - automatically remove all entries added by injectElements Version: 1.8 ------------- - add injectElements Version: 1.7 ------------- - add injectCSS - add optional enforced namespace Version: 1.6 ------------- - added mutation observer to be able to inject into browser elements - use larger icons as fallbackallow-html-temp-6.3.7/api/WindowListener/implementation.js000066400000000000000000001234411410443406400236700ustar00rootroot00000000000000/* * This file is provided by the addon-developer-support repository at * https://github.com/thundernest/addon-developer-support * * Version: 1.56 * * Author: John Bieling (john@thunderbird.net) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Import some things we need. var { ExtensionCommon } = ChromeUtils.import( "resource://gre/modules/ExtensionCommon.jsm" ); var { ExtensionSupport } = ChromeUtils.import( "resource:///modules/ExtensionSupport.jsm" ); 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); } getThunderbirdVersion() { let parts = Services.appinfo.version.split("."); return { major: parseInt(parts[0]), minor: parseInt(parts[1]), revision: parts.length > 2 ? parseInt(parts[2]) : 0, } } 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; // 78,86, and 87+ need special handholding. *Yeah*. if (this.getThunderbirdVersion().major < 86) { let ownerDoc = e.document || e.target.ownerDocument; doc = ownerDoc.getElementById("html-view-browser").contentDocument; } else if (this.getThunderbirdVersion().major < 87) { let ownerDoc = e.document || e.target; doc = ownerDoc.getElementById("html-view-browser").contentDocument; } else { doc = e.document || e.target; } return doc.querySelectorAll("addon-card"); } // Add pref entry to 68 add68PrefsEntry(event) { let id = this.menu_addonPrefs_id + "_" + this.uniqueRandomID; // Get the best size of the icon (16px or bigger) let iconSizes = this.extension.manifest.icons ? Object.keys(this.extension.manifest.icons) : []; iconSizes.sort((a, b) => a - b); let bestSize = iconSizes.filter((e) => parseInt(e) >= 16).shift(); let icon = bestSize ? this.extension.manifest.icons[bestSize] : ""; let name = this.extension.manifest.name; let entry = icon ? event.target.ownerGlobal.MozXULElement.parseXULToFragment( `` ) : event.target.ownerGlobal.MozXULElement.parseXULToFragment( `` ); event.target.appendChild(entry); let noPrefsElem = event.target.querySelector('[disabled="true"]'); // using collapse could be undone by core, so we use display none // noPrefsElem.setAttribute("collapsed", "true"); noPrefsElem.style.display = "none"; event.target.ownerGlobal.document .getElementById(id) .addEventListener("command", this); } // Event handler for the addon manager, to update the state of the options button. handleEvent(e) { switch (e.type) { // 68 add-on options menu showing case "popupshowing": { this.add68PrefsEntry(e); } break; // 78/88 add-on options menu/button click 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; // 68 add-on options menu command case "command": { let WL = {}; WL.extension = this.extension; WL.messenger = this.getMessenger(this.context); e.target.ownerGlobal.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 //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) || (this.getThunderbirdVersion().major == 78 && this.getThunderbirdVersion().minor == 10 && this.getThunderbirdVersion().revision < 2); if (optionsMenu) { // Options menu in 78.0-78.10 and 79-87 let addonOptionsLegacyEntry = card.querySelector( ".extension-options-legacy" ); if (card.addon.isActive && !addonOptionsLegacyEntry) { let addonOptionsEntry = card.querySelector( "addon-options panel-list panel-item[action='preferences']" ); addonOptionsLegacyEntry = card.ownerDocument.createElement( "panel-item" ); addonOptionsLegacyEntry.setAttribute( "data-l10n-id", "preferences-addon-button" ); addonOptionsLegacyEntry.classList.add( "extension-options-legacy" ); addonOptionsEntry.parentNode.insertBefore( addonOptionsLegacyEntry, addonOptionsEntry ); card .querySelector(".extension-options-legacy") .addEventListener("click", this); } else if (!card.addon.isActive && addonOptionsLegacyEntry) { addonOptionsLegacyEntry.remove(); } } else { // Add-on button in 88 let addonOptionsButton = card.querySelector( ".windowlistener-options-button" ); if (card.addon.isActive && !addonOptionsButton) { 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 (!( managerWindow && managerWindow[this.uniqueRandomID] && managerWindow[this.uniqueRandomID].hasAddonManagerEventListeners )) { 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)); } }, }; 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) { 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) { // Create add-on scope window[self.uniqueRandomID] = {}; // Special action #1: If this is the main messenger window if ( window.location.href == "chrome://messenger/content/messenger.xul" || window.location.href == "chrome://messenger/content/messenger.xhtml" ) { if (self.pathToOptionsPage) { if (self.getThunderbirdVersion().major < 78) { let element_addonPrefs = window.document.getElementById( self.menu_addonPrefs_id ); element_addonPrefs.addEventListener( "popupshowing", 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; } } } // Special action #2: If this page contains browser elements let browserElements = window.document.getElementsByTagName( "browser" ); if (browserElements.length > 0) { //register a MutationObserver window[ self.uniqueRandomID ]._mObserver = new window.MutationObserver(function ( mutations ) { mutations.forEach(async function (mutation) { if ( mutation.attributeName == "src" && self.registeredWindows.hasOwnProperty( mutation.target.getAttribute("src") ) ) { // When the MutationObserver callsback, the window is still showing "about:black" and it is going // to unload and then load the new page. Any eventListener attached to the window will be removed // so we cannot listen for the load event. We have to poll manually to learn when loading has finished. // On my system it takes 70ms. let loaded = false; for (let i = 0; i < 100 && !loaded; i++) { await self.sleep(100); let targetWindow = mutation.target.contentWindow.wrappedJSObject; if ( targetWindow && targetWindow.location.href == mutation.target.getAttribute("src") && targetWindow.document.readyState == "complete" ) { loaded = true; break; } } if (loaded) { let targetWindow = mutation.target.contentWindow.wrappedJSObject; // Create add-on scope targetWindow[self.uniqueRandomID] = {}; // Inject with isAddonActivation = false self._loadIntoWindow(targetWindow, false); } } }); }); for (let element of browserElements) { if ( self.registeredWindows.hasOwnProperty( element.getAttribute("src") ) ) { let targetWindow = element.contentWindow.wrappedJSObject; // Create add-on scope targetWindow[self.uniqueRandomID] = {}; // Inject with isAddonActivation = true self._loadIntoWindow(targetWindow, true); } else { // Window/Browser is not yet fully loaded, postpone injection via MutationObserver window[self.uniqueRandomID]._mObserver.observe( element, { attributes: true, childList: false, characterData: false, } ); } } } // Load JS into window 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"); } }, }, }; } _loadIntoWindow(window, isAddonActivation) { if ( window.hasOwnProperty(this.uniqueRandomID) && this.registeredWindows.hasOwnProperty(window.location.href) ) { 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 if ( window.hasOwnProperty(this.uniqueRandomID) && window[this.uniqueRandomID].hasOwnProperty("_mObserver") ) { window[this.uniqueRandomID]._mObserver.disconnect(); let browserElements = window.document.getElementsByTagName("browser"); for (let element of browserElements) { if (element.contentWindow) { this._unloadFromWindow( element.contentWindow.wrappedJSObject, 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 ( this.pathToOptionsPage && (window.location.href == "chrome://messenger/content/messenger.xul" || window.location.href == "chrome://messenger/content/messenger.xhtml") ) { if (this.getThunderbirdVersion().major < 78) { let element_addonPrefs = window.document.getElementById( this.menu_addonPrefs_id ); element_addonPrefs.removeEventListener("popupshowing", this); // Remove our entry. let entry = window.document.getElementById( this.menu_addonPrefs_id + "_" + this.uniqueRandomID ); if (entry) entry.remove(); // Do we have to unhide the noPrefsElement? if (element_addonPrefs.children.length == 1) { let noPrefsElem = element_addonPrefs.querySelector( '[disabled="true"]' ); noPrefsElem.style.display = "inline"; } } else { // 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); let cards = this.getCards(managerWindow); if (this.getThunderbirdVersion().major < 88) { // Remove options menu in 78-87 for (let card of cards) { let addonOptionsLegacyEntry = card.querySelector( ".extension-options-legacy" ); if (addonOptionsLegacyEntry) addonOptionsLegacyEntry.remove(); } } else { // Remove options button in 88 for (let card of cards) { if (card.addon.id == this.extension.id) { 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; } } };allow-html-temp-6.3.7/api/WindowListener/schema.json000066400000000000000000000055621410443406400224430ustar00rootroot00000000000000[ { "namespace": "WindowListener", "functions": [ { "name": "registerDefaultPrefs", "type": "function", "parameters": [ { "name": "aPath", "type": "string", "description": "Relative path to the default file." } ] }, { "name": "registerOptionsPage", "type": "function", "parameters": [ { "name": "aPath", "type": "string", "description": "Path to the options page, which should be made accessible in the (legacy) Add-On Options menu." } ] }, { "name": "registerChromeUrl", "type": "function", "description": "Register folders which should be available as chrome:// urls (as defined in the legacy chrome.manifest)", "parameters": [ { "name": "data", "type": "array", "items": { "type": "array", "items" : { "type": "string" } }, "description": "Array of manifest url definitions (content, locale, resource)" } ] }, { "name": "waitForMasterPassword", "type": "function", "async": true, "parameters": [] }, { "name": "openOptionsDialog", "type": "function", "parameters": [ { "name": "windowId", "type": "integer", "description": "Id of the window the dialog should be opened from." } ] }, { "name": "startListening", "type": "function", "async": true, "parameters": [] }, { "name": "registerWindow", "type": "function", "parameters": [ { "name": "windowHref", "type": "string", "description": "Url of the window, which should be listen for." }, { "name": "jsFile", "type": "string", "description": "Path to the JavaScript file, which should be loaded into the window." } ] }, { "name": "registerStartupScript", "type": "function", "parameters": [ { "name": "aPath", "type": "string", "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." } ] }, { "name": "registerShutdownScript", "type": "function", "parameters": [ { "name": "aPath", "type": "string", "description": "Path to a JavaScript file, which should be executed on add-on shutdown." } ] } ] } ]allow-html-temp-6.3.7/background.js000066400000000000000000000047061410443406400172360ustar00rootroot00000000000000messenger.WindowListener.registerDefaultPrefs("defaults/preferences/allowhtmltemp.js"); messenger.WindowListener.registerChromeUrl([ ["content", "allowhtmltemp", "chrome/content/"], ["resource", "allowhtmltemp", "chrome/"], ["locale", "allowhtmltemp", "en", "chrome/locale/en/"], ["locale", "allowhtmltemp", "ar", "chrome/locale/ar/"], ["locale", "allowhtmltemp", "cs", "chrome/locale/cs/"], ["locale", "allowhtmltemp", "da", "chrome/locale/da/"], ["locale", "allowhtmltemp", "de", "chrome/locale/de/"], ["locale", "allowhtmltemp", "el", "chrome/locale/el/"], ["locale", "allowhtmltemp", "es", "chrome/locale/es/"], ["locale", "allowhtmltemp", "fr", "chrome/locale/fr/"], ["locale", "allowhtmltemp", "he", "chrome/locale/he/"], ["locale", "allowhtmltemp", "hr", "chrome/locale/hr/"], ["locale", "allowhtmltemp", "it", "chrome/locale/it/"], ["locale", "allowhtmltemp", "ja", "chrome/locale/ja/"], ["locale", "allowhtmltemp", "ms", "chrome/locale/ms/"], ["locale", "allowhtmltemp", "nl", "chrome/locale/nl/"], ["locale", "allowhtmltemp", "pl", "chrome/locale/pl/"], ["locale", "allowhtmltemp", "pt-BR", "chrome/locale/pt-BR/"], ["locale", "allowhtmltemp", "ru", "chrome/locale/ru/"], ["locale", "allowhtmltemp", "sk", "chrome/locale/sk/"], ["locale", "allowhtmltemp", "sr", "chrome/locale/sr/"], ["locale", "allowhtmltemp", "sv", "chrome/locale/sv/"], ["locale", "allowhtmltemp", "tr", "chrome/locale/tr/"], ["locale", "allowhtmltemp", "uk", "chrome/locale/uk/"], ["locale", "allowhtmltemp", "zh", "chrome/locale/zh/"], ["locale", "allowhtmltemp", "zh-TW", "chrome/locale/zh-TW/"] ]); messenger.WindowListener.registerOptionsPage("chrome://allowhtmltemp/content/options/aht_options.xhtml"); messenger.WindowListener.registerWindow( "chrome://messenger/content/customizeToolbar.xhtml", "chrome://allowhtmltemp/content/aht_overlay_customizeToolbar.js"); messenger.WindowListener.registerWindow( "chrome://messenger/content/messageWindow.xhtml", "chrome://allowhtmltemp/content/aht_overlay.js"); messenger.WindowListener.registerWindow( "chrome://messenger/content/messenger.xhtml", "chrome://allowhtmltemp/content/aht_overlay.js"); // messenger.WindowListener.registerShutdownScript("chrome://allowhtmltemp/content/shutdown.js") messenger.WindowListener.startListening(); allow-html-temp-6.3.7/chrome/000077500000000000000000000000001410443406400160275ustar00rootroot00000000000000allow-html-temp-6.3.7/chrome/content/000077500000000000000000000000001410443406400175015ustar00rootroot00000000000000allow-html-temp-6.3.7/chrome/content/aht_buttonsListeners.js000066400000000000000000000257001410443406400242660ustar00rootroot00000000000000var { Services } = ChromeUtils.import( "resource://gre/modules/Services.jsm" ); var { MsgHdrToMimeMessage } = ChromeUtils.import( "resource:///modules/gloda/MimeMessage.jsm" ); var ahtButtonSetIcon = { startup: function() { Services.prefs.addObserver("", this, false); this.setCurrentButtonIconLabel(); }, shutdown: function() { Services.prefs.removeObserver("", this); }, observe: function(subject, topic, data) { if (topic != "nsPref:changed") { return; } switch (data) { case "extensions.allowhtmltemp.ButtonFunction": this.setCurrentButtonIconLabel(); break; case "mailnews.message_display.disable_remote_image": this.setCurrentButtonIconLabel(); break; case "extensions.allowhtmltemp.ForceRemoteContent": this.setCurrentButtonIconLabel(); break; } }, setCurrentButtonIconLabel: function() { let aht_pref_buttonfunction = Services.prefs.getIntPref( "extensions.allowhtmltemp.ButtonFunction"); let aht_pref_app_remote = Services.prefs.getBoolPref( "mailnews.message_display.disable_remote_image"); let aht_pref_forceremote = Services.prefs.getBoolPref( "extensions.allowhtmltemp.ForceRemoteContent"); let ahtToolbarButton = document.getElementById("AllowHTMLtemp"); let ahtHdrButton = document.getElementById("hdrAHTButton"); if (ahtToolbarButton) { switch (aht_pref_buttonfunction) { case 0: if (!(aht_pref_app_remote) || (aht_pref_forceremote)) { ahtToolbarButton.setAttribute("AHT-htmlStatus", "Plus"); ahtToolbarButton.setAttribute("label", ahtToolbarButton .getAttribute("labelAHT-htmlStatusPlus")); } else { ahtToolbarButton.setAttribute("AHT-htmlStatus", "Original"); ahtToolbarButton.setAttribute("label", ahtToolbarButton .getAttribute("labelAHT-htmlStatusOriginal")); } break; case 1: ahtToolbarButton.setAttribute("AHT-htmlStatus", "Sanitized"); ahtToolbarButton.setAttribute("label", ahtToolbarButton .getAttribute("labelAHT-htmlStatusSanitized")); break; case 2: ahtToolbarButton.setAttribute("AHT-htmlStatus", "Plaintext"); ahtToolbarButton.setAttribute("label", ahtToolbarButton .getAttribute("labelAHT-htmlStatusPlaintext")); break; } } if (ahtHdrButton) { switch (aht_pref_buttonfunction) { case 0: if (!(aht_pref_app_remote) || (aht_pref_forceremote)) { ahtHdrButton.setAttribute("AHT-htmlStatus", "Plus"); ahtHdrButton.setAttribute("label", ahtHdrButton.getAttribute( "labelAHT-htmlStatusPlus")); } else { ahtHdrButton.setAttribute("AHT-htmlStatus", "Original"); ahtHdrButton.setAttribute("label", ahtHdrButton.getAttribute( "labelAHT-htmlStatusOriginal")); } break; case 1: ahtHdrButton.setAttribute("AHT-htmlStatus", "Sanitized"); ahtHdrButton.setAttribute("label", ahtHdrButton.getAttribute( "labelAHT-htmlStatusSanitized")); break; case 2: ahtHdrButton.setAttribute("AHT-htmlStatus", "Plaintext"); ahtHdrButton.setAttribute("label", ahtHdrButton.getAttribute( "labelAHT-htmlStatusPlaintext")); break; } } } } var ahtButtonStatus = { startup: function() { this.observerService = Components.classes[ "@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); this.observerService.addObserver(this, "mail:updateToolbarItems", false); this.checkMailForHtmlpart(); }, shutdown: function() { this.observerService.removeObserver(this, "mail:updateToolbarItems"); }, observe: function(subject, topic, data) { switch (topic) { case "mail:updateToolbarItems": ahtButtonStatus.checkMailForHtmlpart(); // ahtButtonStatus.resetRemoteContentPopupmenuItem(); ahtButtonSetIcon.setCurrentButtonIconLabel(); break; } }, enableButtons: function() { // console.log("AHT: enableButtons"); // we MUST use removeAttribute("disabled") // setAttribute to false leads to problems in tabbar-toolbar let ahtHdrButton = document.getElementById("hdrAHTButton"); if (ahtHdrButton) ahtHdrButton.removeAttribute("disabled"); let ahtToolbarButton = document.getElementById("AllowHTMLtemp"); if (ahtToolbarButton) ahtToolbarButton.removeAttribute("disabled"); let ahtKey = document.getElementById("ahtButton-key"); if (ahtKey) ahtKey.removeAttribute("disabled"); }, disableButtons: function() { // console.log("AHT: disableButtons"); let ahtHdrButton = document.getElementById("hdrAHTButton"); if (ahtHdrButton) ahtHdrButton.setAttribute("disabled", true); let ahtToolbarButton = document.getElementById("AllowHTMLtemp"); if (ahtToolbarButton) ahtToolbarButton.setAttribute("disabled", true); let ahtKey = document.getElementById("ahtButton-key"); if (ahtKey) ahtKey.setAttribute("disabled", true); }, changeRemoteContentPopupmenuItem: function() { // console.log("AHT: changeRemoteContentPopupmenuItem"); // Tb 31, 38, 45, 52, ... popupmenu item has ID "remoteContentOptionAllowForMsg" // hide original popupmenu item if (document.getElementById("remoteContentOptionAllowForMsg")) aht_originalRemoteContentItem = document.getElementById( "remoteContentOptionAllowForMsg"); if (aht_originalRemoteContentItem) aht_originalRemoteContentItem.setAttribute("hidden", true); // show the aht version of the popupmenu item if (document.getElementById("aht_remoteContentOptionAllowForMsg")) aht_tempRemoteContentItem = document.getElementById( "aht_remoteContentOptionAllowForMsg"); if (aht_tempRemoteContentItem) aht_tempRemoteContentItem.removeAttribute("hidden"); }, resetRemoteContentPopupmenuItem: function() { // console.log("AHT: resetRemoteContentPopupmenuItem"); // Tb 31, 38, 45, 52, ... popupmenu item has ID "remoteContentOptionAllowForMsg" // show original popupmenu item if (document.getElementById("remoteContentOptionAllowForMsg")) aht_originalRemoteContentItem = document.getElementById( "remoteContentOptionAllowForMsg"); if (aht_originalRemoteContentItem) aht_originalRemoteContentItem.removeAttribute("hidden"); // hide the aht version of the popupmenu item if (document.getElementById("aht_remoteContentOptionAllowForMsg")) aht_tempRemoteContentItem = document.getElementById( "aht_remoteContentOptionAllowForMsg"); if (aht_tempRemoteContentItem) aht_tempRemoteContentItem.setAttribute("hidden", true); }, checkMailForHtmlpart: function() { // console.log("AHT: run checkMailForHtmlpart ----------------"); try { if (gFolderDisplay.selectedCount != 1) { // console.log("AHT: selectedCount != 1"); ahtButtonStatus.disableButtons(); } else { // get the msg header (to ask for junk status and 'Body: text/html') let ahtMsgHdr = gFolderDisplay.selectedMessage; // ask for selected messages junk status let ahtMsgJunkScore = ahtMsgHdr.getStringProperty("junkscore"); let ahtMsgIsJunk = (ahtMsgJunkScore == Components.interfaces.nsIJunkMailPlugin.IS_SPAM_SCORE); // if msg is junk disable the ahtButtons if (ahtMsgIsJunk) { // console.log("AHT: message is Junk"); ahtButtonStatus.disableButtons(); } else if (gFolderDisplay.selectedMessageIsNews) { // console.log("AHT: message is News"); ahtButtonStatus.disableButtons(); } else if (gFolderDisplay.selectedMessageIsFeed) { // console.log("AHT: message is Feed"); ahtButtonStatus.enableButtons(); } else { // First check MsgHdr without decrypting to prevent an additional passphrase dialog in case of PGP/MIME MsgHdrToMimeMessage(ahtMsgHdr, null, function(aMsgHdr, aMimeMsg) { // multipart/encrypted enables the button for encrypted PGP/MIME messages // in this case we don't check for HTML, because the check seems not to be possible for PGP/MIME if (aMimeMsg.prettyString().search("multipart/encrypted") != -1) { // console.log("AHT: message is PGP/MIME multipart/encrypted"); ahtButtonStatus.enableButtons(); } else { // search for 'Body: text/html' in MIME parts, // it seems this is only working if messages are downloaded for offline reading? MsgHdrToMimeMessage(ahtMsgHdr, null, function(aMsgHdr, aMimeMsg) { // console.log("AHT: Check for html part ----------------"); // console.log("AHT: Body: text/html " + aMimeMsg.prettyString().search("Body: text/html")); // console.log("AHT: text/html " + aMimeMsg.prettyString().search("text/html")); // console.log("AHT: Body: plain/html " + aMimeMsg.prettyString().search("Body: plain/html")); // console.log("AHT: plain/html " + aMimeMsg.prettyString().search("plain/html")); // console.log("AHT: multipart/alternative " + aMimeMsg.prettyString().search("multipart/alternative")); // console.log("AHT: multipart/signed " + aMimeMsg.prettyString().search("multipart/signed")); // console.log("AHT: multipart/encrypted " + aMimeMsg.prettyString().search("multipart/encrypted")); // 'Body: text/html' is found, enable ahtButtons if (aMimeMsg.prettyString().search("Body: text/html") != -1) { // console.log("AHT: message contains HTML body part"); ahtButtonStatus.enableButtons(); } // no 'Body: text/html', disable ahtButtons else { ahtButtonStatus.disableButtons(); } }, true, { examineEncryptedParts: true }); // examineEncryptedParts=true is necessary for encrypted S/MIME messages } }, true, { examineEncryptedParts: false }); // examineEncryptedParts=false to prevent an additional passphrase dialog in case of PGP/MIME } } } catch (e) { // console.log("AHT: catch error in checkMailForHtmlpart"); ahtButtonStatus.disableButtons(); } } } /* eventListeners are now called from WindowListener API * window.addEventListener("load", function(e) { ahtButtonSetIcon.startup(); }, false); window.addEventListener("unload", function(e) { ahtButtonSetIcon.shutdown(); }, false); window.addEventListener("load", function(e) { ahtButtonStatus.startup(); }, false); window.addEventListener("unload", function(e) { ahtButtonStatus.shutdown(); }, false); **********************************************************/ allow-html-temp-6.3.7/chrome/content/aht_functions.js000066400000000000000000000357711410443406400227200ustar00rootroot00000000000000var { Services } = ChromeUtils.import( "resource://gre/modules/Services.jsm" ); var ahtFunctions = { // 4 variables for the original settings // html and remote content settings prefer_plaintext: false, html_as: 0, disallow_classes: 0, // inline attachment setting mail_inline_attachments: false, block: false, startup: function() { // console.log("AHT: startup"); this.observerService = Components.classes[ "@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); this.observerService.addObserver(this, "MsgMsgDisplayed", false); // if not existing, set addons default settings try { if (Services.prefs.getIntPref( "extensions.allowhtmltemp.ButtonFunction")) { // console.log("AHT: default setting ButtonFunction existing:"); // console.log(Services.prefs.getIntPref("extensions.allowhtmltemp.ButtonFunction")); } } catch (e) { // console.log("AHT: default setting ButtonFunction doesn't exist"); Services.prefs.setIntPref( "extensions.allowhtmltemp.ButtonFunction", 0) } try { if (Services.prefs.getBoolPref( "extensions.allowhtmltemp.ForceRemoteContent")) { // console.log("AHT: default setting ForceRemoteContent existing and true"); } else { // console.log("AHT: default setting ForceRemoteContent existing but false"); } } catch (e) { // console.log("AHT: default setting ForceRemoteContent doesn't exist"); Services.prefs.setBoolPref( "extensions.allowhtmltemp.ForceRemoteContent", false) } try { if (Services.prefs.getBoolPref( "extensions.allowhtmltemp.InlineAttachmentsTemp")) { // console.log("AHT: default setting InlineAttachmentsTemp existing and true"); } else { // console.log("AHT: default setting InlineAttachmentsTemp existing but false"); } } catch (e) { // console.log("AHT: default setting InlineAttachmentsTemp doesn't exist"); Services.prefs.setBoolPref( "extensions.allowhtmltemp.InlineAttachmentsTemp", false) } }, shutdown: function() { this.observerService.removeObserver(this, "MsgMsgDisplayed", false); }, observe: function(subject, topic, data) { switch (topic) { case "MsgMsgDisplayed": // console.log("AHT: MsgMsgDisplayed"); if (this.block) { // Revert to the users default settings // after the message is reloaded. this.RestoreHTMLcontentPrefs(); } break; } }, AllowHTMLtemp: function(ahtKeyboardEvent, ahtTriggeredBy) { // console.log("AHT: fired"); // console.log("AHT: ahtKeyboardEvent: " + ahtKeyboardEvent); // console.log("AHT: ahtTriggeredBy: " + ahtTriggeredBy); // ahtButtons should be disabled, if no msg or multiple msgs are selected, // but sometimes we nevertheless could land here. // So we check if only one email is selected, otherwise we do nothing. if (gFolderDisplay.selectedCount == 1) { // Save users applications default settings this.SaveHTMLcontentPrefs(); // Get users pref for the buttons function // 0 = Original HTML; 1 = Sanitized HTML; 2 = Plaintext let ahtPrefButtonFunction = Services.prefs.getIntPref( "extensions.allowhtmltemp.ButtonFunction"); // Get users pref to force remote content // true = force; false = no remote content let ahtPrefForceRemoteContent = Services.prefs.getBoolPref( "extensions.allowhtmltemp.ForceRemoteContent"); // RemoteContent popupmenu item clicked in remote content bar in a HTML message if (ahtTriggeredBy == "remoteButton") { this.ShowRemote(); } // Keyboard shortcut invokes the same function as a simple click on the addon button else if (ahtTriggeredBy == "keyboard") { switch (ahtPrefButtonFunction) { case 0: if (ahtPrefForceRemoteContent) { this.ShowRemote(); } else { this.ShowOriginalHTML(); } break; case 1: this.ShowSanitizedHTML(); break; case 2: this.ShowPlaintext(); break; } } // If we land here, the trigger must be a click on the addon button. // We must now differ the choosen function by modifier key (ahtKeyboardEvent). // Addon button clicked + both CTRL and SHIFT key else if (ahtKeyboardEvent.shiftKey && (ahtKeyboardEvent.ctrlKey || ahtKeyboardEvent.metaKey)) { this.ShowSanitizedHTML(); } // Addon button clicked + only CTRL key else if ((ahtKeyboardEvent.ctrlKey || ahtKeyboardEvent.metaKey) && !( ahtKeyboardEvent.shiftKey)) { this.ShowRemote(); } // Addon button clicked + only SHIFT key else if ((ahtKeyboardEvent.shiftKey) && !(ahtKeyboardEvent.ctrlKey || ahtKeyboardEvent.metaKey)) { this.ShowPlaintext(); } // Addon button clicked - no key pressed else if (!(ahtKeyboardEvent.ctrlKey || ahtKeyboardEvent.metaKey || ahtKeyboardEvent.shiftKey)) { switch (ahtPrefButtonFunction) { case 0: if (ahtPrefForceRemoteContent) { this.ShowRemote(); } else { this.ShowOriginalHTML(); } break; case 1: this.ShowSanitizedHTML(); break; case 2: this.ShowPlaintext(); break; } } } }, ShowPlaintext: function() { // console.log("AHT: ShowPlaintext"); try { // reload message in plaintext: MsgBodyAsPlaintext(); } catch (e) { // console.log("AHT: Plaintext error"); } }, ShowSanitizedHTML: function() { // console.log("AHT: ShowSanitizedHTML"); try { // reload message in sanitized HTML: MsgBodySanitized(); } catch (e) { // console.log("AHT: ShowSanitizedHTML error"); } }, ShowOriginalHTML: function() { // console.log("AHT: ShowOriginalHTML"); try { // enable temporarily InlineAttachments if temp option is set if (Services.prefs.getBoolPref( "extensions.allowhtmltemp.InlineAttachmentsTemp") == true) Services.prefs.setBoolPref("mail.inline_attachments", true); // reload message with original HTML: MsgBodyAllowHTML(); // show own RemoteContentPopupmenuItem to allow HTML again in case of event ahtButtonStatus.changeRemoteContentPopupmenuItem(); } catch (e) { // console.log("AHT: ShowOriginalHTML error"); } }, ShowRemote: function() { // console.log("AHT: ShowRemote"); try { // enable temporarily HTML Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false); Services.prefs.setIntPref("mailnews.display.html_as", 0); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", 0); // enable temporarily InlineAttachments if temp option is set if (Services.prefs.getBoolPref( "extensions.allowhtmltemp.InlineAttachmentsTemp") == true) Services.prefs.setBoolPref("mail.inline_attachments", true); // now HTML is allowed, so we can reload the message with remote content: LoadMsgWithRemoteContent(); } catch (e) { // console.log("AHT: ShowRemote error"); } }, SaveHTMLcontentPrefs: function() { // console.log("AHT: SaveHTMLcontentPrefs"); if (!this.block) // we need this block to prevent from // starting AHT again before the return // to the original settings! Otherwise we would loose // original settings -> 'Security leak'! { this.block = true; this.prefer_plaintext = Services.prefs.getBoolPref( "mailnews.display.prefer_plaintext"); this.html_as = Services.prefs.getIntPref("mailnews.display.html_as"); this.disallow_classes = Services.prefs.getIntPref( "mailnews.display.disallow_mime_handlers"); this.mail_inline_attachments = Services.prefs.getBoolPref( "mail.inline_attachments"); } }, RestoreHTMLcontentPrefs: function() { // console.log("AHT: RestoreHTMLcontentPrefs"); if (this.block) { Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", this .prefer_plaintext); Services.prefs.setIntPref("mailnews.display.html_as", this.html_as); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", this.disallow_classes); Services.prefs.setBoolPref("mail.inline_attachments", this .mail_inline_attachments); this.block = false; } }, InitPrefs: function() { // console.log("AHT: InitPrefs"); let html_as = Services.prefs.getIntPref("mailnews.display.html_as"); let prefer_plaintext = Services.prefs.getBoolPref( "mailnews.display.prefer_plaintext"); let disallow_classes = Services.prefs.getIntPref( "mailnews.display.disallow_mime_handlers"); const menuIDs = ["ahtAppHtmlBodyAllowHTML", "ahtAppHtmlBodySanitized", "ahtAppHtmlBodyAsPlaintext", "ahtAppHtmlBodyAllParts" ]; if (disallow_classes > 0) gDisallow_classes_no_html = disallow_classes; // else gDisallow_classes_no_html keeps its inital value let HtmlBody_Radiogroup = document.getElementById( "ahtAppHtmlRadiogroup"); let AllowHTML_menuitem = document.getElementById(menuIDs[0]); let Sanitized_menuitem = document.getElementById(menuIDs[1]); let AsPlaintext_menuitem = document.getElementById(menuIDs[2]); let AllBodyParts_menuitem = menuIDs[3] ? document.getElementById( menuIDs[3]) : null; document.getElementById("ahtAppHtmlBodyAllParts").hidden = !Services .prefs.getBoolPref("mailnews.display.show_all_body_parts_menu"); if (!prefer_plaintext && !html_as && !disallow_classes && AllowHTML_menuitem && HtmlBody_Radiogroup) HtmlBody_Radiogroup.selectedIndex = 0; else if (!prefer_plaintext && html_as == 3 && disallow_classes > 0 && Sanitized_menuitem && HtmlBody_Radiogroup) HtmlBody_Radiogroup.selectedIndex = 1; else if (prefer_plaintext && html_as == 1 && disallow_classes > 0 && AsPlaintext_menuitem && HtmlBody_Radiogroup) HtmlBody_Radiogroup.selectedIndex = 2; else if (!prefer_plaintext && html_as == 4 && !disallow_classes && AllBodyParts_menuitem && HtmlBody_Radiogroup) HtmlBody_Radiogroup.selectedIndex = 3; // else (the user edited prefs/user.js) select none of the radio items document.getElementById("ahtForceRemoteContentPrefCheckbox") .disabled = !Services.prefs.getBoolPref( "mailnews.message_display.disable_remote_image"); document.getElementById("ahtInlineAttachmentsTempPrefCheckbox") .disabled = Services.prefs.getBoolPref("mail.inline_attachments"); }, RefreshPrefsOption: function() { document.getElementById("ahtForceRemoteContentPrefCheckbox").disabled = (document.getElementById("ahtRemoteContentPrefCheckbox").checked == true); document.getElementById("ahtInlineAttachmentsTempPrefCheckbox") .disabled = (document.getElementById("ahtInlineAttachmentsPrefCheckbox") .checked == true); }, AhtSetMsgBodyAllowHTML: function() { Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false); Services.prefs.setIntPref("mailnews.display.html_as", 0); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", 0); }, AhtSetMsgBodySanitized: function() { Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false); Services.prefs.setIntPref("mailnews.display.html_as", 3); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", gDisallow_classes_no_html); }, AhtSetMsgBodyAsPlaintext: function() { Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", true); Services.prefs.setIntPref("mailnews.display.html_as", 1); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", gDisallow_classes_no_html); }, AhtSetMsgBodyAllParts: function() { Services.prefs.setBoolPref("mailnews.display.prefer_plaintext", false); Services.prefs.setIntPref("mailnews.display.html_as", 4); Services.prefs.setIntPref("mailnews.display.disallow_mime_handlers", 0); }, // The following function re-enables JavaScript and shows a prompt to inform the user about this. // This will be only done once. // If the user changes the pref in future again by about:config, it will not be reset again. ahtResetJavaScriptToDefaultOnce: function() { // console.log("AHT: ahtResetJavaScriptToDefaultOnce"); // Reset javascript.enabled to true (default) only once try { if (!Services.prefs.getBoolPref( "extensions.allowhtmltemp.reset_javascript_default_done_once")) { // console.log("AHT: JS Reset pref is false or not yet existing"); // Remove the AHT temp option for JavaScript if (!Services.prefs.getBoolPref( "extensions.allowhtmltemp.JavaScriptTemp")) { // console.log("AHT: default setting JavaScriptTemp is false or not existing"); } else { // console.log("AHT: default setting JavaScriptTemp existing and true - will be set to false"); Services.prefs.setBoolPref( "extensions.allowhtmltemp.JavaScriptTemp", false); } // Re-enable JavaScript default pref if (!Services.prefs.getBoolPref( "javascript.enabled")) { // console.log("AHT: javascript.enabled is false - will be reset to true"); Services.prefs.setBoolPref( "javascript.enabled", true); // get title and message for the prompt var aht_bundle = Services.strings.createBundle("chrome://allowhtmltemp/locale/allowhtmltemp.properties"); // get a reference to the prompt service component. var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); // show an alert. For the first argument, supply the parent window. The second // argument is the dialog title and the third argument is the message // to display. promptService.alert(window, aht_bundle.GetStringFromName("reset_javascript_to_default.title"), aht_bundle.GetStringFromName("reset_javascript_to_default.message")); // Indicate the one time reset by a pref, to prevent future run Services.prefs.setBoolPref( "extensions.allowhtmltemp.reset_javascript_default_done_once", true); } else { // console.log("AHT: javascript.enabled is true (default)"); } } else { // console.log("AHT: JS Reset done in the past - pref is true"); } } catch (e) { } } } /* eventListeners are now called from WindowListener API * window.addEventListener("load", function(e) { ahtFunctions.startup(); }, false); window.addEventListener("unload", function(e) { ahtFunctions.shutdown(); }, false); **********************************************************/ allow-html-temp-6.3.7/chrome/content/aht_overlay.js000066400000000000000000000137231410443406400223620ustar00rootroot00000000000000// Import any needed modules. var { Services } = ChromeUtils.import( "resource://gre/modules/Services.jsm" ); // Load an additional JavaScript file. Services.scriptloader.loadSubScript("chrome://allowhtmltemp/content/aht_functions.js", window, "UTF-8"); Services.scriptloader.loadSubScript("chrome://allowhtmltemp/content/aht_buttonsListeners.js", window, "UTF-8"); Services.scriptloader.loadSubScript("chrome://allowhtmltemp/content/aht_statusbarListeners.js", window, "UTF-8"); Services.scriptloader.loadSubScript("chrome://messenger/content/mailWindowOverlay.js", window, "UTF-8"); function onLoad(activatedWhileWindowOpen) { WL.injectCSS("resource://allowhtmltemp/skin/aht_buttons.css"); WL.injectCSS("resource://allowhtmltemp/skin/aht_statusbar.css"); WL.injectElements(` `, ["chrome://allowhtmltemp/locale/allowhtmltemp.dtd","chrome://messenger/locale/messenger.dtd"]); window.ahtFunctions.ahtResetJavaScriptToDefaultOnce(); window.ahtFunctions.startup(); window.ahtStatusbarSetLabelIcon.startup(); window.ahtHideAndShowStatusbarElements.startup(); window.ahtButtonSetIcon.startup(); window.ahtButtonStatus.startup(); } function onUnload(deactivatedWhileWindowOpen) { window.ahtFunctions.shutdown(); window.ahtStatusbarSetLabelIcon.shutdown(); window.ahtHideAndShowStatusbarElements.shutdown(); window.ahtButtonSetIcon.shutdown(); window.ahtButtonStatus.shutdown(); } allow-html-temp-6.3.7/chrome/content/aht_overlay_customizeToolbar.js000066400000000000000000000004001410443406400257730ustar00rootroot00000000000000function onLoad(activatedWhileWindowOpen) { WL.injectCSS("resource://allowhtmltemp/skin/aht_buttons_custToolbar.css"); WL.injectElements(``, ["chrome://allowhtmltemp/locale/allowhtmltemp.dtd"]); } function onUnload(deactivatedWhileWindowOpen) { } allow-html-temp-6.3.7/chrome/content/aht_statusbarListeners.js000066400000000000000000000142261410443406400246010ustar00rootroot00000000000000var { Services } = ChromeUtils.import( "resource://gre/modules/Services.jsm" ); var ahtStatusbarSetLabelIcon = { startup: function() { Services.prefs.addObserver("", this, false); this.setCurrentButtonIconLabel(false); this.setCurrentButtonIconLabel(true); }, shutdown: function() { Services.prefs.removeObserver("", this); }, observe: function(subject, topic, data) { if (topic != "nsPref:changed") { return; } switch (data) { case "mailnews.display.prefer_plaintext": this.setCurrentButtonIconLabel(false); break; case "mailnews.display.html_as": this.setCurrentButtonIconLabel(false); break; case "mailnews.display.disallow_mime_handlers": this.setCurrentButtonIconLabel(false); break; case "rss.display.prefer_plaintext": this.setCurrentButtonIconLabel(true); break; case "rss.display.html_as": this.setCurrentButtonIconLabel(true); break; case "rss.display.disallow_mime_handlers": this.setCurrentButtonIconLabel(true); break; case "rss.show.summary": this.setCurrentButtonIconLabel(true); break; } }, setCurrentButtonIconLabel: function(isFeedOption) { if (!isFeedOption) { let prefer_plaintext = false; let html_as = 0; let disallow_classes = 0; prefer_plaintext = Services.prefs.getBoolPref( "mailnews.display.prefer_plaintext"); html_as = Services.prefs.getIntPref("mailnews.display.html_as"); disallow_classes = Services.prefs.getIntPref( "mailnews.display.disallow_mime_handlers"); let ahtStatusbarpanelText = document.getElementById( "AHT-statusbarpanel-text"); let ahtStatusbarpanelIcon = document.getElementById( "AHT-statusbarpanel-icon"); if (ahtStatusbarpanelText && ahtStatusbarpanelIcon) { if (!prefer_plaintext && !html_as && !disallow_classes) { ahtStatusbarpanelText.setAttribute("value", ahtStatusbarpanelText .getAttribute("labelAHT-htmlStatusOriginal")); ahtStatusbarpanelIcon.setAttribute("AHT-htmlStatus", "Original"); } else if (!prefer_plaintext && html_as == 3 && disallow_classes > 0) { ahtStatusbarpanelText.setAttribute("value", ahtStatusbarpanelText .getAttribute("labelAHT-htmlStatusSanitized")); ahtStatusbarpanelIcon.setAttribute("AHT-htmlStatus", "Sanitized"); } else if (prefer_plaintext && html_as == 1 && disallow_classes > 0) { ahtStatusbarpanelText.setAttribute("value", ahtStatusbarpanelText .getAttribute("labelAHT-htmlStatusPlaintext")); ahtStatusbarpanelIcon.setAttribute("AHT-htmlStatus", "Plaintext"); } } } else { let feed_summary = 0; feed_summary = Services.prefs.getIntPref("rss.show.summary"); let ahtFeedStatusbarpanelText = document.getElementById( "AHT-feed-statusbarpanel-text"); if (ahtFeedStatusbarpanelText) { switch (feed_summary) { case 0: ahtFeedStatusbarpanelText.setAttribute("value", ahtFeedStatusbarpanelText.getAttribute( "labelAHT-viewFeedWebPage")); break; case 1: ahtFeedStatusbarpanelText.setAttribute("value", ahtFeedStatusbarpanelText.getAttribute( "labelAHT-viewFeedSummary")); break; case 2: ahtFeedStatusbarpanelText.setAttribute("value", ahtFeedStatusbarpanelText.getAttribute( "labelAHT-viewFeedSummaryFeedPropsPref")); break; } } } } } var ahtHideAndShowStatusbarElements = { startup: function() { this.observerService = Components.classes[ "@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); this.observerService.addObserver(this, "mail:updateToolbarItems", false); this.observerService.addObserver(this, "MsgMsgDisplayed", false); this.showCurrentStatusbarElement(); }, shutdown: function() { this.observerService.removeObserver(this, "mail:updateToolbarItems"); this.observerService.removeObserver(this, "MsgMsgDisplayed"); }, observe: function(subject, topic, data) { switch (topic) { case "MsgMsgDisplayed": this.showCurrentStatusbarElement(); break; case "mail:updateToolbarItems": this.showCurrentStatusbarElement(); break; } }, showCurrentStatusbarElement: function() { let ahtStatusbarMessage = document.getElementById("AHT-statusbarpanel"); let ahtStatusbarFeed = document.getElementById("AHT-feed-statusbarpanel"); try { // call InitViewBodyMenu() to set the checked attribute in the popupmenu InitViewBodyMenu(); if (gFolderDisplay && (FeedMessageHandler.isFeedFolder(gFolderDisplay.displayedFolder) || gFolderDisplay.selectedMessageIsFeed)) { // if feed mode = website, then disable the AHT html mode statusbar element to prevent user confusion let feed_summary = 0; feed_summary = Services.prefs.getIntPref("rss.show.summary"); if (feed_summary == 0) { ahtStatusbarMessage.setAttribute("hidden", true); } else { ahtStatusbarMessage.removeAttribute("hidden"); } ahtStatusbarFeed.removeAttribute("hidden"); } else { ahtStatusbarMessage.removeAttribute("hidden"); ahtStatusbarFeed.setAttribute("hidden", true); } } catch (e) { ahtStatusbarMessage.setAttribute("hidden", true); ahtStatusbarFeed.setAttribute("hidden", true); } } } /* eventListeners are now called from WindowListener API * window.addEventListener("load", function(e) { ahtStatusbarSetLabelIcon.startup(); }, false); window.addEventListener("unload", function(e) { ahtStatusbarSetLabelIcon.shutdown(); }, false); window.addEventListener("load", function(e) { ahtHideAndShowStatusbarElements.startup(); }, false); window.addEventListener("unload", function(e) { ahtHideAndShowStatusbarElements.shutdown(); }, false); **********************************************************/ allow-html-temp-6.3.7/chrome/content/options/000077500000000000000000000000001410443406400211745ustar00rootroot00000000000000allow-html-temp-6.3.7/chrome/content/options/aht_options.css000066400000000000000000000007361410443406400242430ustar00rootroot00000000000000/* * Don't display the lightweight-theme images * because of overlaying the whole dialog. * On the other hand we need the lightweight=true * to have the dark and light mode ability */ #allowhtmltempPrefs:not([lwt-tree]):-moz-lwtheme { background-image: none !important; } #allowhtmltempPrefs:not([lwt-tree]):-moz-lwtheme-darktext { background: -moz-Dialog !important; color: -moz-DialogText; text-shadow: none; } legend:-moz-lwtheme { background-color: unset; } allow-html-temp-6.3.7/chrome/content/options/aht_options.xhtml000066400000000000000000000125301410443406400246020ustar00rootroot00000000000000 %messengerDTD; %AllowHTMLtempPrefsDTD; ]> &allowhtmltemp.pref.text1; &allowhtmltemp.pref.text2; &allowhtmltemp.pref.HeaderHtml.label; (&bodyMenu.label;) &allowhtmltemp.pref.AppHtml.label; &allowhtmltemp.pref.ButtonHtml.label; &allowhtmltemp.pref.text3; &allowhtmltemp.pref.text4; &allowhtmltemp.pref.text5; &allowhtmltemp.pref.HeaderRemoteContent.label; &allowhtmltemp.pref.HeaderAttachments.label;