Reveal URLs — Architektūra

Šiame dokumente aprašoma, kaip struktūruotas Reveal URLs plėtinys ir kaip veikia jo pagrindinė logika, skirta prisidedantiems ir techniniams skaitytojams. Čia nuolat remiamasi tikrais failais ir simboliais forma path symbol, kad kiekvieną teiginį būtų galima patikrinti šaltinio kode. Naudotojui skirtą aprašymą rasite manual, o rankinio tikrinimo žingsnius — manual test plan.

Apžvalga

Reveal URLs yra viena MV3 WebExtension, kuri iš vieno bendro švaraus kodo branduolio kuriama Chrome, Edge, Opera, Firefox ir Thunderbird sistemoms. Safari atidėta vėlesniam etapui: ji turi manifest ir gali būti build-inama aiškiai ją nurodžius, tačiau pagal numatymą ir CI metu nėra įtraukiama. Plėtinys rodo kiekvienos nuorodos paskirties URL šalia pačios nuorodos rendered email turinyje ir pažymi nuorodą tada, kai matomas tekstas nurodo kitą registrable domeną nei tas, kuris yra jos href.

Saugykla yra pnpm ir TypeScript monorepo, aprašyta pnpm-workspace.yaml ir package.json, o "private": true neleidžia jos publikuoti kaip paketo. Bundlingą valdo tooling/build.mjs, kuris kompiliuoja bendrus paketus ir plonus entry points kiekvienam engine į įkraunamą dist/<target>/ katalogą. webextension-polyfill įtraukiamas į kiekvieną script bundle, o ne paliekamas kaip runtime global.

Du native el. pašto priedai išplečia tą pačią aptikimo logiką į vietas, kur WebExtension nepasiekia: Outlook Add-in ant Office.js ir Gmail Add-on ant Apps Script CardService. Abu pakartotinai naudoja tik PURE analizę iš packages/core/src/findings.ts, kartu su analyseAnchors, analyseHtml, neutraliu modeliu Finding[], hostMismatch logika ir registrable domeno išvedimu per tldts. Skiriasi tik host adapteris ir pateikimas. Outlook naudoja DOMParser ir veikia client-side. Gmail naudoja node-html-parser ir veikia server-side Google Apps Script V8 infrastruktūroje. Kadangi nė viena iš šių aplinkų neleidžia keisti perskaityto pranešimo DOM, abu rodo panelį arba kortelę su radiniais, o ne inline annotation. linkProcessor.ts DOM-mutation kelias ir REVEAL_URLS_CSS ten nenaudojami. Santykinis href sprendžiamas tik prieš patikimą <base href> pačiame laiške ir kitu atveju praleidžiamas.

Kodo bazė aiškiai padalija atsakomybes:

Saugyklos išdėstymas

packages/core — grynasis branduolys

Nė vienas modulis čia neimportuoja extension API, pavyzdžiui, browser.*, chrome.* arba messenger.*, ir nė vienas nenaudoja string-to-markup DOM sinks, tokių kaip innerHTML ar insertAdjacentHTML. Šį susitarimą mechaniškai užtikrina packages/core/test/purity.test.ts, kuris surenka src/**/*.ts, pašalina komentarų kūnus su stripComments ir patikrina, kad nei EXTENSION_API_PATTERN, nei UNSAFE_DOM_PATTERN neatitiktų nė vieno modulio. Viešasis paviršius reeksportuojamas iš packages/core/src/index.ts, o vienintelė runtime priklausomybė yra tldts.

Pagrindiniai moduliai yra:

packages/webext — browser API apvalkalai

Šie moduliai GALI naudoti browser.*, ir tai yra vienintelė vieta, kur liečiamos WebExtension APIs. Viešasis paviršius reeksportuojamas iš packages/webext/src/index.ts. Čia yra content.ts, contentRegistration.ts, storage.ts, background.ts, toolbarAction.ts, messageDisplay.ts, messageDisplayBackground.ts ir options/options.ts.

extensions/<engine> — ploni įėjimo taškai ir manifestai

