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 — Arhitektūra

Šis dokuments apraksta, kā ir strukturēts Reveal URLs paplašinājums un kā darbojas tā pamatloģika, līdzstrādniekiem un tehniskiem lasītājiem. Tajā pastāvīgi ir atsauces uz reāliem failiem un simboliem formā path symbol, lai katru apgalvojumu varētu pārbaudīt pirmkodā. Lietotājam paredzētajam aprakstam skatiet manual. Manuālās pārbaudes soļiem skatiet manual test plan.

Pārskats

Reveal URLs ir viena MV3 WebExtension, kas tiek veidota Chrome, Edge, Opera, Firefox, Safari un Thunderbird no vienas kopīgas, tīras kodola bāzes. Safari izmanto statiskus skriptus tikai iebūvētajiem nodrošinātājiem un ir publicēts App Store. Paplašinājums atklāj katras saites galamērķa URL blakus pašai saitei renderētā e-pastā un iezīmē saiti, kuras redzamais teksts nosauc citu registrable domēnu nekā tas, kurš atrodas tās href.

Repozitorijs ir pnpm un TypeScript monorepo, ko raksturo pnpm-workspace.yaml un package.json, bet "private": true liedz to publiskot kā pakotni. Bundling tiek vadīts ar tooling/build.mjs, kas kompilē kopīgās pakotnes un plānos entry points katram engine ielādējamā dist/<target>/ direktorijā. webextension-polyfill tiek iepakots katrā script bundle, nevis atstāts kā runtime global.

Divi native e-pasta papildinājumi paplašina to pašu noteikšanas loģiku uz virsmām, kuras WebExtension nesasniedz: Outlook Add-in uz Office.js un Gmail Add-on uz Apps Script CardService. Abi atkārtoti izmanto tikai PURE analīzi no packages/core/src/findings.ts, ar analyseAnchors, analyseHtml, neitrālo modeli Finding[], loģiku hostMismatch un registrable domēna noteikšanu ar tldts. Atšķiras tikai host adapteris un attēlošana. Outlook izmanto DOMParser un darbojas client-side. Gmail izmanto node-html-parser un darbojas server-side Google Apps Script V8 infrastruktūrā. Tā kā neviena no šīm platformām neļauj mainīt izlasītā ziņojuma DOM, abi rāda paneli vai karti ar atradumiem, nevis inline annotation. linkProcessor.ts DOM-mutation ceļš un REVEAL_URLS_CSS tur netiek izmantoti. Relatīvs href tiek atrisināts tikai pret uzticamu <base href> pašā e-pastā un citādi tiek izlaists. Šis vietējo atradumu ceļš apzināti paliek tikai http:/https:.

Koda bāze skaidri nošķir atbildības:

Repozitorija izkārtojums

packages/core — tīrais kodols

Neviens modulis šeit neimportē extension API kā browser.*, chrome.* vai messenger.*, un neviens neizmanto string-to-markup DOM sinks kā innerHTML vai insertAdjacentHTML. Šis noteikums tiek mehāniski nodrošināts ar packages/core/test/purity.test.ts, kas savāc src/**/*.ts, izņem komentāru saturu ar stripComments un pārbauda, ka ne EXTENSION_API_PATTERN, ne UNSAFE_DOM_PATTERN nesakrīt ar nevienu moduli. Publiskā virsma tiek reeksportēta no packages/core/src/index.ts, un vienīgā runtime atkarība ir tldts.

Galvenie moduļi ir:

packages/webext — browser API ietvari

Šie moduļi DRĪKST izmantot browser.*, un tā ir vienīgā vieta, kur tiek skartas WebExtension APIs. Publiskā virsma tiek reeksportēta no packages/webext/src/index.ts. Šeit atrodas content.ts, contentRegistration.ts, storage.ts, background.ts, toolbarAction.ts, messageDisplay.ts, messageDisplayBackground.ts un options/options.ts.

extensions/<engine> — plāni ieejas punkti un manifesti

