Reveal URLs — Architektúra

Ez a dokumentum leírja, hogyan épül fel a Reveal URLs bővítmény, és hogyan működik az alaplogikája, közreműködők és technikai olvasók számára. Végig valós fájlokra és szimbólumokra hivatkozik (path symbol formában), így bármely állítás ellenőrizhető a forráskód alapján. A felhasználó felé néző leírásért lásd a kézikönyvet; a kézi ellenőrzési lépésekért lásd a kézi teszttervet.

Áttekintés

A Reveal URLs egyetlen MV3 WebExtension, amely több motorra épül — Chrome, Edge, Opera, Firefox és Thunderbird — egyetlen közös, tiszta kódmagból. (A Safari elhalasztva: van manifesztje, és összeállítható, ha kifejezetten megnevezik, de ki van zárva az alapértelmezett összeállításból és a CI-ből.) A bővítmény minden link cél-URL-jét felfedi a link mellett egy megjelenített e-mailben, és megjelöl egy olyan linket, amelynek látható szövege más regisztrálható domaint nevez meg, mint a href-je — a klasszikus adathalász árulkodó jel.

A tároló egy pnpm/TypeScript monorepó (pnpm-workspace.yaml, package.json "private": true értékkel). A csomagolás egyetlen esbuild-vezérelt szkript, a tooling/build.mjs, amely lefordítja a közös csomagokat és az egyes motorok vékony belépési pontjait egy betölthető dist/<target>/ könyvtárba. A webextension-polyfill minden szkriptbe be van csomagolva, ahelyett, hogy futásidejű globálisként támaszkodnánk rá, így ugyanaz a forrás változatlanul fut a Chromiumon és a Geckón át.

Két natív e-mail-bővítmény kiterjeszti ugyanazt a felismerési magot olyan felületekre, amelyeket a WebExtension nem érhet el: egy Outlook-bővítmény (Office.js munkaablak, elérve az Outlookot weben/Windowson/Macen/iOS-en/Androidon és az Outlook.com-ot) és egy Gmail-bővítmény (Apps Script CardService, elérve a Gmail web-/Android-/iOS-alkalmazásait). Mindkettő csak a TISZTA elemzést használja újra — egy új packages/core/src/findings.ts modul (analyseAnchors/analyseHtml, egy platformfüggetlen Finding[] modell), amely újrahasználja a meglévő hostMismatch logikát és a tldts regisztrálhatódomain-származtatást. Csak a gazdagép-adapter (ahogyan a törzset beolvassa és elemzi) és a megjelenítés tér el felületenként: az Outlook munkaablaka a törzset a saját DOMParser-ével elemzi, és kliensoldalon fut; a Gmail-bővítmény node-html-parser-rel elemez, és szerveroldalon a Google Apps Script V8 infrastruktúráján fut (ingyenes hosztolás, mivel a Google már birtokolja a leveleket; helyi-interaktívan telepítve clasp-pal). Mivel egyik keretrendszer sem tudja módosítani a megjelenített, olvasási módú üzenet DOM-ját (az Office setAsync/prependAsync metódusai csak szerkesztésre valók; a CardService kártyákat jelenít meg, nem üzenet-HTML-t), mindkét bővítmény egy megállapítás-panelt/kártyát mutat, nem beágyazott jelölést — a meglévő linkProcessor.ts DOM-módosító útvonalát és a REVEAL_URLS_CSS-t egyik bővítmény sem használja újra. Egy relatív href csak egy megbízható, e-mailen belüli <base href>-hez képest kerül feloldásra, egyébként kihagyásra, soha nem a postafiók/szolgáltató origójához képest (ami hamis célhelyet gyártana).

A kódbázis élesen elkülöníti a felelősségeket:

A tároló felépítése

packages/core — a tiszta mag