Kiekvienas engine turi savo manifest.json ir savo icons/. Chrome, Firefox ir Thunderbird papildomai turi src/ katalogą su entry points. Edge, Opera ir Safari turi tik manifestus bei piktogramas ir iš naujo naudoja Chrome src/. Pavyzdžiui, extensions/chrome/src/content.ts turi tik void createContentController().bootstrap();, o extensions/chrome/src/background.ts iškviečia registerBackground, registerContentReconciliation ir registerToolbarToggle. extensions/thunderbird/src/background.ts iškviečia registerBackground ir registerMessageDisplay. extensions/_template yra scaffold, o ne build target.

tooling, features ir smoke testai

tooling apima build.mjs, version.mjs ir icons.mjs. features apima Gherkin scenarijus, step definitions ir patikimus doubles failuose features/support/webext.ts ir features/support/world.ts. Kiekvienas engine taip pat turi extensions/<engine>/test/manifest.smoke.test.mjs, o _template naudoja template.smoke.test.mjs.

Atskleidimo srautas

Content kelias prasideda engine entry point-e, pavyzdžiui, extensions/chrome/src/content.ts, kuris iškviečia createContentController().bootstrap()packages/webext/src/content.ts.

Bootstrap

createContentController bootstrap vykdomas vieną kartą kiekvienam frame. Jis perima frame dar prieš kiekvieną await per claimBootstrap, įkelia config per loadConfigWithRetry, įšvirkščia stylesheet per injectStyles tik po sėkmingo nuskaitymo ir atstato markerį per releaseBootstrap, jei įkėlimas galutinai nepavyksta. Kai config.enabled yra aktyvus, jis kviečia start, o bet kuriuo atveju užregistruoja listener onConfigChanged.

Aktyvios svetainės taisyklės nustatymas

start ir applyConfig naudoja resolveSiteRule, kad nuspręstų, ar ir kur atlikti annotation. Pirmiausia bandoma paties dokumento vieta per resolveForHref, kuris perfiltruoja config.sites iki įjungtų taisyklių ir galutinį pasirinkimą palieka selectMostSpecific. Jei dokumentas neatitinka ir jo kilmė yra opaque arba inherited, kaip tikrina hasOpaqueOrigin, tada naudojamas fallback per readTopHref, readOpenerHref ir readReferrer, saugantis nuo cross-origin skaitymų.

Apribojimas iki turinio šaknies ir pokyčių stebėjimas

start išsaugo contentRoot, sukuria annotator per createAnnotator(config) ir apdoroja kiekvieną šaknį iš selectRoots(doc, contentRoot), kuris apgaubia querySelectorAll su try/catch, kad neteisingas selector saugiai grąžintų []. Po to jis stebi doc.body su OBSERVER_OPTIONS. processMutations filtruoja kiekvieną pokytį pagal root, o processAddedNode apima mazgus root viduje, pačią root sritį ir mazgus, kurie ją talpina. applyConfig atlieka teardown, restart arba reflow priklausomai nuo aktyvios taisyklės ir contentRoot pasikeitimo.

Vienos nuorodos žymėjimas

Annotator gyvena faile packages/core/src/linkProcessor.ts. createAnnotator grąžina Annotator su Map, data-ru tokenu iš generateToken ir aktyviu config. process(root) išvalo atjungtus įrašus, surenka a[href] elementus per collectAnchors, išsprendžia URL per resolveAnnotatableUrl, naudoja fast path isAnnotationIntact, kai renderis nepasikeitęs, ir apskaičiuoja mismatch per hostMismatch, prieš kviesdamas annotateAnchor.

annotateAnchor renderina tik SAFE-DOM. Režimu "inline" jis prideda <span class="reveal-urls-url"> su textContent ir <br>. Režimu "title" jis išsaugo esamą title, pakeičia jį URL ir, kai mismatch pabrėžiamas, prideda <span class="reveal-urls-warn"> su REVEAL_URLS_WARN_LABEL. Sukurti mazgai gauna data-ru="<token>", o configFingerprint naudojamas priverstiniam reflow. truncateUrl dirba virš [...displayHref] ir naudoja URL_ELLIPSIS.

Host neatitiktis