Katrs engine nes savu manifest.json un savu icons/. Chrome, Firefox un Thunderbird papildus nes src/ direktoriju ar entry points. Edge, Opera un Safari nes tikai manifestus un ikonas un atkārtoti izmanto Chrome src/. Piemēram, extensions/chrome/src/content.ts satur tikai void createContentController().bootstrap();, bet extensions/chrome/src/background.ts izsauc registerBackground, registerContentReconciliation un registerToolbarToggle. extensions/thunderbird/src/background.ts izsauc registerBackground un registerMessageDisplay. extensions/_template ir scaffold, nevis build target.

tooling, features un smoke testi

tooling ietver build.mjs, version.mjs un icons.mjs. features ietver Gherkin scenārijus, step definitions un uzticamos doubles failos features/support/webext.ts un features/support/world.ts. Katram engine ir arī extensions/<engine>/test/manifest.smoke.test.mjs, bet _template izmanto template.smoke.test.mjs.

Atklāšanas plūsma

Content ceļš sākas engine entry pointā, piemēram, extensions/chrome/src/content.ts, kas izsauc createContentController().bootstrap() no packages/webext/src/content.ts.

Bootstrap

createContentController bootstrap tiek izpildīts reizi katram frame. Tas iegūst frame savā kontrolē pirms jebkura await ar claimBootstrap, ielādē config caur loadConfigWithRetry, injicē stylesheet ar injectStyles tikai pēc veiksmīga lasījuma un atjauno marķieri ar releaseBootstrap, ja ielāde pilnībā neizdodas. Kad config.enabled ir ieslēgts, tas izsauc start, bet jebkurā gadījumā reģistrē listeneri onConfigChanged.

Aktīvās vietnes noteikuma atrisināšana

start un applyConfig izmanto resolveSiteRule, lai izlemtu, vai un kur veikt annotation. Vispirms tiek pārbaudīta paša dokumenta atrašanās vieta ar resolveForHref, kas filtrē config.sites līdz ieslēgtajiem noteikumiem un atstāj izvēli selectMostSpecific ziņā. Ja dokuments nesakrīt un tā izcelsme ir opaque vai inherited, kā to pārbauda hasOpaqueOrigin, seko fallback ar readTopHref, readOpenerHref un readReferrer, aizsargāts pret cross-origin lasījumiem.

Ierobežošana līdz satura saknei un izmaiņu novērošana

start saglabā contentRoot, izveido annotator ar createAnnotator(config) un apstrādā katru sakni no selectRoots(doc, contentRoot), kas ietin querySelectorAll try/catch blokā, lai nederīgs selector droši pārvērstos par []. Pēc tam tas novēro doc.body ar OBSERVER_OPTIONS. processMutations filtrē katru izmaiņu pēc root, targetu atkārtoti apstrādā tikai tad, ja tas ir Element root iekšienē caur isWithinRoot un closest, un vēlo root atklāšanu uztic ierobežotajam discoverMatchedRoots() gājienam katram batch ar vienu querySelectorAll(contentRoot) un deduplikāciju ar WeakSet. pendingShadowHosts atkārtoti pārbauda iepriekš plain hostus, līdz parādās open shadow root, lai būtu segti arī shadow upgrade-i. applyConfig veic teardown, restart vai reflow atkarībā no aktīvā noteikuma un contentRoot izmaiņām.

Vienas saites marķēšana