Minden itteni modul semmilyen bővítmény-API-t (browser.*/chrome.*/messenger.*) nem importál, és nem használ karakterlánc-markup DOM-nyelőt (innerHTML/insertAdjacentHTML). Ezt a szerződést mechanikusan érvényesíti a packages/core/test/purity.test.ts, amely minden src/**/*.ts fájlt begyűjt, a saját stripComments-ével eltávolítja a megjegyzéstörzseket (hogy a jogszerűen a tiltott tokeneket MEGNEVEZŐ docblockok ne buktassák meg az őrt), majd állítja, hogy sem az EXTENSION_API_PATTERN, sem az UNSAFE_DOM_PATTERN nem illeszkedik egyik modulra sem. A nyilvános felület a packages/core/src/index.ts-ből van újraexportálva. Az egyetlen futásidejű függőség a tldts (a public-suffix-tudatos domain-összehasonlításhoz).

A modulok:

packages/webext — a böngésző-API-burkolók

Az itteni modulok HASZNÁLHATJÁK a browser.*-ot, és az egyetlen hely, ahol a WebExtension API-kat érintik. A nyilvános felület a packages/webext/src/index.ts-ből van újraexportálva. A modulok: content.ts (tartalom-életciklus), contentRegistration.ts (dinamikus regisztráció), storage.ts (konfigurációmegőrzés), background.ts (telepítéskori viselkedés), toolbarAction.ts (váltás + jelvény), messageDisplay.ts és messageDisplayBackground.ts (Thunderbird), valamint options/options.ts (a beállításoldal vezérlője).

extensions/<engine> — vékony belépési pontok és manifesztek

Minden motor a saját manifest.json-ját és icons/-ját hordozza. A Chrome, a Firefox és a Thunderbird ezenfelül szállít egy src/-t belépési pontokkal; az Edge, az Opera és a Safari csak manifesztet és ikonokat hordoz, és ÚJRAHASZNÁLJA a Chrome src/-jét (az összeállítási leíró deklarálja — lásd alább). A belépési pontok szándékosan aprók: például az extensions/chrome/src/content.ts csupán void createContentController().bootstrap();, az extensions/chrome/src/background.ts pedig meghívja a registerBackground, registerContentReconciliation és registerToolbarToggle metódusokat. A Firefox háttér-belépési pontja azonos a Chrome-éval; a Thunderbirdé (extensions/thunderbird/src/background.ts) helyette a registerBackground és registerMessageDisplay metódusokat hívja (nincsenek tartalomszkriptjei). Az extensions/_template egy másolásra kész Chromium-váz, nem összeállítási célpont.

tooling, features és a füsttesztek

A tooling tartalmazza a build.mjs, version.mjs és icons.mjs fájlokat. A features tartalmazza a Gherkin-forgatókönyveket, azok lépésdefinícióit és a hű tesztduplikátumokat a features/support/webext.ts és features/support/world.ts fájlokban. Minden motor hordoz egy extensions/<engine>/test/manifest.smoke.test.mjs fájlt is (a _template-é a template.smoke.test.mjs) — ezek a mérvadó manifesztszerződés (lásd a „Motoronkénti összeállítás és a manifesztszerződés” szakaszt alább).

A felfedési folyamat

A tartalomútvonal egy motor-belépési pontnál kezdődik, például az extensions/chrome/src/content.ts-nél, amely meghívja a createContentController().bootstrap()-ot a packages/webext/src/content.ts-ből.

Rendszerindítás (Bootstrap)

A createContentController bootstrap keretenként egyszer fut le:

  1. Szinkronban lefoglalja a keretet, bármely await előtt, a claimBootstrap révén, amely beolvassa/beállítja a BOOTSTRAP_MARKER jelzőt a keret window-ján. Egy statikus, beépített content_scripts bejegyzés és egy átfedő dinamikus felhasználói szkript egyaránt beszúrhatja a content.js-t ugyanabba a keretbe; a közös, elszigetelt-világ window teszi ezt a helyes, egyszeri őrszemmé, és a bármely await előtti lefoglalás azt jelenti, hogy két közel egyidejű beszúrás nem juthat át rajta mindkettő.
  2. ELŐSZÖR betölti a megőrzött konfigurációt a loadConfigWithRetry révén (BOOTSTRAP_CONFIG_RETRIES extra próbálkozás egy átmeneti elutasításnál), megtartva a jelzőt a próbálkozásokon át, hogy egy átfedő beszúrás, amely már nem-műveletté vált, ne ragadjon be. Csak miután az olvasás sikerül, szúrja be a stíluslapot (injectStyles, amely a REVEAL_URLS_CSS-t textContent révén rendeli hozzá), így egy sikertelen olvasás nem fűz hozzá <style>-t, és egy újrapróbálkozás nem duplikálhatja azt.
  3. Ha a konfigurációbetöltés minden próbálkozásnál meghiúsul, a releaseBootstrap visszagörgeti a jelzőt (csak itt, a jelölés megkezdése előtt), hogy egy későbbi újrabeszúrás újrapróbálkozhasson; miután a start lefutott, a jelzőnek meg kell maradnia, mert egy friss jelölő nem birtokolná az előző menet csomópontjait.
  4. Ha config.enabled, meghívja a start-ot, és mindig regisztrál egy onConfigChanged figyelőt, amely újraalkalmazza a konfigurációt (lefedve a letiltott→engedélyezett átmenetet).