packages/core/src/hostMismatch.ts hostMismatch lygina nuorodos matomą tekstą su jos href registrable domeno lygmeniu, o ne pagal žalią host. extractHostCandidates išskaido tekstą į tokenus ir sumažina juos per hostCandidate, kuriam reikalingas taškas ir tldts atpažįstamas public suffix. Href host ir kandidatai redukuojami per tldts.getDomain. Bet kuris kandidatas su kitu domenu sukelia mismatch.

Stiliai ir kontrastas

packages/core/src/styles.ts saugo REVEAL_URLS_CSS ir helperius applyColour, applyFontOverrides ir applyContrastBackdrop. applyContrastBackdrop nuskaito elemento apskaičiuotą spalvą ir pirmą opaque foną per getComputedStyle, tada naudoja needsWhiteBackdroppackages/core/src/contrast.ts, kad nuspręstų, ar nustatyti style.backgroundColor = "white". contrast.ts pateikia parseColour, relativeLuminance ir contrastRatio.

Konfigūruojami host ir turinio ribojimas

SiteRule faile packages/core/src/config.ts nusako, KUR annotation veikia per match ir allFrames, ir KOKS container jį apriboja per contentRoot, kartu su enabled ir žyme builtin. DEFAULT_SITES apima Gmail, Proton su allFrames ir abu Outlook host. Config sujungia globalius toggles, colours, font overrides ir sąrašą sites.

Match pattern mechanizmas

packages/core/src/matchPattern.ts saugo restrictive gramatiką MATCH_PATTERN: tik http ir https, pasirenkamą *. wildcard host, glob path, be port, be * scheme ir be <all_urls>. matchesPattern lygina URL su validuotu pattern ir path dalį perduoda pathGlobMatches. parsePatternParts padalija pattern į host, path, scheme ir wildcardHost.

Kai taisyklės persidengia, taikomas principas "most specific wins". compareSiteSpecificity iškelia exact host virš wildcard host, po to ilgesnį literal host, po to labiau literal path per literalPathLength, o galiausiai ASCII tiebreak. selectMostSpecific grąžina specifiškiausią įjungtą taisyklę, atitinkančią URL.

