Safari is a supported active target with its own entry points. Built-in providers use static scripts and Safari requests no all-sites access. User-added sites receive a runtime optional permission, then the background injects content.js after navigation only for granted, enabled, non-builtin rules, with allFrames per rule. Safari settings are device-local and do not roam between devices. macOS packaging uses Xcode and the Apple Developer Program/App Store route.
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, Safari in Thunderbird — iz enega deljenega, čistega
jedra kode. Safari uporablja statične skripte samo za vgrajene ponudnike in je
objavljen v App Store. 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. Ta pot natívnih ugotovitev namerno ostaja
omejena na http:/https:.
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/destination.ts— razvrščanje in primerjava neujemanj cilja glede na shemo.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,
prek isWithinRoot in closest, pozno odkrivanje rootov pa prepusti
omejenemu prehodu discoverMatchedRoots() za vsak batch z enim
querySelectorAll(contentRoot) in deduplikacijo z WeakSet.
pendingShadowHosts poceni znova preverja prej plain hoste, dokler se ne
pojavi open shadow root, tako da so pokriti tudi shadow upgrade-i. Tako se
ujame dogajanje znotraj telesa sporočila in shadow-host upgrade-i, 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 preda deloresolveAnnotatableHref.null(preskok) vrne le za prazen, notranji ali neparsabilen href, relativne href-e razreši glede nabaseURIin sprejme vsako parsabilno shemo, vključno zmailto:,tel:,sms:ter neprosojnimi URL-jijavascript:,data:inblob:.ignoreHostsvelja le za povezavehttp:/https:z gostiteljem. - 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 glede na shemo z
destinationMismatchna očiščenem besedilu anchorja:classifySchemespletne povezave usmeri vhostMismatch, pošto vmailMismatch, telefon vphoneMismatch, preostale pa vopaqueMismatch. Nato pokličeannotateAnchor;showOnlyOnMismatchzatre le vidno razkritje v vrstici pri povezavi brez neskladja, njen ciljnititlepa ohrani.
annotateAnchor ustvarja samo varen DOM. Vedno shrani izvirni title
(TitleSave) in zapiše naslov cilja — to je edini zapis v anchor. Ločenega
načina naslova ali opozorilne značke ni. Če showOnlyOnMismatch ne skrije
povezave brez neskladja, prednjo vstavi <span class="reveal-urls-url"> in zaključni
<br>, pri čemer otroci same povezave ostanejo nedotaknjeni.
needsLeadingInlineRevealBreak doda začetni <br>, kadar vidna inline vsebina
v isti vrstici stoji pred povezavo. Span dobesedno prikaže avtorski absolutni
href ali relativni href, razrešen glede na baseURI, prek inertnega
textContent (s puščico in U+00A0 kot predpono, nato krajšan s truncateUrl);
ko je priključen, prek applyContrastBackdrop dobi kontrastno ozadje. Pri
poudarjenem neujemanju se REVEAL_URLS_MISMATCH_CLASS doda
REVEAL_URLS_URL_CLASS, kar prek applyColour uporabi nastavljeno barvo
neujemanja in privzeto krepko pisavo v REVEAL_URLS_CSS; izrecna prilagoditev
teže pisave prek applyFontOverrides ima prednost. Vsako ustvarjeno vozlišče je
označeno z data-ru="<token>", vsak vnos pa nosi configFingerprint, da
sprememba render konfiguracije prisili nov izris. 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 je spletna veja
destinationMismatch: primerja vidno besedilo povezave z njenim href po
registrabilni domeni, ne po surovem hostname. Poddomena brez neskladja 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.
Pri poštnih povezavah mailMismatch primerja vse registrabilne domene
prejemnikov, vključno z to, cc in bcc; phoneMismatch primerja razrešljive
klicane številke. opaqueMismatch vedno označi SCRIPT_LIKE_SCHEMES —
javascript:, data: in vbscript: — druge neprosojne sheme pa označi le,
kadar vidno besedilo poimenuje konkreten spletni, e-poštni ali telefonski cilj.
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
FastMail, Gmail, Proton (z allFrames), Tuta, Yahoo, Zoho 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,
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. Vsako NOTRANJE sidro dokumenta (šest strani
architecture.html,faq.html,index.html,licence.html,manual.html,privacy.html) nosidata-doc-link="<page>", njegov 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#site-selectnaredijo 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.