Az aktív webhelyszabály feloldása

A start (és az applyConfig) meghívja a resolveSiteRule-t annak eldöntéséhez, hogy jelöljön-e — és hol. A resolveSiteRule először a futó dokumentum SAJÁT helyét próbálja a resolveForHref révén, amely a config.sites-ot az engedélyezett szabályokra szűri, és a mag selectMostSpecific-jére bízza, hogy a legspecifikusabb egyezés nyerjen (egy specifikus beépített legyőz egy tág felhasználói helyettesítő karaktert). Amikor a saját hely semmire nem illeszkedik, ÉS a dokumentum saját origója átlátszatlan vagy örökölt — a hasOpaqueOrigin kapuzza, amely csak about:blank/about:srcdoc (öröklött az INHERITED_ORIGIN_URLS révén) vagy blob:/data: (OPAQUE_ORIGIN_SCHEMES) esetén igaz —, akkor sorrendben visszaesik a FELSŐ keretre (readTopHref, a beépített Proton üzenet-iframe eset), a MEGNYITÓ ablakra (readOpenerHref, az Outlook felugró olvasóablaka, amelyet a window.open("about:blank") nyit meg), és végül a document.referrer-re (readReferrer). Minden külső olvasás védve van a kereszt-origós hozzáférés ellen (ami hibát dob), és csak akkor járul hozzá jelölttel, ha olvasható. Egy KÖZÖNSÉGES, nem illeszkedő dokumentum mérvadó, és soha nem örököl szabályt a környezeti kontextusból, tétlenül hagyva a vezérlőt.

A tartalomgyökérre korlátozás és a módosítások megfigyelése

A start rögzíti az illesztett szabály contentRoot választóját, létrehozza a jelölőt a createAnnotator(config) révén, és feldolgozza a selectRoots(doc, contentRoot) minden illesztett gyökerét (egy try/catchbe csomagolt querySelectorAll, így egy érvénytelen választó biztonságosan []-re bukik). Ezután megfigyeli a doc.body-t (egy stabil tároló) az OBSERVER_OPTIONS-szal ({ childList: true, subtree: true }).

A megfigyelő a processMutations-t táplálja, amely minden módosítást a tartalomgyökérre kapuz: csak akkor dolgoz fel újra egy módosítás-target-et, amikor az egy Element egy gyökéren belül (isWithinRoot, closest révén), és minden hozzáadott Elementet a processAddedNode-on át irányít, amely megjelöl egy olyan csomópontot, amely egy gyökéren belül van, IS egy gyökér, vagy TARTALMAZ egyet (egy lusta SPA-felcsatolás). Így az egy gyökéren belüli forgás és egy később felcsatolódó testvér-üzenettörzs egyaránt elkapódik, míg a gyökereken kívüli alkalmazásfelület érintetlen marad. Egy null jelölő (a bővítmény leállt) nem-művelet.

Az applyConfig egyeztet egy élő konfigurációváltozást: lebont, amikor semmilyen szabály nem illeszkedik, vagy az engedélyezett jelző ki van kapcsolva; újraindít (visszavonás + újramegfigyelés), amikor a feloldott contentRoot megváltozott; és egyébként helyben átrendez — annotator.setConfig, majd annotator.process minden illesztett gyökér felett — a megfigyelő újraindítása nélkül.