permissions.getAll() gali pranešti grants visoje WebExtension gramatikoje (<all_urls>, *://*/*, https://*/*, *://*.host/*, https://host/*), kuriuos MATCH_PATTERN atmestų. parseGrantedOrigin paverčia juos į GrantedOriginParts, originCovers tikrina padengimą per match, o matchAllowsOriginFallback nurodo, ar path yra tiksliai ORIGIN_FALLBACK_PATH, tai yra /*.

Dinaminė registracija

packages/webext/src/contentRegistration.ts užregistruoja po vieną dynamic content script kiekvienai naudotojo pridėtai taisyklei. Built-ins aptarnaujami per static content_scripts entries ir niekada neregistruojami dinamiškai. desiredContentScripts perfiltruoja config.sites, naudoja isOriginGranted, originCovers ir contentScriptId, ir nustato matchOriginAsFallback: true tik tada, kai tai leidžia matchAllowsOriginFallback(rule.match).

reconcileContentScriptsOnce nuskaito suteiktas kilmes, išsaugotą config ir live registrations bei sulygina jas property-aware diff būdu. sameRegistration normalizuoja tiek pageidaujamą descriptor, tiek live read-back. Kadangi nėra updateContentScripts, pakeistas script pašalinamas ir užregistruojamas iš naujo. reconcileContentScripts naudoja promise inFlight, kad nuosekliai vykdytų triggerius, kurie gali nutikti beveik vienu metu.

Aiškaus leidimo srautas

Static manifestai deklaruoja optional_host_permissions (http://*/*, https://*/*), kad naudotojas runtime metu galėtų suteikti leidimą papildomam host. packages/webext/src/options/options.ts addSite validuoja visą kandidato rule per normaliseSiteRule, išveda origin per matchOrigin ir kviečia browser.permissions.request iš Add mygtuko click gesture. Tik po patvirtinimo pridedama eilutė builtin:false.

Config, saugykla ir kanoninis modelis

Vienintelis validavimo piltuvas

normaliseConfig faile packages/core/src/config.ts yra vienintelis kanoninis skaitymo kelias. Jis konvertuoja ir apriboja kiekvieną lauką, neteisingų duomenų atveju grąžina numatytąją reikšmę ir niekada nemeta exception. Jis sudarytas iš validatorių, tokių kaip normaliseBoolean, normaliseRenderMode, normaliseMaxLength, normaliseIgnoreHosts, normaliseMatchColour, normaliseMismatchColour, normaliseCssSize, normaliseFontWeight, normaliseContentRoot, normaliseMatchPattern ir normaliseSites. normaliseSites išmeta neteisingus entries, pašalina dublikatus pagal match, priverčia builtin: true ten, kur taisyklė persidengia su built-in, ir atkuria trūkstamus built-ins.

Saugojimas

packages/webext/src/storage.ts apgaubia WebExtension storage API. configArea naudoja browser.storage.sync, kai jis prieinamas, ir pereina prie browser.storage.local, pavyzdžiui, Thunderbird aplinkoje. getConfig, setConfig ir onConfigChanged visada perleidžia reikšmes per normaliseConfig, todėl sugadinta saugykla negali grąžinti neteisingo Config. Abi add-on formos naudoja tą patį piltuvą ant savo host storage.

Kanoninis config modelis ir tiksliniai atnaujinimai

Trys write path perskaito kanoninį config, pakeičia tik vieną dalyką ir įrašo jį atgal, nesaugodami neužbaigtų form edits:

toggleEnabled faile packages/webext/src/toolbarAction.ts seka tuo pačiu modeliu ir background pusėje.

Build pagal engine ir manifesto sutartis

tooling/build.mjs valdo vieną bendrą esbuild build kiekvienam WebExtension target. Descriptor TARGETS nustato SOURCE TARGET kiekvienam target. Chrome, Firefox ir Thunderbird turi savo src/. Edge, Opera ir Safari naudoja chrome kaip šaltinį. Outlook add-in turi atskirą šaltinį. ACTIVE_TARGETS, tai yra Chrome, Edge, Firefox, Opera ir Thunderbird, yra tie, kurie build-inami per --all ir --package. Safari ir Outlook build-inami tik tada, kai aiškiai įvardijami, o Gmail naudoja make build-gmail.

Gmail bundle taikomas Apps Script V8 runtime, kuriame nėra nei ES modules, nei native URL. Todėl jis build-inamas kaip ESM, o po to jam pašalinamas export {…}, kad trigger funkcijos liktų globalios. Nedidelis URL polyfill įtraukiamas tik prireikus. buildTarget išvalo dist/<target>/, nukopijuoja manifest.json ir icons/, supakuoja scripts kaip IIFE, options puslapį kaip ESM ir nukopijuoja options.html bei options.css.

tooling/version.mjs stamp-ina versijas. Jis paima MAJOR.MINOR iš root package.json, suranda didžiausią BUILD number ir prideda jį kiekvienam target manifest. Outlook add-in turi tiek manifest.json, tiek manifest.xml. tooling/icons.mjs, paleistas su make icons, rasterises assets/icon.svg į icon48.png ir icon128.png.

Manifesto sutartis apibrėžta failuose extensions/<engine>/test/manifest.smoke.test.mjs, kur tikrinama kiekvieno manifesto forma ir kiekvieno referenced file buvimas.

Internacionalizacija (i18n)

Reveal URLs lokalizuotas į daugelį kalbų be anglų. Vienintelis šio rinkinio šaltinis yra packages/core/src/locales.json, kuriame yra SUPPORTED_LOCALES, vietiniai pavadinimai ir numatytoji kalba. Kiekvienas neangliškas string yra machine-translated ir laukia žmogaus peržiūros. Kilmės žymeklis skiriasi pagal formatą: _locales jį neša lauke description, site catalogues jį įrašo faile site/i18n/README.md, o išversti docs pirmoje eilutėje turi HTML komentarą.

Yra trys nepriklausomi lokalizacijos paviršiai:

„Aktyvių svetainių“ slėpimas pašto kliente (AD-3)

Mail client target, pavyzdžiui, Thunderbird, jau mato visus rendered messages, todėl host pagrindu veikiantis „Active sites“ redaktorius ten yra nereikalingas. Sprendimas perduodamas per mailClient: true lauką faile tooling/build.mjs.

Saugumo ir privatumo laikysena

Testavimas