Annotator dzīvo failā packages/core/src/linkProcessor.ts. createAnnotator atgriež Annotator ar Map, data-ru tokenu no generateToken un aktīvo config. Tā metode process(root):

  1. Iztīra reģistra ierakstus, kuru enkurs vairs nav savienots.
  2. Savāc kandidātenkurus ar collectAnchors (pakārtotos a[href] un pašu sakni, ja tā ir a[href], atmetot dublikātus).
  3. Atrisina katra enkura galamērķi ar resolveAnnotatableUrl, kas deleģē uz resolveAnnotatableHref. Tas atgriež null (izlaist) tikai tukšam / lapas iekšējam vai neparsējamam href, relatīvus href atrisina pret baseURI un pieņem jebkuru parsējamu shēmu, tostarp mailto:, tel:, sms: un necaurredzamus javascript:, data: un blob: URL. ignoreHosts attiecas tikai uz http:/https: saitēm ar host.
  4. Izvēlas idempotento ātro ceļu, kad isAnnotationIntact apstiprina, ka renderējuma pirkstu nospiedums nav mainījies un katrs izveidotais mezgls vēl ir savienots un savā vietā; citādi atceļ novecojušo anotāciju un renderē no jauna.
  5. Aprēķina no shēmas atkarīgo neatbilstību ar destinationMismatch pēc tīrā enkura teksta: classifyScheme tīmekļa saites novirza uz hostMismatch, pastu uz mailMismatch, tālruni uz phoneMismatch, bet pārējās uz opaqueMismatch. Tad tas izsauc annotateAnchor; showOnlyOnMismatch apslēpj tikai saites bez neatbilstības redzamo iekļauto atklājumu, bet saglabā tās galamērķa title.

annotateAnchor renderē tikai SAFE-DOM. Tas vienmēr saglabā sākotnējo title (TitleSave) un ieraksta galamērķa title — vienīgo rakstīšanu enkurā. Nav atsevišķa title režīma vai brīdinājuma nozīmītes. Ja showOnlyOnMismatch neapslēpj saiti bez neatbilstības, tas priekšā ievieto <span class="reveal-urls-url"> un beigu <br>, atstājot saites bērnus neskartus. needsLeadingInlineRevealBreak pievieno sākuma <br>, kad redzams iekļauts saturs ir pirms saites tajā pašā rindā. Span rāda autora norādītu absolūtu href vārds vārdā vai pret baseURI atrisinātu relatīvu href, izmantojot inertu textContent (ar bultiņu un U+00A0 nedalāmo atstarpi priekšā, pēc tam saīsinātu ar truncateUrl); pēc savienošanas tas saņem kontrasta fonu caur applyContrastBackdrop. Pie uzsvērtas neatbilstības REVEAL_URLS_MISMATCH_CLASS tiek pievienota REVEAL_URLS_URL_CLASS, lietojot konfigurēto neatbilstības krāsu ar applyColour un pēc noklusējuma treknrakstu no REVEAL_URLS_CSS; skaidra fonta biezuma pārrakstīšana ar applyFontOverrides ir prioritāra. Katrs izveidotais mezgls ir marķēts ar data-ru="<token>" (nekad pats enkurs), un katrs ieraksts glabā savu configFingerprint, lai renderēšanas konfigurācijas maiņa piespiestu pārliešanu. revertOwned noņem izveidotos mezglus pēc atsauces un atjauno saglabāto title; revertAll to dara katram piederošam enkuram un iztīra reģistru.

truncateUrl darbojas ar Unicode KODA PUNKTIEM ([...displayHref]), tādēļ griezums nekad nesadala surogātpāri; tas saglabā shēmas un host izcelsmi un saīsina pārējo ar beigu URL_ELLIPSIS.

Host neatbilstība

packages/core/src/hostMismatch.ts hostMismatch ir tīmekļa zars no destinationMismatch: tas salīdzina saites redzamo tekstu ar tās href pēc registrable domēna, nevis neapstrādāta host nosaukuma, tādēļ apakšdomēns bez neatbilstības netiek atzīmēts, bet līdzīgs domēns tiek. extractHostCandidates sadala tekstu un samazina katru tokenu ar hostCandidate (tam vajadzīgs punkts un tldts atpazīts ICANN publiskais sufikss, noraidot parastu punktotu tekstu, piemēram, e.g). Gan href host, gan katrs kandidāts ar tldts.getDomain tiek samazināti līdz savam registrable domēnam, un JEBKURS kandidāts, kura domēns atšķiras no href domēna, rada neatbilstību — tādēļ īstā ļaunprātīgā host nosaukšana līdzās ēsmas host nevar apslāpēt brīdinājumu. Tas nekad nemet izņēmumu.