Egyetlen horgony jelölése

A jelölő a packages/core/src/linkProcessor.ts-ben él. A createAnnotator egy Annotator-t ad vissza olyan lezárt állapot felett, amelyet az oldal nem tud olvasni: egy erős Map horgonyidentitás szerint kulcsozva, egy jelölőnkénti data-ru token a generateToken-ből (crypto.getRandomValues), és az aktuális konfiguráció. A process(root) metódusa:

  1. Kimetszi azokat a nyilvántartási bejegyzéseket, amelyek horgonya már nincs csatlakoztatva.
  2. A collectAnchors-szal jelölt horgonyokat gyűjt (leszármazott a[href] plusz maga a gyökér, ha az egy a[href], deduplikálva).
  3. Minden horgony célhelyét a resolveAnnotatableUrl-lel oldja fel, amely null-t (kihagyás) ad vissza egy üres/oldalon belüli href, egy elemezhetetlen href, bármely http:/https:-től eltérő séma, vagy egy ignoreHosts bejegyzéssel egyenlő vagy alá tartozó gazdagép esetén; a relatív hrefeket a baseURI-hez képest oldja fel, így az eredmény abszolút.
  4. Az idempotens gyorsútvonalat választja, amikor az isAnnotationIntact megerősíti, hogy a megjelenítési ujjlenyomat változatlan, és minden létrehozott csomópont még csatlakoztatva és a helyén van; egyébként visszavonja az elavult jelölést és újramegjelenít.
  5. Az hostMismatch-csel eltérést számít a tiszta horgonyszövegen, tiszteletben tartja a showOnlyOnMismatch-et (érintetlenül hagyva a tisztességes linkeket), és meghívja az annotateAnchor-t.

Az annotateAnchor csak BIZTONSÁGOS-DOM-ot jelenít meg. "inline" módban egy <span class="reveal-urls-url">-t fűz elé, amelynek URL-jét textContent révén állítja be (egy nyíllal és egy U+00A0 nem-törő szóközzel előtaggal, a truncateUrl-lel csonkolva), amelyet egy <br> követ, a link saját gyermekeit érintetlenül hagyva; a csatlakoztatott span ezután kontraszthátteret kap az applyContrastBackdrop révén. "title" módban elmenti az eredeti title-t (TitleSave), felülírja az URL-lel — az egyetlen írás a horgonyra —, és egy kiemelt eltérésnél egy testvér-<span class="reveal-urls-warn"> jelvényt fűz hozzá, amely a REVEAL_URLS_WARN_LABEL-t viseli. Minden létrehozott csomópont data-ru="<token>"-nel van címkézve (soha nem a horgony), és minden bejegyzés rögzíti a configFingerprint-jét, így egy megjelenítéskonfiguráció-változás átrendezésre kényszerít. A színek és a betűtípus-felülbírálások tipizált element.style.* tulajdonságokon keresztül kerülnek alkalmazásra (applyColour/applyFontOverrides), soha nem CSS-karakterláncba interpolálva. A revertOwned referencia szerint távolítja el a létrehozott csomópontokat, és visszaállítja az elmentett title-t; a revertAll ezt minden birtokolt horgonyra elvégzi, és kiüríti a nyilvántartást.

A truncateUrl Unicode KÓDPONTOK felett működik ([...displayHref]), így egy vágás soha nem hasít ketté egy helyettesítő párt; megőrzi a séma-és-gazdagép origót, és a többit egy záró URL_ELLIPSIS-szel rövidíti.

Gazdagép-eltérés

A packages/core/src/hostMismatch.ts hostMismatch a link látható szövegét hasonlítja össze a href-jével regisztrálható domain, nem nyers gazdagépnév szerint, így egy tisztességes aldomain nincs megjelölve, míg egy megtévesztő igen. Az extractHostCandidates szétbontja a szöveget, és minden tokent a hostCandidate révén redukál (amely megkövetel egy pontot és egy tldts-felismert ICANN public suffixet, elutasítva a közönséges pontozott szöveget, például az e.g-t). Mind a href gazdagépe, mind minden jelölt a regisztrálható domainjére redukálódik a tldts.getDomain-nel, és BÁRMELY jelölt, amelynek domainje eltér a hrefétől, eltérést eredményez — így a valós rosszindulatú gazdagép csali gazdagép melletti megnevezése nem tudja elnyomni a figyelmeztetést. Soha nem dob hibát.

