From 6c5c265466b7ca7fcc89620e547d83f3d6fb4599 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 18:09:25 +0200 Subject: [PATCH 1/7] Fix return code of runtime.onMessage event listener Such handlers should either return a boolean or a Promise. Use the an immediately resolved Promise in case of the synchronously handled clipboardWrite message. Return false if no message was handled. Reported by John Bieling. Signed-off-by: Jan Kiszka --- background-script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/background-script.js b/background-script.js index 3951a9f..894b04b 100644 --- a/background-script.js +++ b/background-script.js @@ -40,7 +40,9 @@ function main() messenger.messageDisplayAction.setBadgeText( {tabId: sender.tab.id, text: null}); }, 500); + return Promise.resolve(); } + return false; }); messenger.messageDisplayAction.onClicked.addListener(tab => { From 26f6addd2c8df093c6f5c1908bd00ae71c53509a Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 11:32:57 +0200 Subject: [PATCH 2/7] Switch to background page Allows to load ES6 modules from the script which we will use soon. Signed-off-by: Jan Kiszka --- Makefile | 1 + background.html | 7 +++++++ manifest.json | 4 +--- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 background.html diff --git a/Makefile b/Makefile index 6496d73..d63a8d8 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ ARCHIVE_NAME=$(PACKAGE_NAME)-$(RELEASE_TAG).xpi PACKAGE_FILES= \ manifest.json \ copypatch*.png \ + background.html \ background-script.js \ content-script.js \ api/CopyPatch/ \ diff --git a/background.html b/background.html new file mode 100644 index 0000000..50e5110 --- /dev/null +++ b/background.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/manifest.json b/manifest.json index 125d1b1..06b7539 100644 --- a/manifest.json +++ b/manifest.json @@ -17,9 +17,7 @@ "64": "copypatch64.png" }, "background": { - "scripts": [ - "background-script.js" - ] + "page": "background.html" }, "permissions": [ "messagesRead", From 8eac833ef27a9cf32ab0917b93cafc3662fa864b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 11:47:27 +0200 Subject: [PATCH 3/7] Use tab parameter of onCommand callback This was introduced in TB 106 and backported to 102.4 (actually 102.3.3, but that is not a legal for struct_min_version). Using this allows to drop the open-coded tab lookup. Based on changes suggested by John Bieling. Signed-off-by: Jan Kiszka --- background-script.js | 23 +++-------------------- manifest.json | 2 +- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/background-script.js b/background-script.js index 894b04b..f30a33d 100644 --- a/background-script.js +++ b/background-script.js @@ -11,18 +11,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -async function getCurrentWindow() -{ - let windows = await messenger.windows.getAll(); - - for (let window of windows) { - if ((window.type === "messageDisplay" || window.type === "normal") - && window.focused === true) - return window; - } - return null; -} - function main() { messenger.runtime.onMessage.addListener((request, sender, sendResponse) => { @@ -49,18 +37,13 @@ function main() messenger.tabs.executeScript(tab.id, {file: "content-script.js"}); }); - messenger.commands.onCommand.addListener(async (name) => { + messenger.commands.onCommand.addListener(async (name, tab) => { if (name !== "copyPatch") { return; } - let window = await getCurrentWindow(); - if (window) { - let tabs = await messenger.tabs.query({windowId: window.id}); - if (await messenger.messageDisplayAction.isEnabled({tabId: tabs[0].id})) { - messenger.tabs.executeScript(tabs[0].id, - {file: "content-script.js"}); - } + if (await messenger.messageDisplayAction.isEnabled({tabId: tab.id})) { + messenger.tabs.executeScript(tab.id, {file: "content-script.js"}); } }); diff --git a/manifest.json b/manifest.json index 06b7539..3ee430e 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "applications": { "gecko": { "id": "copypatch@kiszka.org", - "strict_min_version": "78.4.0", + "strict_min_version": "102.4.0", "strict_max_version": "102.*" } }, From 1765426fcf1a1da3c4593a3fe8d7eacda6c00132 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 12:53:40 +0200 Subject: [PATCH 4/7] Add email-addresses JS module via npm This will be used by the upcoming migration to an Experiment-free message processing. The modules needs one patch right now: "this" is undefined at top-level when a script is loaded as module, see [1]. Upstream PR will be sent. [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this Signed-off-by: Jan Kiszka --- .gitignore | 2 ++ Makefile | 13 +++++++++++-- package.json | 12 ++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 package.json diff --git a/.gitignore b/.gitignore index e9f5d9a..40211b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ copypatch*.xpi +node_modules +package-lock.json diff --git a/Makefile b/Makefile index d63a8d8..510d3a5 100644 --- a/Makefile +++ b/Makefile @@ -29,13 +29,22 @@ PACKAGE_FILES= \ background-script.js \ content-script.js \ api/CopyPatch/ \ + node_modules/email-addresses/LICENSE \ + node_modules/email-addresses/lib/email-addresses.js \ COPYING UPDATE_VERSION='s|"version":.*|"version": "$(VERSION)",|' -all package: clean $(PACKAGE_FILES) +all package: clean node_modules $(PACKAGE_FILES) zip -r $(ARCHIVE_NAME) $(PACKAGE_FILES) +node_modules: package.json + npm install + sed -i 's/(this)/(globalThis)/' node_modules/email-addresses/lib/email-addresses.js + +distclean: clean + rm -rf node_modules + clean: rm -f $(ARCHIVE_NAME) @@ -52,4 +61,4 @@ release: git commit -s manifest.json -m "Bump version number" git tag -as $(VERSION) -m "Release $(VERSION)" -.PHONY: clean release +.PHONY: clean distclean release diff --git a/package.json b/package.json new file mode 100644 index 0000000..4110079 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "copypatch", + "description": "Copy email content to clipboard for application as patch", + "repository": { + "type": "git", + "url": "https://github.com/jan-kiszka/copypatch.git" + }, + "license": "MPL-2.0", + "dependencies": { + "email-addresses": "^5.0.0" + } +} From 2ffb6398ad4803679bf699c699715674738ee1f8 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 13:08:44 +0200 Subject: [PATCH 5/7] Convert from Experiment API to pure WebExtension Use browser.messages.getFull() to obtain the selected message and then parse the message, also with the help of the newly added email-addresses module. The interface between content and background scripts remain the same. Based on original conversion by John Bieling. Signed-off-by: Jan Kiszka --- Makefile | 1 - api/CopyPatch/implementation.js | 93 --------------------------------- api/CopyPatch/schema.json | 18 ------- background-script.js | 87 ++++++++++++++++++++++++++++-- manifest.json | 16 ------ 5 files changed, 82 insertions(+), 133 deletions(-) delete mode 100644 api/CopyPatch/implementation.js delete mode 100644 api/CopyPatch/schema.json diff --git a/Makefile b/Makefile index 510d3a5..fdc2580 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,6 @@ PACKAGE_FILES= \ background.html \ background-script.js \ content-script.js \ - api/CopyPatch/ \ node_modules/email-addresses/LICENSE \ node_modules/email-addresses/lib/email-addresses.js \ COPYING diff --git a/api/CopyPatch/implementation.js b/api/CopyPatch/implementation.js deleted file mode 100644 index 3c5cc9b..0000000 --- a/api/CopyPatch/implementation.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copy Patch Thunderbird Add-On - * - * Copyright (c) Jan Kiszka, 2019-2020 - * - * Authors: - * Jan Kiszka - * - * 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/. - */ - -const { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm"); -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { jsmime } = ChromeUtils.import("resource:///modules/jsmime.jsm"); - -function do_getSelectedMessage(windowId) -{ - let win = Services.wm.getOuterWindowWithId(windowId); - - if (win.GetNumSelectedMessages() === 0) { - return null; - } - - let selectedMsg = win.gFolderDisplay.selectedMessage; - let msgURI = selectedMsg.folder.getUriForMsg(selectedMsg); - - let msgStream = Components.classes["@mozilla.org/network/sync-stream-listener;1"].createInstance(); - let consumer = msgStream.QueryInterface(Components.interfaces.nsIInputStream); - let scriptInput = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(); - let scriptInputStream = scriptInput.QueryInterface(Components.interfaces.nsIScriptableInputStream); - scriptInputStream.init(consumer); - - let service = win.messenger.messageServiceFromURI(msgURI); - service.streamMessage(msgURI, msgStream, win.msgWindow, null, false, null); - - let msgHeader; - let msgBody = ""; - let done = false; - let emitter = { - startPart: function(partNum, header) - { - if (!msgHeader) { - msgHeader = header; - } - }, - - deliverPartData: function(partNum, data) - { - if (!done) { - msgBody += data; - } - }, - - endPart: function(partNum) - { - done = true; - }, - }; - - let opts = { - bodyformat: "decode", - strformat: "unicode", - }; - let parser = new jsmime.MimeParser(emitter, opts); - - while (scriptInputStream.available()) { - let data = scriptInputStream.read(scriptInputStream.available()); - parser.deliverData(data); - } - parser.deliverEOF(); - - let hdr = { - from: msgHeader.get("from"), - replyTo: msgHeader.get("reply-to"), - date: msgHeader.get("date"), - subject: msgHeader.get("subject") - }; - return {header: hdr, body: msgBody}; -} - -var CopyPatch = class extends ExtensionCommon.ExtensionAPI { - getAPI(context) { - return { - CopyPatch: { - async getSelectedMessage(windowId) { - return do_getSelectedMessage(windowId); - }, - } - } - } -}; diff --git a/api/CopyPatch/schema.json b/api/CopyPatch/schema.json deleted file mode 100644 index 4e3f62a..0000000 --- a/api/CopyPatch/schema.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "namespace": "CopyPatch", - "functions": [ - { - "name": "getSelectedMessage", - "type": "function", - "async": true, - "parameters": [ - { - "name": "windowId", - "type": "integer" - } - ] - } - ] - } -] diff --git a/background-script.js b/background-script.js index f30a33d..ad00800 100644 --- a/background-script.js +++ b/background-script.js @@ -1,21 +1,99 @@ /* * Copy Patch Thunderbird Add-On * - * Copyright (c) Jan Kiszka, 2019-2020 + * Copyright (c) Jan Kiszka, 2019-2023 + * Copyright (c) John Bieling, 2023 * * Authors: * Jan Kiszka + * John Bieling * * 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 "./node_modules/email-addresses/lib/email-addresses.js"; + +function getFirstHeader(arr) +{ + if (Array.isArray(arr) && arr.length > 0) { + return arr[0]; + } + return undefined; +} + +function getAllHeader(arr) +{ + if (Array.isArray(arr) && arr.length > 0) { + return arr; + } + return undefined; +} + +function parseDisplayName(addr) +{ + let rv = emailAddresses.parseOneAddress(addr); + return { + name: rv.name, + email: rv.address, + } +} + +/* Find first text/plain body */ +function getBody(parts) +{ + /* First check all parts in this level */ + for (let part of parts) { + if (part.body && part.contentType == "text/plain") { + return part.body; + } + } + /* Now check all subparts */ + for (let part of parts) { + if (part.parts) { + let body = getBody(part.parts); + if (body) { + return body; + } + } + } + return null; +} + +async function getMsgData(messageId) +{ + let full = await browser.messages.getFull(messageId); + let date = await getFirstHeader(full.headers["date"]); + let from = await getAllHeader(full.headers["from"]); + let replyTo = await getAllHeader(full.headers["reply-to"]); + let subject = await getFirstHeader(full.headers["subject"]); + let body = getBody(full.parts); + + if (!body) { + return null; + } + + return { + header: { + date: date ? new Date(date) : date, + from: from ? from.map(addr => parseDisplayName(addr)) : from, + replyTo: replyTo ? replyTo.map(addr => parseDisplayName(addr)) : replyTo, + subject: subject + }, + body: body, + isPatch: (body.indexOf("\n---") >= 0 && body.indexOf("\n+++") >= 0) || + body.indexOf("\ndiff --git") >= 0 + } +} + function main() { messenger.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "getMsg") { - return messenger.CopyPatch.getSelectedMessage(sender.tab.windowId); + return browser.messageDisplay.getDisplayedMessage(sender.tab.id).then( + msg => getMsgData(msg.id) + ); } if (request.action === "clipboardWrite") { navigator.clipboard.writeText(request.text); @@ -48,10 +126,9 @@ function main() }); messenger.messageDisplay.onMessageDisplayed.addListener(async (tab, message) => { - msg = await messenger.CopyPatch.getSelectedMessage(tab.windowId); + let msg = await getMsgData(message.id); - /* detect patch pattern in the body */ - if ((msg.body.indexOf("\n---") >= 0 && msg.body.indexOf("\n+++") >= 0) || msg.body.indexOf("\ndiff --git") >= 0) { + if (msg && msg.isPatch) { messenger.messageDisplayAction.enable(tab.id); } else { messenger.messageDisplayAction.disable(tab.id); diff --git a/manifest.json b/manifest.json index 3ee430e..f10006b 100644 --- a/manifest.json +++ b/manifest.json @@ -35,21 +35,5 @@ }, "description": "Copy as patch" } - }, - "experiment_apis": { - "CopyPatch": { - "schema": "api/CopyPatch/schema.json", - "parent": { - "scopes": [ - "addon_parent" - ], - "paths": [ - [ - "CopyPatch" - ] - ], - "script": "api/CopyPatch/implementation.js" - } - } } } From 837929e293e78a82ad82177a598aafc31dcc37d4 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 19:06:14 +0200 Subject: [PATCH 6/7] Drop upper version limit With the Experiment API gone, we can drop strict_max_version, reducing future maintenance efforts. Signed-off-by: Jan Kiszka --- manifest.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index f10006b..18a12b8 100644 --- a/manifest.json +++ b/manifest.json @@ -3,8 +3,7 @@ "applications": { "gecko": { "id": "copypatch@kiszka.org", - "strict_min_version": "102.4.0", - "strict_max_version": "102.*" + "strict_min_version": "102.4.0" } }, "name": "Copy Patch", From 8e38a3431264202b209e2b72d3c5899edfa665e0 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 7 Aug 2023 19:14:22 +0200 Subject: [PATCH 7/7] Switch homepage to github Now that the project is hosted and discussed there, let's move it officially. Signed-off-by: Jan Kiszka --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 18a12b8..9d7421b 100644 --- a/manifest.json +++ b/manifest.json @@ -10,7 +10,7 @@ "description": "Copy email content to clipboard for application as patch", "author": "Jan Kiszka", "version": "2.1.3", - "homepage_url": "http://git.kiszka.org/copypatch.git", + "homepage_url": "https://github.com/jan-kiszka/copypatch", "icons": { "32": "copypatch.png", "64": "copypatch64.png"