Pasta saitēm mailMismatch salīdzina visus saņēmēju registrable domēnus, ieskaitot to, cc un bcc; phoneMismatch salīdzina atrisināmus izsauktos numurus. opaqueMismatch vienmēr atzīmē SCRIPT_LIKE_SCHEMESjavascript:, data: un vbscript: — bet citas necaurredzamas shēmas tiek atzīmētas tikai tad, ja redzamais teksts nosauc konkrētu tīmekļa, e-pasta vai tālruņa galamērķi.

Stili un kontrasts

packages/core/src/styles.ts glabā REVEAL_URLS_CSS un helper-us applyColour, applyFontOverrides un applyContrastBackdrop. applyContrastBackdrop nolasa elementa aprēķināto krāsu un pirmo opaque fonu ar getComputedStyle, pēc tam izmanto needsWhiteBackdrop no packages/core/src/contrast.ts, lai izlemtu, vai uzlikt style.backgroundColor = "white". contrast.ts sniedz parseColour, relativeLuminance un contrastRatio.

Konfigurējami host un satura ierobežošana

SiteRule failā packages/core/src/config.ts nosaka, KUR annotation darbojas caur match un allFrames, un KURŠ container to ierobežo caur contentRoot, kopā ar enabled un atzīmi builtin. DEFAULT_SITES aptver FastMail, Gmail, Proton ar allFrames, Tuta, Yahoo, Zoho un abus Outlook host-us. Config apvieno globālos toggles, colours, font overrides un sarakstu sites.

Match pattern mehānisms

packages/core/src/matchPattern.ts glabā restrictive gramatiku MATCH_PATTERN: tikai http un https, izvēles *. wildcard host, glob path, bez port, bez * scheme un bez <all_urls>. matchesPattern salīdzina URL ar validētu pattern un deleģē path daļu pathGlobMatches. parsePatternParts sadala pattern host, path, scheme un wildcardHost.

Kad noteikumi pārklājas, darbojas princips "most specific wins". compareSiteSpecificity sarindo exact host pirms wildcard host, pēc tam garāku literal host, pēc tam literalāku path ar literalPathLength, un beigās ASCII tiebreak. selectMostSpecific atgriež viskonkrētāko ieslēgto noteikumu, kas atbilst URL.

permissions.getAll() var ziņot grants pilnajā WebExtension gramatikā (<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*), kurus MATCH_PATTERN noraidītu. parseGrantedOrigin pārvērš tos par GrantedOriginParts, originCovers pārbauda pārklājumu pāri match, un matchAllowsOriginFallback ziņo, vai path ir precīzi ORIGIN_FALLBACK_PATH, tas ir, /*.

Dinamiskā reģistrācija

packages/webext/src/contentRegistration.ts reģistrē vienu dynamic content script katram lietotāja pievienotam noteikumam. Built-ins tiek apkalpoti ar static content_scripts entries un nekad netiek reģistrēti dinamiski. desiredContentScripts filtrē config.sites, izmanto isOriginGranted, originCovers un contentScriptId, un iestata matchOriginAsFallback: true tikai tad, ja to atļauj matchAllowsOriginFallback(rule.match).

reconcileContentScriptsOnce nolasa piešķirtās izcelsmes, saglabāto config un live registrations un saskaņo tās ar property-aware diff. sameRegistration normalizē gan vēlamo descriptor, gan live read-back. Tā kā nav updateContentScripts, izmainīts script tiek noņemts un reģistrēts no jauna. reconcileContentScripts izmanto inFlight promise, lai serializētu trigger-us, kas var notikt tuvu viens otram.

Skaidri piešķirtas atļaujas plūsma

Static manifesti deklarē optional_host_permissions (http://*/*, https://*/*), lai lietotājs runtime laikā varētu piešķirt atļauju papildu host-am. packages/webext/src/options/options.ts addSite validē visu kandidāta rule ar normaliseSiteRule, iegūst origin ar matchOrigin un izsauc browser.permissions.request Add pogas click gesture iekšienē. Tikai pēc apstiprinājuma tiek pievienota rinda builtin:false.

Config, storage un kanoniskais modelis

Vienīgais validācijas funnel