Stílusok és kontraszt

A packages/core/src/styles.ts tartalmazza a beszúrható REVEAL_URLS_CSS-t (rögzített elrendezés, vastagság, alak és egy rögzített fehér, teljesen átlátszatlan chip-háttér — background: #ffffff, opacity: 1; a színek SOHA nem interpolálódnak bele) és a futásidejű applyColour/applyFontOverrides/applyContrastBackdrop segédfüggvényeket. Az applyContrastBackdrop beolvassa az elem feloldott színét és az elemTŐL magától felfelé haladva talált első átlátszatlan hátteret (a saját háttere — a chip rögzített fehér alapértelmezése vagy egy hosztoldal specifikusabb felülbírálása — bármely őse előtt) a szabványos getComputedStyle-on keresztül (az element.ownerDocument?.defaultView-ból származtatva, így keret-biztonságos és csonkolható), és amikor a packages/core/src/contrast.ts needsWhiteBackdrop jelenti, hogy a szöveg megbukik a WCAG AA-n (CONTRAST_THRESHOLD) az adott háttérrel szemben, ÉS a fehér valóban segít, beállítja a style.backgroundColor = "white"-t. A contrast.ts biztosítja a parseColour, relativeLuminance és contrastRatio függvényeket; elemzi a getComputedStyle által visszaadott rgb()/rgba()/hex formákat, és egy teljesen átlátszó színt „nem találtként” kezel.

Konfigurálható gazdagépek és tartalomkorlátozás

Egy SiteRule (packages/core/src/config.ts) megmondja, HOL fut a jelölés (match, allFrames), és MELYIK tároló korlátozza (contentRoot), plusz egy enabled és egy builtin jelző. A szállított DEFAULT_SITES lefedi a Gmailt, a Protont (allFrames-szel) és mindkét Outlook-gazdagépet. A Config összesíti a globális kapcsolókat, színeket, betűtípus-felülbírálásokat és a sites tömböt.

Az egyeztetésiminta-motor

A packages/core/src/matchPattern.ts egy szándékosan MEGSZORÍTÓ, hozzáadáskori grammatikát tartalmaz, a MATCH_PATTERN-t (csak http/https, opcionális vezető *. gazdagép-helyettesítő, glob-útvonal; nincs port, nincs * séma, nincs <all_urls>). A matchesPattern egy URL-t illeszt egy validált mintához, az útvonalat a pathGlobMatches-re bízva (minden * bármely karaktersorozatra illeszkedik); biztonságosan false-ra bukik. A parsePatternParts egy validált mintát host/path/scheme/wildcardHost részekre bont.

Az átfedő szabályok a „legspecifikusabb nyer” elv szerint oldódnak fel: a compareSiteSpecificity így rangsorol: (1) pontos gazdagép a helyettesítő gazdagép felett, (2) hosszabb literál gazdagép, (3) literálisabb útvonal (literalPathLength), majd (4) egy determinisztikus ASCII holtverseny-döntő; a selectMostSpecific egy URL-re illeszkedő legspecifikusabb engedélyezett szabályt ad vissza.

A megadott-origó-lefedettség egy KÜLÖNÁLLÓ, szándékosan TÁGABB illesztő. A permissions.getAll() a teljes WebExtension-grammatikában (<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*) jelenthet megadásokat, amelyeket a megszorító MATCH_PATTERN elutasítana. A parseGrantedOrigin ezeket GrantedOriginParts-ra elemzi, és az originCovers jelenti, hogy egy megadott origó lefed-e egy szabály match-ét (pl. egy tág https://*/* megadás valóban lefed minden https-szabályt). A matchAllowsOriginFallback jelenti, hogy egy szabály útvonala pontosan ORIGIN_FALLBACK_PATH (/*)-e — a Chromium és a Gecko/Firefox 128 megszorításainak biztonságos metszete a dinamikus átlátszatlan-origó visszaesésen.

Dinamikus regisztráció

A packages/webext/src/contentRegistration.ts felhasználó által hozzáadott szabályonként egy dinamikus tartalomszkriptet regisztrál. A beépítetteket a statikus content_scripts bejegyzések szolgálják ki, és SOHA nem regisztrálódnak dinamikusan. A desiredContentScripts a config.sites-ot nem-beépített, engedélyezett szabályokra szűri, amelyek origója isOriginGranted (az originCovers-re bízva), mindegyiket egy stabil azonosítójú RegisteredContentScript-re képezi le (contentScriptId, a match egy FNV-1a hasítása, így az azonosító csak az API-biztos karakterkészletet használja), és a matchOriginAsFallback: true-t CSAK akkor állítja be, amikor a matchAllowsOriginFallback(rule.match) fennáll (egy /* útvonal). A jelző ilyen kapuzása megakadályozza, hogy egy nem konform útvonalú szabály miatt az egész kötegregisztrációt elutasítsák; egy ilyen szabály felugró lefedettség nélkül regisztrálódik, terv szerint.

A reconcileContentScriptsOnce beolvassa a megadott origókat, a tárolt konfigurációt és az élő regisztrációkat, majd egy TULAJDONSÁG-TUDATOS diff révén konvergál: a sameRegistration a kívánt leírót és az élő visszaolvasást is egy normalizált formára vetíti (minden mező valós WebExtension-alapértelmezését alkalmazva — az allFrames/matchOriginAsFallback alapból false, a persistAcrossSessions alapból TRUE, a runAt alapból document_idle), így egy frissen regisztrált szkript EGYENLŐNEK bizonyul azzal a leíróval, amely létrehozta, és nem alakulhat ki újraregisztrációs hurok. Mivel nincs updateContentScripts, egy megváltozott szkript deregisztrálódik, majd újraregisztrálódik. A reconcileContentScripts ezt egy folyamatban lévő ígérettel (inFlight) burkolja, így két közel egyidejű kiváltó (egy konfigurációváltozás ÉS a permissions.onAdded, amely egy webhely-hozzáadáskor egyaránt kioldódik) sorosítódik, nem versenyeznek. A registerContentReconciliation beköti a kiváltókat (permissions.onAdded/onRemoved, onConfigChanged), és indításkor egyszer konvergál; csak a Chrome és a Firefox háttér-belépési pontjai importálják.

A választható engedélyfolyamat

A statikus manifesztek deklarálják az optional_host_permissions-t (http://*/*, https://*/*), így a felhasználó futásidőben megadhat egy tetszőleges további webhosztot. A packages/webext/src/options/options.ts addSite metódusa validálja a TELJES jelölt szabályt (match ÉS content-root) a normaliseSiteRule révén, MIELŐTT bármely engedélyt kérne, a gazdagép-origót a matchOrigin-nal származtatja, és a browser.permissions.request-et a Hozzáadás gomb kattintási gesztusán belülről hívja; csak megadás esetén fűz hozzá egy builtin:false sort, és ír a kanonikus íróútvonalon keresztül.

Konfiguráció, tárolás és a kanonikus minta

Az egyetlen validációs tölcsér

A packages/core/src/config.ts-ben lévő normaliseConfig az egyetlen kanonikus olvasóútvonal: minden mezőt kényszerít és korlátoz, bármely érvénytelenre az alapértelmezésre visszaesve, és soha nem dob hibát. Mezőnkénti validátorokból épül fel — normaliseBoolean, normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts (minden bejegyzést egy csupasz punycode gazdagépnévre redukálva a bareHostname révén), normaliseMatchColour/normaliseMismatchColour (a safeColour.ts isSafeColour által kapuzva), normaliseCssSize, normaliseFontWeight, normaliseContentRoot (a CONTENT_ROOT_PATTERN/CONTENT_ROOT_MAX_LENGTH által korlátozva), normaliseMatchPattern (a MATCH_PATTERN által kapuzva) és normaliseSites. A normaliseSites eldobja az elutasítottakat, match szerint deduplikál, TRUE-ra KÉNYSZERÍTI a builtin-t minden olyan szabályon, amelynek match-e egyenlő egy beépítettével (bezárva a pontos-egyezés-árnyékolja-a-beépítettet / kettős-beszúrás vektort), újravet bármely hiányzó beépítettet (így egy manipulált tároló nem tud elejteni egy mag-szolgáltatót), és match szerint alfanumerikusan rendez egy kanonikus sorrendhez. A configFingerprint csak a megjelenítést befolyásoló mezőket hasítja (kizárva az enabled, ignoreHosts és sites mezőket, amelyek inditást/leállítást és kihagyást vezérelnek, nem átrendezést) a fnv1a révén.

Tárolás

A packages/webext/src/storage.ts a WebExtension tároló API-t burkolja. A configArea a browser.storage.sync-et használja, ha elérhető (így a beállítások vándorolnak), és a browser.storage.local-ra esik vissza (pl. Thunderbirdön); a configAreaName jelenti, melyik aktív. A getConfig, setConfig és onConfigChanged mind a normaliseConfig-on tölcsérezik át a nyers értéküket, így egy hibás vagy manipulált tároló soha nem adhat át érvénytelen Config-ot egy hívónak. Az onConfigChanged ezenfelül figyelmen kívül hagyja az INAKTÍV tárolóterület eseményeit és a nem kapcsolódó kulcsok változásait.

A két bővítmény ugyanazt az olvasás -> normaliseConfig -> visszatérés tölcsért használja újra a saját gazdagép-tárolói felett. Az Outlook munkaablakának extensions/outlook/src/roamingStorage.ts-e a Office.context.roamingSettings-t burkolja (szinkron get/set, saveAsync-cal véglegesítve); a Gmail-bővítmény packages/gmail/src/propertiesStorage.ts-e a PropertiesService.getUserProperties()-t burkolja (felhasználónkénti, az adott felhasználó eszközei között vándorló, JSON-kódolt egy kulcs alatt, és NEM igényel extra OAuth-hatókört — soha nem a megosztott ScriptProperties). Mindkettő elérhetővé teszi a getConfig/setConfig metódusokat plusz a kártya-releváns getIgnoreHosts/getHighlightMismatch hozzáférőket, és egy elérhetetlen gazdagép-felületet egy dedikált hibával véd. A Gmail kezdőlap-kiváltója egy CardService beállítási kártyát jelenít meg a getConfig-ból, és az onSaveSettings űrlapküldő kezelője az elemzett ignoreHosts/highlightMismatch-et az aktuális konfigurációra egyesíti, és a setConfig-on keresztül megőrzi; a kontextuális kiváltó az ignoreHosts-ot az adapterbe, a highlightMismatch-et pedig a kártyaépítőbe fűzi, védekezően olvasva, így egy tárolási hiba az alapértelmezésekre esik vissza, ahelyett, hogy megtörné az üzenetelemzést.

A kanonikus konfiguráció + célzott frissítés minta

Három íróútvonal beolvassa a kanonikus konfigurációt, pontosan egy dolgot megváltoztat, és visszaírja — soha nem véglegesítve el nem mentett űrlap-szerkesztéseket, és soha nem jelenítve meg újra az űrlapot:

Az eszköztár-művelet saját toggleEnabled-je (packages/webext/src/toolbarAction.ts) ugyanezt a mintát követi a háttér oldaláról.

Motoronkénti összeállítás és a manifesztszerződés

A tooling/build.mjs egyetlen esbuild-összeállítást vezényel minden WebExtension-célponthoz. A TARGETS leíró megnevezi az egyes célpontok FORRÁSCÉLPONTJÁT: a Chrome, a Firefox és a Thunderbird a saját src/-jét szállítja; az Edge, az Opera és a Safari a chrome-ot deklarálja forrásaként; az Outlook (Office.js) bővítmény a saját forrása. Az ACTIVE_TARGETS (Chrome, Edge, Firefox, Opera és Thunderbird — a Safari és az Outlook kizárva) az, amit az --all/--package összeállít; a Safari és az Outlook csak akkor épül, ha kifejezetten megnevezik, a Gmail (Apps Script) bővítmény pedig egy különálló esbuild-köteg (make build-gmail) az --all hurkon kívül. Az a Gmail-köteg az Apps Script V8 futtatókörnyezetét célozza, amelynek se ES-moduljai, se natív URL-je nincs: ESM-ként épül, majd a záró export {…} utasítását eltávolítják (így a kiváltófüggvények felső szintű globálisok maradnak, amelyeket az Apps Script meghívhat), és becsomagol egy kis URL polyfillt (csak akkor telepítve, ha az URL hiányzik), így a közös mag new URL(...)-je szerveroldalon feloldja a linkeket. A buildTarget kitisztítja a dist/<target>/-t, szó szerint másolja a célpontonkénti manifest.json-t és icons/-t, minden forrásszkriptet IIFE-ként csomagol (a beszúrt INJECTED_SCRIPTS content.ts/messageDisplay.ts nem lehet ES-modul, a hátterek is klasszikus workerek/eseményoldalak), a közös beállításoldalt pedig ESM-ként, és átmásolja az options.html/options.css-t a packages/webext/src/options/-ból. A webextension-polyfill minden szkriptbe be van csomagolva.

A tooling/version.mjs bélyegzi a verziókat: a MAJOR.MINOR-t a gyökér package.json-ból veszi, és egy automatikusan növekvő BUILD-számot (a célpont-manifesztek legmagasabb létező harmadik komponenséből olvasva) fűz minden célpont-manifesztbe, azokat lépésben tartva. A legtöbb célpont egyetlen manifest.json-t hordoz; az Outlook-bővítmény két verziózott manifesztet hordoz (manifest.json és manifest.xml), és mindkettő bélyegzésre kerül, és a _template váz is lépésben van bélyegezve. A gyökér package.json ugyanazon a verzión marad, de érvényes semver marad (egy négyrészes verzió megengedett egy manifesztben, soha nem a package.json-ban). A tooling/icons.mjs (a make icons révén futtatva) az egyetlen vektorforrást, az assets/icon.svg-t rasztereli minden célpont icons/icon48.png és icons/icon128.png fájljává, előbb a cairosvg-t, majd az inkscape-et, majd az ImageMagicket próbálva.

A manifesztszerződés — a motoronkénti pontos kötelező mezők — az egyetlen igazságforrás minden extensions/<engine>/test/manifest.smoke.test.mjs-ben (pl. az extensions/chrome/test/manifest.smoke.test.mjs assertManifestShape-je, amely az összeállítást is elvégzi, és megerősíti, hogy minden hivatkozott fájl feloldódik a distben). A projekt deduplikációs konvenciója szerint a mezőnkénti alak itt NINCS újrafelsorolva; hivatkozzon azokra a füsttesztekre és a kézi tesztterv „Manifest contract” megjegyzésére.

Nemzetköziesítés (i18n)

A Reveal URLs az angolon kívül sok nyelvre lokalizált. A készlet egyetlen forrásból származik, a packages/core/src/locales.json-ból (SUPPORTED_LOCALES, natív nevek és az alapértelmezés), amelyet mind a bővítménycsomagok (a feloldott JSON-import révén), mind a sima Node-alapú webhely-összeállítás olvas. Minden nem angol karakterlánc gépi fordítású, és emberi ellenőrzésre vár; az eredetjelölő formátumonkénti (a _locales az egyes üzenetek description-jét használja, a webhely-felület szótárai a site/i18n/README.md-ben rögzítik, a lefordított dokumentumforrások pedig egy első soros HTML-megjegyzést hordoznak).

Három független lokalizációs felület van, szándékosan nem összekötve, és mindegyik egy összeállításidő / futásidő vonal mentén oszlik meg.

Levelezőprogram – „Aktív webhelyek” elrejtése (AD-3)

Egy levelezőprogram-célpont (ma a Thunderbird) már minden megjelenített üzenetet lát, így a gazdagépenkénti „Aktív webhelyek” szerkesztő ott felesleges, és eltávolításra kerül. A döntést egy dokumentált célpontonkénti leíró-jelző hordozza, a mailClient: true, a tooling/build.mjs TARGETS.thunderbird-jén — soha nem egy bekódolt célpontnév az összeállítási vagy UI-logikában —, és két félben valósul meg:

Biztonsági és adatvédelmi helyzet

Tesztelés