Reveal URLs — Architektúra
Tento dokument opisuje, ako je rozšírenie Reveal URLs usporiadané a ako
funguje jeho základná logika. Je určený prispievateľom a technicky
zameraným čitateľom. V celom texte odkazuje na skutočné súbory a symboly
(v tvare path symbol), aby sa každé tvrdenie dalo overiť v zdrojovom
kóde. Používateľský pohľad nájdete v manual; kroky ručného
overenia sú v manual test plan.
Prehľad
Reveal URLs je jedna MV3 WebExtension budovaná pre viacero enginov —
Chrome, Edge, Opera, Firefox a Thunderbird — z jedného zdieľaného, čistého
jadra kódu. (Safari je zatiaľ odložené: má manifest a dá sa zostaviť, ak ho
pomenujete výslovne, ale nie je súčasťou predvoleného buildu ani CI.)
Rozšírenie zobrazuje cieľový URL každej poštovej väzby vedľa odkazu v
vykreslenom e-maile a označí odkaz, ktorého viditeľný text pomenúva inú
registrovateľnú doménu než jeho href — klasický príznak phishingu.
Repozitár je monorepo pnpm/TypeScript (pnpm-workspace.yaml,
package.json s "private": true). Balenie riadi jediný skript postavený
na esbuild, tooling/build.mjs, ktorý kompiluje zdieľané balíčky aj tenké
vstupné body jednotlivých enginov do načítateľného adresára
dist/<target>/. webextension-polyfill sa balí priamo do každého
skriptu namiesto spoliehania sa na globálny runtime objekt, takže rovnaký
zdroj beží bez úprav v Chromium aj Gecko.
Dva natívne poštové doplnky rozširujú rovnaké detekčné jadro na povrchy,
kam sa WebExtension nedostane: Outlook Add-in (panel Office.js, dostupný
v Outlook on the web/Windows/Mac/iOS/Android a Outlook.com) a
Gmail Add-on (Apps Script CardService, pre Gmail na webe/Android/iOS).
Oba znovu používajú iba čistú analýzu — nový modul
packages/core/src/findings.ts (analyseAnchors/analyseHtml, platformovo
neutrálny model Finding[]) stavia na existujúcej logike hostMismatch a
odvodzovaní registrovateľných domén cez tldts. Líši sa iba hostiteľský
adaptér (ako sa číta a parsuje telo správy) a prezentácia. Outlook panel
parsuje telo vlastným DOMParser a beží na strane klienta; Gmail doplnok
parsuje cez node-html-parser a beží na serverovej infraštruktúre Google
Apps Script V8. Keďže ani jeden framework nedokáže meniť vykreslený DOM
správy v režime čítania (setAsync/prependAsync v Office fungujú len pri
kompozícii a CardService rendruje karty, nie HTML správy), oba doplnky
zobrazujú panel/kartu zistení namiesto vkladania anotácií priamo do textu.
Existujúca DOM-mutačná cesta z linkProcessor.ts ani REVEAL_URLS_CSS sa
preto v týchto doplnkoch nepoužívajú. Relatívny href sa vyhodnocuje iba voči
dôveryhodnému <base href> v e-maile; inak sa preskočí a nikdy sa
nevyhodnocuje voči pôvodu schránky alebo poskytovateľa.
Kódová základňa má zodpovednosti ostro oddelené:
packages/coreobsahuje čistú logiku — bez browser API, bez úložiska a bez sinkov, ktoré by menili markup. Dá sa jednotkovo testovať v obyčajnom JavaScripte a je jediným zdrojom pravdy pre anotáciu odkazov, validáciu konfigurácie, párovanie match patternov, porovnávanie hostiteľov a farby s ohľadom na kontrast.packages/webextobaluje túto logiku WebExtension API vrstvou: životný cyklus content skriptov, dynamickú registráciu, UI nastavení, background wiring, akciu na paneli nástrojov a cestu zobrazenia správ v Thunderbirde.extensions/<engine>drží tenké vstupné body pre jednotlivé enginy a ichmanifest.json.toolingobsahuje build, verziovanie a skripty na generovanie ikon.featuresobsahuje Cucumber BDD scenáre a ich test doubles.
Rozloženie repozitára
packages/core — čisté jadro
Žiadny modul tu neimportuje API rozšírenia (browser.*/chrome.*/messenger.*)
a nepoužíva DOM sinky, ktoré prijímajú reťazce ako markup
(innerHTML/insertAdjacentHTML). Tento kontrakt mechanicky vynucuje
packages/core/test/purity.test.ts, ktorý prejde všetky src/**/*.ts,
odstráni telá komentárov vlastným stripComments (aby docblocky, ktoré tieto
tokeny len pomenúvajú, nespôsobili falošný poplach) a potom overí, že sa v
žiadnom module nezhodujú ani EXTENSION_API_PATTERN, ani
UNSAFE_DOM_PATTERN. Verejná plocha sa re-exportuje z
packages/core/src/index.ts. Jedinou runtime závislosťou je tldts,
potrebný na porovnávanie domén s ohľadom na public suffix.
Moduly sú:
packages/core/src/config.ts— schémaConfig/SiteRule, predvolené hodnoty a jediný validačný lieviknormaliseConfig.packages/core/src/matchPattern.ts— gramatika match patternov, matcher, poradie podľa špecificity a pokrytie udelených originov.packages/core/src/linkProcessor.ts— anotátor pracujúci nad dokumentom.packages/core/src/hostMismatch.ts— kontrola nesúladu registrovateľnej domény.packages/core/src/styles.tsapackages/core/src/contrast.ts— injektovateľný stylesheet a farbenie s ohľadom na kontrast.packages/core/src/safeColour.ts— predikát bezpečnej farbyisSafeColour.
packages/webext — obaly browser-API
Moduly tu už MÔŽU používať browser.* a sú jediným miestom, kde sa dotýkame
WebExtension API. Verejná plocha sa re-exportuje z
packages/webext/src/index.ts. Moduly sú: content.ts (životný cyklus
content skriptov), contentRegistration.ts (dynamická registrácia),
storage.ts (perzistencia konfigurácie), background.ts (správanie pri
inštalácii), toolbarAction.ts (prepínač + badge),
messageDisplay.ts a messageDisplayBackground.ts (Thunderbird) a
options/options.ts (radič stránky nastavení).
extensions/<engine> — tenké vstupné body a manifesty
Každý engine má vlastný manifest.json a icons/. Chrome, Firefox a
Thunderbird navyše obsahujú src/ so vstupnými bodmi; Edge, Opera a Safari
majú len manifest a ikony a ZNOVU používajú src/ z Chrome
(deklarované v build deskriptore nižšie). Vstupné body sú zámerne malé:
napríklad extensions/chrome/src/content.ts je iba
void createContentController().bootstrap(); a
extensions/chrome/src/background.ts volá registerBackground,
registerContentReconciliation a registerToolbarToggle. Firefox background
entry je zhodný s Chrome; Thunderbird
(extensions/thunderbird/src/background.ts) namiesto toho volá
registerBackground a registerMessageDisplay, pretože content skripty nemá.
extensions/_template je kopírovateľná chromium kostra, nie build target.
tooling, features a smoke testy
tooling obsahuje build.mjs, version.mjs a icons.mjs. features
obsahuje Gherkin scenáre, step definitions a verné test doubles v
features/support/webext.ts a features/support/world.ts. Každý engine má aj
extensions/<engine>/test/manifest.smoke.test.mjs
(_template má template.smoke.test.mjs) — tie predstavujú autoritatívny
kontrakt manifestu (pozri nižšie časť „Build pre jednotlivé enginy a kontrakt
manifestu“).
Pipeline odhaľovania
Cesta obsahu sa začína v engine entry, napríklad
extensions/chrome/src/content.ts, ktoré volá
createContentController().bootstrap() z packages/webext/src/content.ts.
Bootstrap
createContentController spustí bootstrap raz pre každý frame:
- Frame si nárokuje synchronne, ešte pred akýmkoľvek
await, cezclaimBootstrap, ktorý číta a nastavuje príznakBOOTSTRAP_MARKERnawindowdaného frame. Statický vstup vcontent_scriptsaj prekrývajúci sa dynamický používateľský skript môžu do toho istého frame injektovaťcontent.js; zdieľanéwindowv isolated world je preto správna ochrana „iba raz“ a skoré nárokovanie zaručí, že dve takmer súčasné injekcie neprejdú obe. - Najprv načíta perzistovanú konfiguráciu cez
loadConfigWithRetry(BOOTSTRAP_CONFIG_RETRIESdodatočných pokusov pri dočasnom zlyhaní), pričom marker drží počas opakovaní, aby sa prekrývajúca injekcia, ktorá už skončila ako no-op, neocitla v nekonzistentnom stave. Až po úspešnom čítaní injektuje stylesheet (injectStyles, ktorý priraďujeREVEAL_URLS_CSSceztextContent), takže zlyhané čítanie nepridá žiadny<style>a retry ho nemôže zdvojiť. - Ak načítanie konfigurácie zlyhá pri všetkých pokusoch,
releaseBootstrapmarker vráti späť (iba tu, pred začatím anotácie), aby neskoršia reinjekcia mohla skúsiť znova; keď užstartprebehne, marker musí zostať, lebo nový anotátor by nevlastnil uzly z predchádzajúceho behu. - Keď
config.enabledplatí, zavolástart, a vždy zaregistruje listeneronConfigChanged, ktorý znovu aplikuje konfiguráciu a pokrýva tak prechod disabled → enabled.
Určenie pravidla aktívnej stránky
start (aj applyConfig) volá resolveSiteRule, aby sa rozhodlo, či a kde sa
má anotovať. resolveSiteRule najprv skúsi vlastnú lokaciou bežiaceho
dokumentu cez resolveForHref, ktorý filtruje config.sites na povolené
pravidlá a deleguje na selectMostSpecific, takže vyhrá najšpecifickejšia
zhoda. Keď vlastná lokácia nič nezhoduje a pôvod dokumentu je nepriehľadný alebo
zdedený — riadené hasOpaqueOrigin, ktoré vracia true iba pre
about:blank/about:srcdoc (zdedené cez INHERITED_ORIGIN_URLS) alebo
blob:/data: (OPAQUE_ORIGIN_SCHEMES) — pristupuje sa postupne k TOP frame
(readTopHref), otvárajúcemu oknu (readOpenerHref) a napokon k
document.referrer (readReferrer). Každé externé čítanie je chránené proti
cross-origin prístupu a pridá kandidáta iba vtedy, keď je čitateľné. Bežný
nezhodný dokument je autoritatívny a nikdy nepreberá pravidlo z okolitého
kontextu.
Obmedzenie na koreň obsahu a sledovanie zmien
start si uloží contentRoot z vyhovujúceho pravidla, vytvorí anotátor cez
createAnnotator(config) a spracuje každý koreň zhodný s
selectRoots(doc, contentRoot). Potom pozoruje doc.body so
OBSERVER_OPTIONS ({ childList: true, subtree: true }).
Observer posiela mutácie do processMutations, ktoré každú mutáciu filtruje
cez content root: cieľ znovu spracuje len vtedy, keď je Element vnútri rootu,
a každý pridaný element pošle do processAddedNode, ktoré anotuje uzol vtedy,
keď je v roote, je sám rootom alebo root obsahuje. Vďaka tomu sa zachytí
churn v tele správy aj neskoršie pripojenie súrodeneckého tela správy v SPA,
zatiaľ čo aplikačné chrome mimo rootov zostane nedotknuté. Null anotátor je
bez vedľajších účinkov.
applyConfig zosúlaďuje živú zmenu konfigurácie: keď už pravidlo neplatí alebo
je rozšírenie vypnuté, ukončí sa; keď sa zmení contentRoot, vykoná reštart
(revert + nové observe); inak iba pretečie konfiguráciu na mieste —
annotator.setConfig a následné annotator.process nad každým rootom bez
reštartu observera.
Označenie jedného odkazu
Anotátor žije v packages/core/src/linkProcessor.ts. createAnnotator vracia
Annotator, ktorého stav žije v closure a stránka ho nevidí: silná Map
kľúčovaná identitou anchoru, token data-ru generovaný cez generateToken
(crypto.getRandomValues) a aktuálna konfigurácia. Metóda process(root):
- Odstráni záznamy pre anchory, ktoré už nie sú pripojené.
- Nazbiera kandidátne anchory cez
collectAnchors. - Vyrieši cieľ každého anchoru cez
resolveAnnotatableUrl, ktorý vracianullpre prázdny alebo vnútrostránkový href, neparsovateľný href, schému inú nežhttp:/https:a hostiteľov zignoreHosts. - Vezme idempotentnú rýchlu cestu, keď
isAnnotationIntactpotvrdí nezmenený fingerprint renderu a všetky vytvorené uzly sú stále pripojené a na správnom mieste; inak starú anotáciu revertuje a vyrenderuje znovu. - Vypočíta nesúlad cez
hostMismatchna očistenom texte anchoru, rešpektujeshowOnlyOnMismatcha voláannotateAnchor.
annotateAnchor vytvára iba bezpečný DOM. V režime "inline" vloží na začiatok
<span class="reveal-urls-url"> s URL nastaveným cez textContent
(s predponou šípky a U+00A0, skracovaný cez truncateUrl) a zaň <br>,
pričom deti samotného odkazu nechá nedotknuté. Pripojený span potom dostane
kontrastné pozadie cez applyContrastBackdrop. V režime "title" uloží
pôvodný title (TitleSave), prepíše ho URL a pri zvýraznenom nesúlade
pridá súrodenecký badge
<span class="reveal-urls-warn"> s textom REVEAL_URLS_WARN_LABEL.
Každý vytvorený uzol je označený data-ru="<token>" a každý záznam nesie
configFingerprint, aby zmena render-konfigurácie vynútila nové rozloženie.
Farby a typografické override sa aplikujú cez typed element.style.*
properties (applyColour/applyFontOverrides), nikdy nie interpoláciou do CSS
reťazca. revertOwned odstráni vytvorené uzly podľa referencie a obnoví
uložený title; revertAll to vykoná pre všetky vlastnené anchory a vyčistí
register.
truncateUrl pracuje s Unicode code points ([...displayHref]), takže nikdy
neprereže surrogate pair; zachová schému a hostiteľa a zvyšok skráti
pridaním URL_ELLIPSIS.
Nesúlad hostiteľa
packages/core/src/hostMismatch.ts hostMismatch porovnáva viditeľný text
odkazu s jeho href podľa registrovateľnej domény, nie podľa surového
hostname. Čestná subdoména sa teda neoznačí, zatiaľ čo napodobnenina áno.
extractHostCandidates rozdelí text a každý token zredukuje cez
hostCandidate, ktoré vyžaduje bodku aj ICANN public suffix rozpoznaný cez
tldts. Hostiteľ z href aj kandidáti sa zredukujú na registrovateľnú doménu
cez tldts.getDomain a nesúlad vznikne pri ktoromkoľvek kandidátovi s inou
doménou. Funkcia nikdy nevyhadzuje výnimku.
Štýly a kontrast
packages/core/src/styles.ts obsahuje injektovateľný REVEAL_URLS_CSS
(pevné rozloženie, váhu, tvar a plne nepriehľadné biele pozadie čipu) aj
runtime helpery applyColour, applyFontOverrides a
applyContrastBackdrop. applyContrastBackdrop číta vypočítanú farbu textu
a prvé nepriehľadné pozadie pri prechode od samotného elementu smerom nahor
cez getComputedStyle; keď packages/core/src/contrast.ts
needsWhiteBackdrop vyhodnotí, že text nespĺňa WCAG AA
(CONTRAST_THRESHOLD) a biele pozadie skutočne pomôže, nastaví
style.backgroundColor = "white". contrast.ts poskytuje parseColour,
relativeLuminance a contrastRatio; parsuje tvary rgb()/rgba()/hex,
ktoré vracia getComputedStyle, a úplne transparentnú farbu chápe ako
„nenájdenú“.
Konfigurovateľní hostitelia a rozsah obsahu
SiteRule v packages/core/src/config.ts určuje KDE anotácia beží (match,
allFrames) a KTORÝ kontajner ju obmedzuje (contentRoot), spolu s enabled
a príznakom builtin. Dodané DEFAULT_SITES pokrývajú Gmail, Proton
(allFrames) a oba Outlook hosty. Config združuje globálne prepínače,
farby, typografické override a pole sites.
Engine match patternov
packages/core/src/matchPattern.ts obsahuje zámerne reštriktívnu gramatiku
MATCH_PATTERN (iba http/https, voliteľný leading *. wildcard hosta,
glob path; bez portu, bez schémy *, bez <all_urls>). matchesPattern
porovná URL s validovaným patternom a cestu deleguje na pathGlobMatches.
parsePatternParts rozkladá pattern na host, path, scheme a
wildcardHost.
Pri prekrývajúcich sa pravidlách vyhráva najšpecifickejšie:
compareSiteSpecificity hodnotí presný host nad wildcard hostom, dlhší
doslovný host, doslovnejšiu cestu a nakoniec deterministický ASCII tiebreak;
selectMostSpecific vracia najšpecifickejšie povolené pravidlo zhodné s URL.
Pokrytie udelených originov je samostatný a zámerne širší matcher.
permissions.getAll() môže vracať granty v plnej webextension gramatike
(<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*),
ktorú reštriktívny MATCH_PATTERN neprijíma. parseGrantedOrigin ich rozloží
do GrantedOriginParts a originCovers hlási, či grant pokrýva pravidlo
match. matchAllowsOriginFallback zasa hlási, či je cesta pravidla presne
ORIGIN_FALLBACK_PATH (/*), teda bezpečný prienik obmedzení Chromium a
Gecko/Firefox 128 pri fallbacku nepriehľadných originov.
Dynamická registrácia
packages/webext/src/contentRegistration.ts registruje jeden dynamický content
script pre každé používateľom pridané pravidlo. Built-in pravidlá obsluhujú
statické content_scripts a nikdy sa dynamicky neregistrujú.
desiredContentScripts filtruje config.sites na nebuilt-in, povolené
pravidlá s udeleným originom, každé mapuje na RegisteredContentScript
so stabilným id (contentScriptId, FNV-1a hash match patternu) a nastaví
matchOriginAsFallback: true iba vtedy, keď matchAllowsOriginFallback(rule.match)
platí. Tým sa zabráni tomu, aby jedno pravidlo s nevyhovujúcou cestou zhodilo
celú dávku registrácií; takýto skript sa zaregistruje bez pokrytia pop-out okien.
reconcileContentScriptsOnce načíta udelené originy, uloženú konfiguráciu a
živé registrácie a potom ich zosúladí cez diff podľa vlastností:
sameRegistration normalizuje požadovaný deskriptor aj hodnotu vrátenú API
na rovnakú podobu, vrátane skutočných predvolených hodnôt. Keďže neexistuje
updateContentScripts, zmenený skript sa odregistruje a zaregistruje znovu.
reconcileContentScripts to obaluje in-flight promise (inFlight), aby sa dve
takmer súčasné spustenia nesúťažili. registerContentReconciliation pripája
triggery permissions.onAdded/onRemoved a onConfigChanged a vykoná jedno
zosúladenie pri štarte; importuje sa len z Chrome a Firefox background entry.
Tok voliteľného oprávnenia
Statické manifesty deklarujú optional_host_permissions
(http://*/*, https://*/*), aby mohol používateľ za behu udeliť ľubovoľný
ďalší webový host. packages/webext/src/options/options.ts addSite
validuje celé kandidátne pravidlo (match aj content root) cez
normaliseSiteRule ešte pred žiadosťou o oprávnenie, odvádza host origin cez
matchOrigin a volá browser.permissions.request zo samotného click gesta
na tlačidle Add. Až po udelení oprávnenia pridá riadok builtin:false
a uloží ho cez kanonickú zapisovaciu cestu.
Konfigurácia, úložisko a kanonický vzor
Jediný validačný lievik
normaliseConfig v packages/core/src/config.ts je jediná kanonická cesta
čítania: každé pole pretypuje, ohraničí a pri neplatnej hodnote sa vráti k
predvolenému stavu; nikdy nevyhadzuje výnimku. Stavia na validátoroch
normaliseBoolean, normaliseRenderMode, normaliseMaxLength,
normaliseIgnoreHosts, normaliseMatchColour, normaliseMismatchColour,
normaliseCssSize, normaliseFontWeight, normaliseContentRoot,
normaliseMatchPattern a normaliseSites. normaliseSites zahodí neplatné
pravidlá, deduplikuje podľa match, vynúti builtin: true pre pravidlá
zhodné s built-in matchmi, znovu doplní chýbajúce built-in pravidlá a zoradí
ich alfanumericky podľa match. configFingerprint hashuje iba polia, ktoré
ovplyvňujú render.
Úložisko
packages/webext/src/storage.ts obaluje WebExtension storage API.
configArea používa browser.storage.sync, keď je dostupné, a inak padá na
browser.storage.local. configAreaName hlási, ktorá oblasť je aktívna.
getConfig, setConfig a onConfigChanged vždy vedú surovú hodnotu cez
normaliseConfig, takže ani poškodené úložisko nevráti neplatný Config.
onConfigChanged navyše ignoruje udalosti z neaktívnej storage oblasti a zmeny
nesúvisiacich kľúčov.
Oba doplnky znovu používajú rovnaký vzor read → normaliseConfig → return nad
vlastnými host úložiskami. Outlook task pane používa
extensions/outlook/src/roamingStorage.ts, Gmail doplnok
packages/gmail/src/propertiesStorage.ts. Obe vrstvy exponujú getConfig a
setConfig aj card-relevant accessor metódy a chýbajúci host surface ošetria
samostatnou chybou. Gmail homepage trigger renderuje CardService settings card z
getConfig, onSaveSettings mieša parsované ignoreHosts/highlightMismatch
do aktuálnej konfigurácie a perzistuje ich cez setConfig; contextual trigger
potom bezpečne číta tieto polia s fallbackom na defaulty.
Vzor kanonickej konfigurácie a cielených aktualizácií
Tri zapisovacie cesty najprv načítajú kanonickú konfiguráciu, zmenia presne jednu vec a zapíšu ju späť — nikdy neuložia nerozpracované úpravy formulára a nikdy celý formulár zbytočne neprekresľujú:
removeSitevoptions.tsprečítagetConfig, zapíšesetConfigs poľomsites, z ktorého je odstránené iba mazané pravidlo, a až potom upraví DOM a prípadne vykonápermissions.remove.addSitenajprv validuje, potom žiada o oprávnenie, pridá riadok a nakoniec volásaveOptions.toggleEnabledpri okamžitom prepínači Enable mení v uloženej konfigurácii ibaenableda zapisuje ho bez re-renderu; pri zlyhaní checkbox vráti.
Rovnaký vzor používa aj toolbar action toggleEnabled
(packages/webext/src/toolbarAction.ts) z background strany.
Build pre jednotlivé enginy a kontrakt manifestu
tooling/build.mjs riadi jeden spoločný esbuild build pre všetky ciele
WebExtension. Deskriptor TARGETS pomenúva zdrojový target každého cieľa:
Chrome, Firefox a Thunderbird majú vlastné src/; Edge, Opera a Safari
deklarujú chrome ako zdroj; Outlook add-in je samostatný zdroj.
ACTIVE_TARGETS (Chrome, Edge, Firefox, Opera a Thunderbird — Safari a Outlook
nie) sú ciele, ktoré stavia --all/--package. Safari a Outlook sa stavajú
len pri explicitnom uvedení a Gmail add-on sa balí oddelene cez
make build-gmail. Gmail bundle cieli na Apps Script V8 runtime, ktorý nemá
ES moduly ani natívne URL: zostaví sa ako ESM, odstráni sa jeho záverečné
export {…} a pribalí sa malý polyfill URL, aby spoločné jadro vedelo
server-side vyhodnocovať odkazy.
buildTarget čistí dist/<target>/, kopíruje cieľové manifest.json a
icons/, bundluje každý zdrojový skript ako IIFE a zdieľanú stránku options ako
ESM a kopíruje options.html/options.css z packages/webext/src/options/.
webextension-polyfill sa balí do každého skriptu.
tooling/version.mjs pečiatkuje verzie: z root package.json berie MAJOR.MINOR
a pridáva autoinkrementované BUILD číslo do všetkých target manifestov.
Väčšina targetov nesie jeden manifest.json; Outlook add-in nesie dva
verzionované manifesty (manifest.json a manifest.xml) a oba sa pečiatkujú,
rovnako aj scaffold _template. Root package.json zostáva na rovnakej verzii,
ale len v platnom semver tvare. tooling/icons.mjs (spúšťané cez make icons)
rastorizuje jediný vektorový zdroj assets/icon.svg na
icons/icon48.png a icons/icon128.png pre každý target.
Kontrakt manifestu — teda presné povinné polia pre každý engine — je jediným
zdrojom pravdy v extensions/<engine>/test/manifest.smoke.test.mjs. Napríklad
extensions/chrome/test/manifest.smoke.test.mjs cez assertManifestShape
potvrdzuje tvar manifestu a že každý odkazovaný súbor v builde naozaj existuje.
Podľa pravidla deduplikácie sa tu preto presný zoznam polí znovu nevymenúva;
pozrite si smoke testy a poznámku „Manifest contract“ v
manual test plan.
Internacionalizácia (i18n)
Reveal URLs je lokalizované do mnohých jazykov popri angličtine. Zoznam je
single-source v packages/core/src/locales.json (SUPPORTED_LOCALES,
native names a default), ktorý čítajú balíky rozšírenia aj plain-Node build
webu. Každý neanglický reťazec je strojovo preložený a čaká na ľudskú revíziu;
značenie pôvodu sa líši podľa formátu.
Existujú tri nezávislé lokalizačné povrchy, zámerne bez vzájomného prepojenia, a každý je rozdelený na build-time a runtime časť.
- Stránka nastavení (AD-1). Dodáva sa so STATICKÝM anglickým markupom; každý
prekladateľný uzol nesie
data-i18n="<key>"alebodata-i18n-<attr>="<key>". Za behu je aplikačnou vrstvoupackages/webext/src/options/locale.ts: cezruntime.getURLnačíta zabalené_locales/<code>/messages.json, prepíše označené uzly iba ceztextContenta nastaví<html lang>. Stránka je plne lokalizovaná, vrátane JS-generovaných labelov riadkov stránok a truly dynamic stringov ako status line, add-site feedback a version line. Anglický fallback bundlovaný dooptions.jszaručuje, že sa pred zlyhaním fetchu nikdy nezobrazia len holé kľúče správ. Link na manuál sleduje aktívny locale k jeho lokalizovanému názvu stránky (nl/handleiding.html) a voľba jazyka sa ukladá poduiLocalevstorage.local. - Chrome webu (AD-2). Generátor statického webu
tooling/site.mjsserver-side vyrenderuje všetky chrome uzly v angličtine a označí ichdata-i18n, pričom ponechá<html lang="en-GB">. Browserový loadersite/scripts/i18n.mjspotom podľa uloženéholocalStoragevýberu alebo podporovaného jazyka znavigator.languagesprepne chrome do jazyka návštevníka načítanímsite/i18n/<code>.json. Na rozdiel od aplikačnej vrstvy options vymieňa hodnoty cezinnerHTML, pretože texty chrome webu obsahujú dôveryhodný inline markup ako<code>a<a>. - Dokumentačné stránky (AD-8/AD-9). Dokumenty sa rendrujú PRE KAŽDÝ locale
pri builde, nie runtime výmenou. Anglické docs zostávajú na top-level cestách;
každý iný locale dostane kópiu pod
<code>/a pod názvom preloženým cezpackages/core/src/site-page-names.json. Pri chýbajúcom preklade sa použije anglický zdroj abuildSiteto zaloguje. Build navyše vytvára tenkýnoindexredirect na starých anglických cestách akonl/manual.html. - Lokalizácia interných odkazov (Finding 2). Vybraný jazyk sa prenáša aj pri
navigácii v dokumentácii. Každý interný odkaz na dokumentačnú stránku nesie
data-doc-link="<page>"a jeho href už bez JavaScriptu mieri na správny lokalizovaný variant. Runtime loaderrewriteDocLinksnásledne prepisuje všetky interné odkazy na aktívny locale, zachováva#hashaj?querya externé odkazy necháva bez zmeny.
Potlačenie „Aktívnych stránok“ v poštovom klientovi (AD-3)
Mail-client target (dnes Thunderbird) už vidí každú vykreslenú správu, takže
editor „Active sites“ je nadbytočný a odstraňuje sa. Rozhodnutie nesie flag
mailClient: true v tooling/build.mjs na TARGETS.thunderbird a implementuje
sa v dvoch poloviciach:
- Build-time.
copyAssetssa riadidescriptorFor(target).mailClient: pre poštového klienta vykoná čistú transformáciuremoveSitesSection(html), ktorá vyreže celý<section class="sites">. - Runtime. Bundlovaný radič
packages/webext/src/options/options.tstoleruje chýbajúcu sekciu.renderSites,readSites,applyConfigareadFormpri chýbajúcom#sites-listskončia no-op. Keďže vynechaniesitesby inak dovolilonormaliseConfigznovu zasiať built-in defaults,saveOptionspri absentujúcej sekcii zachová uloženésites. Runtime guardguardMailClientSitessekciu odstráni, ak tam ešte je, a deteguje mail client podľa prítomnostimessages,messageDisplayaleboscripting.messageDisplayna injektovanombrowser.
Bezpečnostný a súkromný profil
- Žiadna sieť ani exfiltrácia. Žiadny modul nevykonáva
fetch,XHRani iné sieťové volania; rozšírenie číta iba uloženú konfiguráciu a DOM stránky. - Iba safe DOM. Anotácia aj stylesheet používajú výlučne
createElement,textContenta typedstyle.*properties, nikdy nieinnerHTML/insertAdjacentHTML/eval. - Každý vstup sa normalizuje. Každé čítanie aj zapisovanie vedie cez
normaliseConfig; farby prechádzajúisSafeColour; match patterny prechádzajúMATCH_PATTERN; selektorycontentRootsú ohraničené. - Najmenšie potrebné oprávnenia. Built-in webmail originy sú pevné
host_permissions; ďalšie hosty sú opt-in cezoptional_host_permissionsapermissions.requestviazané na používateľské gesto. - Zber dát v Gecko. Manifesty Firefoxu a Thunderbirdu deklarujú
browser_specific_settings.gecko.data_collection_permissions.required: ["none"]. - Licencia. Projekt je AGPL-3.0-only (
LICENSE,package.json).
Testovanie
- Vitest unit tests pokrývajú oba balíky:
packages/core/test/*(konfigurácia, kontrast, host mismatch, link processor, styles a purity guard) apackages/webext/test/*(content, storage, options, registration, toolbar, message display a end-to-end test vlastných stránok). Každý balík spúšťavitest run. - Cucumber BDD scenáre žijú v
features/a cez verné test doubles vfeatures/support/webext.tsafeatures/support/world.tsriadia skutočné moduly@reveal-urls/webext. - Smoke testy manifestov pre targety
(
extensions/<engine>/test/manifest.smoke.test.mjs, nadnode:test) overujú presný tvar manifestu a to, že build rieši všetky odkazované súbory.tooling/test/pridáva testy buildu, verzií a definition-of-done. - CI (
.github/workflows/ci.yml) blokuje každý push a pull request cezmake test,make bddamake lint. Verziovanie a balenie sú zámerne oddelené do tag-triggered.github/workflows/release.yml.