normaliseConfig failā packages/core/src/config.ts ir vienīgais kanoniskais lasīšanas ceļš. Tas pārveido un ierobežo katru lauku, atgriežas pie noklusējuma vērtības nederīgu datu gadījumā un nekad nemet exception. Tas ir būvēts no validatoriem kā normaliseBoolean, normaliseMaxLength, normaliseIgnoreHosts, normaliseMatchColour, normaliseMismatchColour, normaliseCssSize, normaliseFontWeight, normaliseContentRoot, normaliseMatchPattern un normaliseSites. normaliseSites izmet nederīgus entries, noņem dublikātus pēc match, uzspiež builtin: true pārklājošiem built-in noteikumiem un atjauno trūkstošos built-ins.

Glabāšana

packages/webext/src/storage.ts aptin WebExtension storage API. configArea izmanto browser.storage.sync, ja tas ir pieejams, un pāriet uz browser.storage.local, piemēram, Thunderbird. getConfig, setConfig un onConfigChanged vienmēr laiž vērtības cauri normaliseConfig, tāpēc bojāts store nekad nevar atdot nederīgu Config. Abas add-on formas izmanto to pašu funnel virs savām host storage sistēmām.

Kanoniskais config modelis un mērķēti atjauninājumi

Trīs write path lasa kanonisko config, izmaina tieši vienu lietu un raksta to atpakaļ, nesaglabājot nepabeigtus form edits:

toggleEnabled failā packages/webext/src/toolbarAction.ts seko tam pašam modelim arī background pusē.

Build pa engine un manifesta līgums

tooling/build.mjs vada vienotu esbuild build katram WebExtension target. Descriptor TARGETS nosaka SOURCE TARGET katram target. Chrome, Firefox un Thunderbird ir savs src/. Edge, Opera un Safari izmanto chrome kā avotu. Outlook add-in ir savs avots. ACTIVE_TARGETS, tas ir, Chrome, Edge, Firefox, Opera un Thunderbird, ir tie, kas tiek build-oti ar --all un --package. Safari un Outlook tiek build-oti tikai tad, kad ir skaidri nosaukti, bet Gmail izmanto make build-gmail.

Gmail bundle mērķē Apps Script V8 runtime, kam nav ne ES modules, ne native URL. Tādēļ tas tiek build-ots kā ESM, pēc tam no tā tiek noņemts export {…}, lai trigger funkcijas paliktu globālas. Neliels URL polyfill tiek iekļauts tikai vajadzības gadījumā. buildTarget iztīra dist/<target>/, nokopē manifest.json un icons/, iesaiņo scripts kā IIFE, options lapu kā ESM un nokopē options.html un options.css.

tooling/version.mjs stamp-ē versijas. Tas paņem MAJOR.MINOR no root package.json, atrod augstāko BUILD number un pievieno to katram target manifestam. Outlook add-in nes gan manifest.json, gan manifest.xml. tooling/icons.mjs, kas darbojas ar make icons, rasterises assets/icon.svg failos icon48.png un icon128.png.

Manifesta līgums ir definēts failos extensions/<engine>/test/manifest.smoke.test.mjs, kur tiek pārbaudīta katra manifesta forma un katra referenced file esamība.

Internacionalizācija (i18n)

Reveal URLs ir lokalizēts daudzās valodās līdzās angļu valodai. Vienīgais šī kopuma avots ir packages/core/src/locales.json, kas satur SUPPORTED_LOCALES, vietējos nosaukumus un noklusējuma valodu. Katrs neangļu string ir machine-translated un gaida cilvēka pārskatīšanu. Izcelsmes marķieris atšķiras pa formātiem: _locales to nes description, site catalogues to pieraksta site/i18n/README.md, bet tulkotie docs pirmajā rindā nes HTML komentāru.

Ir trīs neatkarīgas lokalizācijas virsmas:

“Aktīvo vietņu” slēpšana pasta klientā (AD-3)

Mail client target, piemēram, Thunderbird, jau redz visus rendered messages, tāpēc host-os balstītais “Active sites” redaktors tur ir lieks. Lēmums tiek nests ar mailClient: true failā tooling/build.mjs.

Drošības un privātuma nostāja

Testēšana