Reveal URLs — Arhitektura
Ta dokument opisuje, kako je razširitev Reveal URLs zgrajena in kako deluje
njena osrednja logika. Namenjen je sodelavcem in tehnično usmerjenim
bralcem. Skozi besedilo navaja dejanske datoteke in simbole (v obliki
path symbol), da je mogoče vsako trditev preveriti v izvorni kodi.
Uporabniški pogled je v manual; koraki ročnega preverjanja so v
manual test plan.
Pregled
Reveal URLs je ena sama MV3 WebExtension, zgrajena za več pogonov — Chrome,
Edge, Opera, Firefox in Thunderbird — iz enega deljenega, čistega jedra
kode. (Safari je za zdaj odložen: ima manifest in ga je mogoče zgraditi, če ga
izrecno navedete, vendar ni del privzete gradnje niti CI.) Razširitev ob
povezavi v izrisanem e-sporočilu pokaže ciljni URL in označi povezavo, katere
vidno besedilo poimenuje drugo registrabilno domeno kot njen href — klasičen
znak phishinga.
Repozitorij je pnpm/TypeScript monorepo (pnpm-workspace.yaml,
package.json z "private": true). Pakiranje vodi en sam skript na osnovi
esbuild, tooling/build.mjs, ki prevede skupne pakete in tanke vstopne točke
posameznih pogonov v naložljiv imenik dist/<target>/.
webextension-polyfill je vključen v vsak skript, namesto da bi se zanašali
na globalni runtime objekt, zato isti izvorni kôd brez sprememb deluje v
Chromiumu in Gecko.
Dva natívna dodatka za e-pošto razširjata isto jedro zaznavanja na površine,
do katerih WebExtension ne more: Outlook Add-in (naloga Office.js,
dosegljiva v Outlook on the web/Windows/Mac/iOS/Android in Outlook.com) in
Gmail Add-on (Apps Script CardService za Gmail na spletu/Android/iOS).
Oba ponovno uporabljata samo čisto analizo — nov modul
packages/core/src/findings.ts (analyseAnchors/analyseHtml, platformno
nevtralen model Finding[]) ponovno uporablja obstoječo logiko
hostMismatch in izpeljavo registrabilnih domen prek tldts. Razlikujeta se
samo prilagojevalnik gostitelja (kako se telo sporočila prebere in razčleni)
in predstavitev. Outlookovo opravilo telo razčleni z lastnim DOMParser in
teče na strani odjemalca; Gmailov dodatek ga razčleni z
node-html-parser in teče na strežniški infrastrukturi Google Apps Script
V8. Ker noben okvir ne more spreminjati izrisanega DOM-a prebranega sporočila
(setAsync/prependAsync v Office delujeta le pri pisanju, CardService pa
upodablja kartice, ne HTML sporočila), oba dodatka prikažeta panel/kartico
ugotovitev namesto vstavljenih pripisov v samo besedilo. Obstoječa
DOM-mutacijska pot iz linkProcessor.ts in REVEAL_URLS_CSS se zato v teh
dodatkih ne uporabita. Relativni href se razreši le glede na zaupanja vreden
<base href> znotraj e-sporočila; sicer se preskoči in nikoli ne razrešuje
glede na izvor predala ali ponudnika.
Odgovornosti v kodi so jasno ločene:
packages/corevsebuje čisto logiko — brez browser API-jev, brez shranjevanja in brez sinkov, ki bi sprejemali markup. Enostavno jo je enotsko testirati v navadnem JavaScriptu in predstavlja en sam vir resnice za pripise povezav, preverjanje konfiguracije, ujemanje match patternov, primerjavo gostiteljev in barvanje z upoštevanjem kontrasta.packages/webextokoli tega jedra ovije WebExtension API-je: življenjski cikel content skript, dinamično registracijo, uporabniški vmesnik nastavitev, background wiring, dejanje v orodni vrstici in pot prikaza sporočil v Thunderbirdu.extensions/<engine>vsebuje tanke vstopne točke za posamezen pogon in njegovmanifest.json.toolingvsebuje skripte za gradnjo, pečatenje različic in generiranje ikon.featuresvsebuje scenarije Cucumber BDD in njihove test doubles.
Razporeditev repozitorija
packages/core — čisto jedro
Noben modul tukaj ne uvaža API-ja razširitve
(browser.*/chrome.*/messenger.*) in ne uporablja DOM sinkov, ki
sprejemajo niz kot markup (innerHTML/insertAdjacentHTML). Ta pogodba je
mehansko uveljavljena v packages/core/test/purity.test.ts, ki pregleda vse
src/**/*.ts, odstrani telesa komentarjev z lastnim stripComments in nato
preveri, da v nobenem modulu ne ustrezata niti EXTENSION_API_PATTERN niti
UNSAFE_DOM_PATTERN. Javni vmesnik se ponovno izvozi iz
packages/core/src/index.ts. Edina runtime odvisnost je tldts, potreben za
primerjavo domen z upoštevanjem public suffixov.
Moduli so:
packages/core/src/config.ts— shemaConfig/SiteRule, privzete vrednosti in enoten validacijski lijaknormaliseConfig.packages/core/src/matchPattern.ts— slovnica match patternov, matcher, razvrščanje po specifičnosti in pokritje odobrenih izvorov.packages/core/src/linkProcessor.ts— anotator na ravni dokumenta.packages/core/src/hostMismatch.ts— preverjanje neujemanja registrabilne domene.packages/core/src/styles.tsinpackages/core/src/contrast.ts— vstavljiv stylesheet ter barvanje z upoštevanjem kontrasta.packages/core/src/safeColour.ts— predikat varne barveisSafeColour.
packages/webext — ovoji brskalniškega API-ja
Moduli tukaj SMEJO uporabljati browser.* in so edino mesto, kjer se
dotaknemo WebExtension API-jev. Javni vmesnik se ponovno izvozi iz
packages/webext/src/index.ts. Moduli so: content.ts (življenjski cikel
content skript), contentRegistration.ts (dinamična registracija),
storage.ts (vztrajnost konfiguracije), background.ts (obnašanje ob
namestitvi), toolbarAction.ts (preklop + badge), messageDisplay.ts in
messageDisplayBackground.ts (Thunderbird) ter options/options.ts
(krmilnik strani z nastavitvami).
extensions/<engine> — tanke vstopne točke in manifesti
Vsak pogon ima svoj manifest.json in icons/. Chrome, Firefox in
Thunderbird imajo dodatno še src/ z vstopnimi točkami; Edge, Opera in Safari
imajo le manifest in ikone in PONOVNO uporabljajo Chromov src/
(določeno v deskriptorju gradnje spodaj). Vstopne točke so namenoma zelo
majhne: na primer extensions/chrome/src/content.ts vsebuje le
void createContentController().bootstrap();, medtem ko
extensions/chrome/src/background.ts kliče registerBackground,
registerContentReconciliation in registerToolbarToggle. Firefoxova
background vstopna točka je enaka Chromovi; Thunderbirdova
(extensions/thunderbird/src/background.ts) namesto tega kliče
registerBackground in registerMessageDisplay, ker content skript nima.
extensions/_template je pripravljena Chromium osnova za kopiranje, ne build
target.
tooling, features in smoke testi
tooling vsebuje build.mjs, version.mjs in icons.mjs. features
vsebuje scenarije Gherkin, njihove step definitions in zveste test doubles v
features/support/webext.ts in features/support/world.ts. Vsak pogon ima še
extensions/<engine>/test/manifest.smoke.test.mjs
(_template ima template.smoke.test.mjs) — ti testi predstavljajo
avtoritativno pogodbo manifesta (glej spodaj razdelek »Gradnja po pogonu in
pogodba manifesta«).
Potek razkrivanja
Pot vsebine se začne v vstopni točki pogona, na primer
extensions/chrome/src/content.ts, ki kliče
createContentController().bootstrap() iz packages/webext/src/content.ts.
Bootstrap
createContentController zažene bootstrap enkrat na okvir:
- Okvir si sinhrono prilasti še pred vsakim
awaitprekclaimBootstrap, ki nawindowtega okvirja prebere in nastavi zastavicoBOOTSTRAP_MARKER. Statičen vnos vcontent_scriptsin prekrivajoči se dinamični uporabniški skript lahko oba v isti okvir vbrizgatacontent.js, zato je skupniwindowv isolated world prava zaščita »samo enkrat«, zgodnja prilastitev pa zagotovi, da dve skoraj sočasni injekciji ne uspeta obe. - Najprej naloži shranjeno konfiguracijo prek
loadConfigWithRetry(BOOTSTRAP_CONFIG_RETRIESdodatnih poskusov ob prehodni napaki) in med ponovitvami drži marker, da se prekrivajoča injekcija, ki je že končala kot no-op, ne izgubi. Šele ko je branje uspešno, vstavi stylesheet (injectStyles, kiREVEAL_URLS_CSSdodeli prektextContent), tako da neuspešno branje ne doda nobenega<style>in ponovni poskus ne more podvojiti vnosa. - Če nalaganje konfiguracije odpove pri vseh poskusih,
releaseBootstrapmarker vrne nazaj (samo tukaj, še preden se začne označevanje), da lahko poznejša reinjekcija poskusi znova; ko jestartenkrat stekel, mora marker ostati, ker nov anotator ne bi posedoval vozlišč iz prejšnjega prehoda. - Ko velja
config.enabled, se pokličestart; vedno pa se registrira tudi poslušaleconConfigChanged, ki konfiguracijo znova uporabi in pokrije prehod disabled → enabled.
Določanje pravila aktivnega mesta
start (in applyConfig) kliče resolveSiteRule, da odloči, ali in kje se bo
označevalo. resolveSiteRule najprej poskusi z lastno lokacijo dokumenta prek
resolveForHref, ki filtrira config.sites na omogočena pravila in delegira
na selectMostSpecific, tako da zmaga najnatančnejše ujemanje. Ko lastna
lokacija ne zadene ničesar, izvor dokumenta pa je neprozoren ali podedovan —
kar določa hasOpaqueOrigin, true le za about:blank/about:srcdoc
(podedovano prek INHERITED_ORIGIN_URLS) ali blob:/data:
(OPAQUE_ORIGIN_SCHEMES) — se po vrsti preizkusijo TOP okvir (readTopHref),
okno odpiralca (readOpenerHref) in nazadnje document.referrer
(readReferrer). Vsako zunanje branje je zaščiteno pred cross-origin dostopom
in kandidata prispeva le, če je branje dejansko mogoče. Običajen dokument brez
ujemanja ostane avtoritativen in nikoli ne podeduje pravila iz okoliščin.
Omejitev na koren vsebine in opazovanje sprememb
start shrani contentRoot iz izbranega pravila, ustvari anotator prek
createAnnotator(config) in obdela vsak koren iz
selectRoots(doc, contentRoot). Nato opazuje doc.body s
OBSERVER_OPTIONS ({ childList: true, subtree: true }).
Observer mutacije pošilja v processMutations, ki vsako mutacijo najprej
omeji na content root: cilj ponovno obdela le, če je Element znotraj root-a,
vsak dodani element pa posreduje v processAddedNode, ki anotira vozlišče,
če je znotraj root-a, če je samo root ali če root vsebuje. Tako se ujame
dogajanje znotraj telesa sporočila in tudi poznejša montaža novega telesa v
SPA, medtem ko chrome aplikacije zunaj root-ov ostane nedotaknjen. Null
anotator nima stranskih učinkov.
applyConfig uskladi živo spremembo konfiguracije: če ni več ujemajočega
pravila ali če je razširitev izklopljena, se vse podre; če se spremeni
contentRoot, se izvede ponovni zagon (revert + novo opazovanje); sicer pa se
konfiguracija preteče na mestu z annotator.setConfig in
annotator.process brez ponovnega zagona observerja.
Označevanje posamezne povezave
Anotator živi v packages/core/src/linkProcessor.ts. createAnnotator vrne
Annotator, katerega stanje živi v closure in ni vidno strani: močna Map,
ključa po identiteti anchorja, token data-ru, ustvarjen z generateToken
(crypto.getRandomValues), in trenutna konfiguracija. Metoda process(root):
- Odstrani vnose za anchore, ki niso več povezani.
- Zbere kandidatne anchore prek
collectAnchors. - Razreši cilj vsakega anchorja prek
resolveAnnotatableUrl, ki vrnenullza prazen ali notranji href, neparsabilen href, sheme razenhttp:/https:in gostitelje zignoreHosts. - Uporabi idempotentno hitro pot, kadar
isAnnotationIntactpotrdi, da se render fingerprint ni spremenil in da so ustvarjena vozlišča še vedno povezana in na pravem mestu; sicer star pripis odstrani in ga izriše znova. - Izračuna neujemanje prek
hostMismatchna očiščenem besedilu anchorja, upoštevashowOnlyOnMismatchin pokličeannotateAnchor.
annotateAnchor ustvarja samo varen DOM. V načinu "inline" na začetek vstavi
<span class="reveal-urls-url"> z URL-jem prek textContent
(s puščico in U+00A0, krajšano s truncateUrl) ter za njim <br>, pri čemer
otroci same povezave ostanejo nedotaknjeni. Povezani span nato prek
applyContrastBackdrop dobi kontrastno ozadje. V načinu "title" shrani
izvirni title (TitleSave), ga prepiše z URL-jem in pri poudarjenem
neujemanju doda sosednji badge
<span class="reveal-urls-warn"> z besedilom REVEAL_URLS_WARN_LABEL.
Vsako ustvarjeno vozlišče je označeno z data-ru="<token>", vsak vnos pa
nosi configFingerprint, da sprememba render konfiguracije prisili nov izris.
Barve in tipografske prilagoditve se uporabijo prek typed
element.style.* properties (applyColour/applyFontOverrides), nikoli z
interpolacijo v CSS niz. revertOwned odstrani ustvarjena vozlišča po
referenci in obnovi shranjeni title; revertAll to stori za vse anchorje in
počisti register.
truncateUrl dela po Unicode code points ([...displayHref]), zato nikoli ne
pretrga surrogate para; ohrani shemo in gostitelja, preostanek pa skrajša z
URL_ELLIPSIS.
Neujemanje gostitelja
packages/core/src/hostMismatch.ts hostMismatch primerja vidno besedilo
povezave z njenim href po registrabilni domeni, ne po surovem hostname.
Poštena poddomena zato ni označena, medtem ko je posnemovalna. extractHostCandidates
razbije besedilo in vsak token zmanjša prek hostCandidate, ki zahteva piko in
prek tldts prepoznan ICANN public suffix. Gostitelj iz href-a in kandidati se
zmanjšajo na registrabilno domeno prek tldts.getDomain; do neujemanja pride,
če se kateri koli kandidat razlikuje. Funkcija nikoli ne meče izjem.
Slogi in kontrast
packages/core/src/styles.ts vsebuje vstavljivi REVEAL_URLS_CSS
(fiksna postavitev, teža, oblika in povsem neprosojno belo ozadje čipa) ter
runtime helperje applyColour, applyFontOverrides in
applyContrastBackdrop. applyContrastBackdrop prek getComputedStyle prebere
izračunano barvo besedila in prvo neprosojno ozadje pri hoji od samega elementa
navzgor; ko packages/core/src/contrast.ts needsWhiteBackdrop ugotovi, da
besedilo ne dosega WCAG AA (CONTRAST_THRESHOLD) in da bela res pomaga,
nastavi style.backgroundColor = "white". contrast.ts ponuja parseColour,
relativeLuminance in contrastRatio; zna razčleniti oblike rgb()/rgba()/hex,
ki jih vrača getComputedStyle, popolnoma prozorno barvo pa obravnava kot
»ni najdeno«.
Nastavljivi gostitelji in omejevanje vsebine
SiteRule (packages/core/src/config.ts) določa, KJE pripisovanje teče
(match, allFrames) in KATERI vsebinski vsebnik ga omejuje (contentRoot),
skupaj z enabled in oznako builtin. Vgrajeni DEFAULT_SITES pokrivajo
Gmail, Proton (z allFrames) in oba Outlook gostitelja. Config združuje
globalna stikala, barve, tipografske prilagoditve in polje sites.
Mehanizem vzorcev ujemanja
packages/core/src/matchPattern.ts vsebuje namenoma omejeno slovnico
MATCH_PATTERN (samo http/https, opcijski začetni *. wildcard za gostitelja,
glob za pot; brez porta, brez sheme *, brez <all_urls>). matchesPattern
primerja URL z validiranim vzorcem in pot prepusti pathGlobMatches.
parsePatternParts razdeli vzorec na host, path, scheme in
wildcardHost.
Pri prekrivajočih se pravilih zmaga »najbolj specifično«:
compareSiteSpecificity razvršča po natančnem gostitelju pred wildcard
gostiteljem, po daljšem dobesednem gostitelju, po bolj dobesedni poti in nato
po determinističnem ASCII razreševalcu izenačenj; selectMostSpecific vrne
najbolj specifično omogočeno pravilo, ki se ujema z URL-jem.
Pokritje odobrenih izvorov je ločen in namerno širši matcher.
permissions.getAll() lahko vrne odobritve v polni webextension slovnici
(<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*),
ki jih omejeni MATCH_PATTERN ne sprejme. parseGrantedOrigin jih razčleni v
GrantedOriginParts, originCovers pa pove, ali odobren izvor pokriva pravilo
match. matchAllowsOriginFallback pove, ali je pot pravila točno
ORIGIN_FALLBACK_PATH (/*), torej varen presek omejitev Chromiuma in
Gecko/Firefox 128 pri fallbacku neprozornih izvorov.
Dinamična registracija
packages/webext/src/contentRegistration.ts registrira en dinamičen content
script za vsako uporabniško dodano pravilo. Vgrajena pravila obdelajo statični
content_scripts in se nikoli ne registrirajo dinamično. desiredContentScripts
filtrira config.sites na nevgrajena, omogočena pravila z odobrenim izvorom,
vsako preslika v RegisteredContentScript s stabilnim id
(contentScriptId, FNV-1a hash match vzorca) in nastavi
matchOriginAsFallback: true le tedaj, ko velja
matchAllowsOriginFallback(rule.match). Tako pravilo z neustrezno potjo ne more
podreti celotne serije registracij; v takem primeru se skript registrira brez
pokritja pop-out oken.
reconcileContentScriptsOnce prebere odobrene izvore, shranjeno konfiguracijo
in žive registracije ter jih uskladi prek diff-a po lastnostih:
sameRegistration želeni deskriptor in stanje, vrnjeno iz API-ja, normalizira
na isto obliko, vključno z dejanskimi privzetimi vrednostmi. Ker ne obstaja
updateContentScripts, se spremenjen skript odregistrira in registrira znova.
reconcileContentScripts to ovije z in-flight promise (inFlight), da se dve
skoraj sočasni sprožitvi ne stepeta. registerContentReconciliation priključi
triggery permissions.onAdded/onRemoved in onConfigChanged ter ob zagonu
izvede eno uskladitev; uvožen je samo v Chrome in Firefox background entry.
Tok izrecnega dovoljenja
Statični manifesti deklarirajo optional_host_permissions
(http://*/*, https://*/*), da lahko uporabnik med delovanjem odobri
poljuben dodatni spletni gostitelj. packages/webext/src/options/options.ts
addSite validira CELOTNO kandidatno pravilo (match in content root) prek
normaliseSiteRule, še preden zahteva dovoljenje, izpelje origin gostitelja
prek matchOrigin in pokliče browser.permissions.request znotraj klika na
gumb Add. Šele po odobritvi doda vrstico builtin:false in jo shrani po
kanonični poti zapisovanja.
Konfiguracija, shramba in kanonični vzorec
Enotni validacijski lijak
normaliseConfig v packages/core/src/config.ts je enotna kanonična pot
branja: vsako polje pretvori, omeji in ob neveljavni vrednosti vrne privzeto
stanje; nikoli ne meče izjem. Gradi na validacijah normaliseBoolean,
normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts,
normaliseMatchColour, normaliseMismatchColour, normaliseCssSize,
normaliseFontWeight, normaliseContentRoot, normaliseMatchPattern in
normaliseSites. normaliseSites zavrže neveljavna pravila, deduplicira po
match, vsili builtin: true pravilom, ki se ujemajo z vgrajenimi, ponovno
doda manjkajoča vgrajena pravila in vse razvrsti alfanumerično po match.
configFingerprint zgoščuje samo polja, ki vplivajo na izris.
Shramba
packages/webext/src/storage.ts ovija WebExtension storage API. configArea
uporablja browser.storage.sync, kjer je na voljo, sicer pa pade nazaj na
browser.storage.local. configAreaName pove, katero območje je aktivno.
getConfig, setConfig in onConfigChanged vedno vodijo surovo vrednost skozi
normaliseConfig, zato niti poškodovana shramba ne vrne neveljavnega Config.
onConfigChanged dodatno ignorira dogodke iz neaktivnega storage območja in
spremembe nepovezanih ključev.
Oba dodatka ponovno uporabita isti vzorec read → normaliseConfig → return nad
lastnima gostiteljskima shrambama. Outlook task pane uporablja
extensions/outlook/src/roamingStorage.ts, Gmailov dodatek pa
packages/gmail/src/propertiesStorage.ts. Obe plasti izpostavljata getConfig
in setConfig ter dodatne accessorje za kartice, manjkajoč host surface pa
obravnavata z namensko napako. Gmail homepage trigger iz getConfig sestavi
CardService settings card, onSaveSettings pa prebrane
ignoreHosts/highlightMismatch zlije v trenutno konfiguracijo in jih shrani
prek setConfig; contextual trigger potem ta polja obrambno prebere z vrnitvijo
na privzete vrednosti.
Vzorec kanonične konfiguracije in ciljnih posodobitev
Tri poti zapisovanja najprej preberejo kanonično konfiguracijo, spremenijo točno eno stvar in jo zapišejo nazaj — nikoli ne shranijo nezaključenih urejanj obrazca in nikoli po nepotrebnem ne izrišejo celotnega obrazca:
removeSitevoptions.tspreberegetConfig, zapišesetConfigs poljemsites, iz katerega je odstranjeno samo izbrisano pravilo, nato pa spremeni DOM in po potrebi pokličepermissions.remove.addSitenajprej validira, nato zahteva dovoljenje, doda vrstico in na koncu pokličesaveOptions.toggleEnabledpri hitrem stikalu Enable spremeni samoenabledv shranjeni konfiguraciji in jo zapiše brez ponovnega izrisa; ob napaki checkbox vrne.
Enak vzorec z background strani uporablja tudi toolbar action toggleEnabled
(packages/webext/src/toolbarAction.ts).
Gradnja po pogonu in pogodba manifesta
tooling/build.mjs vodi enoten esbuild build za vse cilje WebExtension.
Deskriptor TARGETS določa izvorni target za vsak cilj: Chrome, Firefox in
Thunderbird imajo lasten src/; Edge, Opera in Safari kot vir uporabijo
chrome; Outlook add-in je ločen vir. ACTIVE_TARGETS
(Chrome, Edge, Firefox, Opera in Thunderbird — brez Safari in Outlooka) so
cili, ki jih gradi --all/--package. Safari in Outlook se zgradita le ob
izrecni navedbi, Gmail add-on pa se pakira ločeno z make build-gmail.
Gmailov bundle cilja Apps Script V8 runtime, ki nima ES modulov niti
native URL: zgrajen je kot ESM, odstrani se njegov zaključni export {…},
priloži pa se majhen polyfill URL, da lahko skupno jedro strežniško
razrešuje povezave.
buildTarget počisti dist/<target>/, dobesedno prekopira ciljni
manifest.json in icons/, vsak izvorni skript zgradi kot IIFE, skupno stran
options pa kot ESM ter prekopira options.html/options.css iz
packages/webext/src/options/. webextension-polyfill je vključen v vsak
skript.
tooling/version.mjs pečati različice: iz root package.json vzame MAJOR.MINOR
in doda samodejno povečevano BUILD številko v vse target manifeste. Večina
targetov ima en manifest.json; Outlook add-in ima dva manifestna zapisa
(manifest.json in manifest.xml) in oba se pečatita, enako velja za
scaffold _template. Root package.json ostane na isti različici, vendar v
veljavnem semver zapisu. tooling/icons.mjs (zagnan z make icons) rasterizira
edini vektorski vir assets/icon.svg v icons/icon48.png in
icons/icon128.png za vsak target.
Pogodba manifesta — torej natančno zahtevana polja po posameznem pogonu — je
en sam vir resnice v extensions/<engine>/test/manifest.smoke.test.mjs. Na
primer extensions/chrome/test/manifest.smoke.test.mjs prek
assertManifestShape potrjuje obliko manifesta in da vsak referenciran objekt v
izhodnem buildu res obstaja. Zaradi pravila deduplikacije se zato tukaj točen
seznam polj ne našteva znova; glejte smoke teste in opombo »Manifest contract« v
manual test plan.
Internacionalizacija (i18n)
Reveal URLs je lokaliziran v številne jezike poleg angleščine. Nabor je
single-source v packages/core/src/locales.json (SUPPORTED_LOCALES,
native names in default), ki ga berejo tako paketi razširitve kot tudi
plain-Node build spletnega mesta. Vsako neangleško besedilo je strojno
prevedeno in čaka na človeški pregled; oznaka izvora se razlikuje po formatu.
Obstajajo tri neodvisne površine lokalizacije, namenoma brez križnega povezovanja, vsaka pa je razdeljena na build-time in runtime plast.
- Stran z nastavitvami (AD-1). Dostavljena je s STATIČNIM angleškim
markupom; vsako prevajano vozlišče nosi
data-i18n="<key>"alidata-i18n-<attr>="<key>". Med izvajanjem je aplikacijska plastpackages/webext/src/options/locale.ts: prekruntime.getURLnaloži zapakirano_locales/<code>/messages.json, prepiše označena vozlišča samo prektextContentin nastavi<html lang>. Stran je v celoti lokalizirana, vključno z JS-zgrajenimi oznakami vrstic strani in res dinamičnimi nizi, kot so status line, add-site feedback in version line. Angleški fallback, vgrajen voptions.js, zagotovi, da ob neuspelem fetchu uporabnik nikoli ne vidi le golih ključev sporočil. Povezava na priročnik sledi aktivnemu locale v njegovo lokalizirano ime strani (nl/handleiding.html), izbira jezika pa se shrani poduiLocalevstorage.local. - Chrome spletnega mesta (AD-2). Generator statičnega mesta
tooling/site.mjsstrežniško izriše vsa chrome vozlišča v angleščini in jih označi zdata-i18n, pri čemer ohrani<html lang="en-GB">. Browser-side loadersite/scripts/i18n.mjsnato glede na shranjeno izbiro vlocalStorageali podprti jezik iznavigator.languagespreklopi chrome v obiskovalčev jezik z nalaganjemsite/i18n/<code>.json. Za razliko od plasti za options vrednosti menja prekinnerHTML, ker nizi chrome-a spletnega mesta vsebujejo zaupanja vreden inline markup, kot sta<code>in<a>. - Dokumentacijske strani (AD-8/AD-9). Dokumentacija se izriše ZA VSAK
locale ob času gradnje, ne z runtime menjavo. Angleški docs ostanejo na
top-level poteh; vsak drug locale dobi kopijo pod
<code>/in pod imenom, prevedenim prekpackages/core/src/site-page-names.json. Če prevod manjka, se uporabi angleški vir,buildSitepa to zabeleži v logu. Build dodatno izdela taneknoindexredirect na starih angleških poteh, kot jenl/manual.html. - Lokalizacija notranjih povezav (Finding 2). Izbrani jezik se prenaša tudi
med navigacijo po dokumentaciji. Vsaka notranja povezava na dokumentacijsko
stran nosi
data-doc-link="<page>", njen href pa že brez JavaScripta kaže na pravilen lokalizirani dvojnik. Runtime loaderrewriteDocLinksnato vse notranje povezave prepiše na aktivni locale, pri čemer ohrani#hashin?query, zunanje povezave pa pusti pri miru.
Zatiranje »Aktivnih mest« v poštnem odjemalcu (AD-3)
Mail-client target (danes Thunderbird) že vidi vsako izrisano sporočilo, zato
je urejevalnik »Active sites« odveč in se odstrani. Odločitev nosi zastavica
mailClient: true pri TARGETS.thunderbird v tooling/build.mjs, izvedena pa
je v dveh plasteh:
- Build-time.
copyAssetsse ravna podescriptorFor(target).mailClient: za poštnega odjemalca izvede čisto transformacijoremoveSitesSection(html), ki odstrani celoten<section class="sites">. - Runtime. Vgrajeni krmilnik
packages/webext/src/options/options.tsprenaša odsotnost sekcije.renderSites,readSites,applyConfiginreadFormob manjkajočem#sites-listnaredijo no-op. Ker bi izpustsitessicer dovolilnormaliseConfig, da znova poseje vgrajene privzete vrednosti,saveOptionsob odsotni sekciji ohrani shranjenisites. Runtime guardguardMailClientSitessekcijo odstrani, če je še prisotna, in poštnega odjemalca prepozna pomessages,messageDisplayaliscripting.messageDisplayna vbrizganembrowser.
Varnostni in zasebnostni profil
- Brez omrežja ali eksfiltracije. Noben modul ne uporablja
fetch,XHRali drugega omrežnega klica; razširitev bere le shranjeno konfiguracijo in DOM strani. - Samo varen DOM. Pripisi in vstavljen stylesheet uporabljajo izključno
createElement,textContentin typedstyle.*properties, nikoliinnerHTML/insertAdjacentHTML/eval. - Vsak zunanji vhod je normaliziran. Vsako branje in pisanje gre skozi
normaliseConfig; barve preveriisSafeColour; match patterni morajo skoziMATCH_PATTERN; selektorjicontentRootso omejeni. - Najmanjši potrebni privilegiji. Vgrajeni izvori webmaila so fiksni
host_permissions; dodatni gostitelji so opt-in prekoptional_host_permissionsin z uporabniško gesto vezanegapermissions.request. - Zbiranje podatkov v Gecko. Manifesta za Firefox in Thunderbird
deklarirata
browser_specific_settings.gecko.data_collection_permissions.required: ["none"]. - Licenca. Projekt je AGPL-3.0-only (
LICENSE,package.json).
Testiranje
- Vitest unit tests pokrivajo oba paketa:
packages/core/test/*(konfiguracija, kontrast, host mismatch, link processor, styles in purity guard) terpackages/webext/test/*(content, storage, options, registration, toolbar, message display in end-to-end test uporabniško dodanega mesta). Vsak paket poganjavitest run. - Cucumber BDD scenariji živijo v
features/in prek zvestih test doubles vfeatures/support/webext.tsterfeatures/support/world.tspoganjajo resnične module@reveal-urls/webext. - Smoke testi manifestov po targetih
(
extensions/<engine>/test/manifest.smoke.test.mjs, nanode:test) potrjujejo natančno obliko manifesta in da build pravilno razreši vse referencirane datoteke.tooling/test/dodaja teste gradnje, različic in definition-of-done. - CI (
.github/workflows/ci.yml) ustavi vsak push in pull request zmake test,make bddinmake lint. Pečatenje različic in pakiranje sta namenoma ločena v tag-triggered.github/workflows/release.yml.