From f445a0d7965d6f6c8ef26ecadaaf2d85f9152090 Mon Sep 17 00:00:00 2001 From: Enes Yilmaz Date: Sat, 8 Aug 2026 22:16:56 -0400 Subject: [PATCH] fix: scan the whole page instead of guessed compose selectors Every scan was scoped to div[g_editable="true"] and three other guesses at Gmail's internal class names. That put an assumption on the critical path: if the compose root did not match, nothing was ever examined and the extension silently did nothing, with no way to tell that apart from not being installed. It never needed to be that fragile. #mt-signature and data-signature-template are Mailsuite's own labels and appear on nothing else, so if one is in the page it is theirs. Scanning from document.body deletes the whole failure mode. Side effect, documented: this also hides the signature on Mailtrack mail you receive. Purely visual, in your own browser, and it changes nothing about what anyone actually sent. Also handles Gmail's id rewriting. Gmail turns id="mt-signature" into id="m_-1234...mt-signature" when it renders or quotes a message, which is why Mailsuite ships its own un-prefixing helper. Matching on contains covers it. Two console lines added, because the last round could not distinguish "not working" from "not loaded" without reading Chrome's Preferences file off disk: [MailSuiteFree] active on mail.google.com [MailSuiteFree] removed 1 signature block(s) 16 tests, all passing. Version to 0.2.0. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_013rTepGiDUe15iPn6XqmKjy --- manifest.json | 4 ++-- src/detect.js | 4 ++++ src/strip.js | 39 ++++++++++++++++++++++++++------------- test/detect.test.js | 11 +++++++++++ 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/manifest.json b/manifest.json index b5633fa..9843c62 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ -{ +{ "manifest_version": 3, "name": "Mailsuite Signature Stripper", - "version": "0.1.0", + "version": "0.2.0", "description": "Removes the Mailsuite/Mailtrack promotional signature from your outgoing Gmail messages. Read tracking keeps working.", "permissions": ["storage"], "host_permissions": ["https://mail.google.com/*"], diff --git a/src/detect.js b/src/detect.js index 536188e..76f1112 100644 --- a/src/detect.js +++ b/src/detect.js @@ -29,6 +29,10 @@ */ const SIGNATURE_MARKERS = [ '#mt-signature', + /* Gmail rewrites ids when it quotes or renders a message, turning + id="mt-signature" into id="m_-1234...mt-signature", which is why + Mailsuite ships its own un-prefixing helper. Match on contains. */ + '[id*="mt-signature"]', '[data-signature-template]', '[class*="mt-signature"]', '[class*="mt-old-signature"]', diff --git a/src/strip.js b/src/strip.js index aa0daa7..9857ce1 100644 --- a/src/strip.js +++ b/src/strip.js @@ -10,14 +10,23 @@ const detect = globalThis.MailSuiteFreeDetect; if (!detect) return; - /* Gmail's compose body, across layouts and locales. */ - const COMPOSE = [ - 'div[g_editable="true"]', - 'div.Am.Al.editable', - 'div[contenteditable="true"][role="textbox"]', - 'div[aria-label="Message Body"]', - ].join(','); - + /* + * Scan the whole page, not a list of guessed compose selectors. + * + * The first version scoped every scan to div[g_editable="true"] and friends. + * That put a guess about Gmail's internal class names on the critical path: + * if the compose root did not match, nothing was ever examined and the + * extension silently did nothing. + * + * It does not need to be that fragile. #mt-signature and + * data-signature-template are Mailsuite's own labels and appear on nothing + * else, so if one is in the page it is theirs and it can go. Scanning from + * document.body removes an entire class of failure. + * + * Side effect worth knowing: this also hides the signature on Mailtrack mail + * you receive. Purely visual, in your browser, and it changes nothing about + * what anybody sent. + */ const settings = { enabled: true, keepUnsubscribe: true, removed: 0 }; chrome.storage.local.get(settings).then((stored) => Object.assign(settings, stored)); @@ -26,14 +35,14 @@ }); const scrubAll = () => { - if (!settings.enabled) return 0; - let removed = 0; - for (const root of document.querySelectorAll(COMPOSE)) { - removed += detect.scrubRoot(root, { keepUnsubscribe: settings.keepUnsubscribe }); - } + if (!settings.enabled || !document.body) return 0; + const removed = detect.scrubRoot(document.body, { + keepUnsubscribe: settings.keepUnsubscribe, + }); if (removed) { settings.removed += removed; chrome.storage.local.set({ removed: settings.removed }); + console.log(`[MailSuiteFree] removed ${removed} signature block(s)`); } return removed; }; @@ -81,5 +90,9 @@ true, ); + /* Says the content script is alive. If this line is missing from the Gmail + console, the extension is not loaded, which is a different problem from the + extension not working. */ + console.log('[MailSuiteFree] active on', location.host); scrubAll(); })(); diff --git a/test/detect.test.js b/test/detect.test.js index ba0effa..2b03665 100644 --- a/test/detect.test.js +++ b/test/detect.test.js @@ -187,6 +187,17 @@ test('a beacon inside the real signature survives it', () => { assert.equal(root.querySelectorAll('img[src*="/trace/mail/"]').length, 1); }); +test('catches the signature after Gmail has prefixed the id', () => { + /* Gmail rewrites ids when it renders or quotes a message, which is why + Mailsuite ships its own un-prefixing helper. */ + const root = compose( + '
Body
' + realSignature(18).replace('id="mt-signature"', 'id="m_-8891234567890mt-signature"'), + ); + + assert.equal(detect.scrubRoot(root), 1); + assert.equal(root.querySelector('[id*="mt-signature"]'), null); +}); + test('markedBlocks returns only the outermost match', () => { const root = compose(realSignature(17).replace('alt="Mailsuite"', 'alt="Mailsuite" class="mt-signature-logo"')); const blocks = detect.markedBlocks(root);