Reveal URLs — Arkitektura
Ky dokument përshkruan si është strukturuar zgjerimi Reveal URLs dhe si
punon logjika e tij bazë, për kontribues dhe lexues teknikë. Ai i referohet
vazhdimisht skedarëve dhe simboleve reale, në formën path symbol, që çdo
pretendim të mund të verifikohet te burimi. Për përshkrimin e orientuar nga
përdoruesi shihni manual. Për hapat e verifikimit manual shihni
manual test plan.
Përmbledhje
Reveal URLs është një MV3 WebExtension i vetëm, i ndërtuar për Chrome, Edge,
Opera, Firefox dhe Thunderbird nga një bërthamë e vetme dhe e pastër kodesh.
Safari është shtyrë për më vonë: ka manifest dhe mund të ndërtohet kur
kërkohet shprehimisht, por përjashtohet nga build-i i parazgjedhur dhe nga
CI. Zgjerimi zbulon URL-në përfundimtare të çdo lidhjeje pranë vetë lidhjes
në një email të renderuar dhe shënon një lidhje kur teksti i dukshëm përmend
një domen registrable tjetër nga ai i href-it të saj.
Repozitori është një monorepo për pnpm dhe TypeScript, i përshkruar nga
pnpm-workspace.yaml dhe package.json, ndërsa "private": true e mban
jashtë publikimit si paketë. Bundling drejtohet nga tooling/build.mjs, i
cili përpilon paketat e përbashkëta dhe entry points e holla për secilin
engine në një direktori të ngarkueshme dist/<target>/.
webextension-polyfill përfshihet në çdo script bundle, në vend që të lihet
si global i runtime-it, në mënyrë që i njëjti kod të funksionojë si në
Chromium ashtu edhe në Gecko.
Dy shtesa vendase emaili e shtrijnë të njëjtën logjikë zbulimi në sipërfaqe
ku WebExtension nuk arrin dot: një Outlook Add-in mbi Office.js dhe një
Gmail Add-on mbi Apps Script CardService. Të dyja ripërdorin vetëm
analizën PURE nga packages/core/src/findings.ts, me analyseAnchors,
analyseHtml, modelin neutral Finding[], logjikën hostMismatch dhe
nxjerrjen e domenit registrable nga tldts. Ndryshojnë vetëm adapter-i i
host-it, pra mënyra si lexohet dhe pars-ohet trupi i mesazhit, dhe mënyra e
paraqitjes. Outlook bën parsing me DOMParser dhe punon client-side.
Gmail përdor node-html-parser dhe punon server-side në infrastrukturën
Apps Script V8 të Google-it. Asnjëra nga këto platforma nuk lejon të
ndryshohet DOM-i i mesazhit të lexuar, prandaj të dyja tregojnë panel ose
card me gjetje, jo inline annotation. Rruga ekzistuese e DOM mutation në
linkProcessor.ts dhe REVEAL_URLS_CSS nuk përdoren atje. Një href relativ
zgjidhet vetëm kundrejt një <base href> të besueshëm brenda emailit dhe
ndryshe anashkalohet.
Kodi ndahet qartë sipas përgjegjësive:
packages/coremban logjikën PURE, pa browser APIs, pa storage dhe pa sinks markup-u. Është burimi i vetëm i së vërtetës për annotation, validimin e config-ut, krahasimin e hosteve dhe ngjyrosjen me kujdes për kontrastin.packages/webexte mbështjell këtë bërthamë me WebExtension APIs: lifecycle-in e content-it, regjistrimin dinamik të scripts, UI-n e opsioneve, wiring-un e background-it dhe toolbar action.extensions/<engine>mban entry points për secilin engine dhemanifest.json-et e tyre.toolingmban skriptet e build-it, version-stamping dhe gjenerimit të ikonave.featuresmban skenarët Cucumber BDD dhe doubles-et e testimit.
Skema e depozitës
packages/core — bërthama e pastër
Asnjë modul këtu nuk importon extension API, si browser.*, chrome.* ose
messenger.*, dhe asnjë nuk përdor string-to-markup DOM sinks si
innerHTML ose insertAdjacentHTML. Ky kontratë zbatohet mekanikisht nga
packages/core/test/purity.test.ts, i cili mbledh src/**/*.ts, heq trupat
e komenteve me stripComments dhe verifikon që as
EXTENSION_API_PATTERN dhe as UNSAFE_DOM_PATTERN të mos përputhen me
asnjë modul. Sipërfaqja publike rieksportohet nga
packages/core/src/index.ts, ndërsa varësia e vetme runtime është tldts.
Modulet kryesore janë:
packages/core/src/config.ts— skemaConfigdheSiteRule, vlerat e parazgjedhura dhe funnel-i i vetëm i validimitnormaliseConfigpackages/core/src/matchPattern.ts— gramatika e match pattern-it, matcher-i dhe rregullat e specificitetitpackages/core/src/linkProcessor.ts— annotator-i në nivel dokumentipackages/core/src/hostMismatch.ts— kontrolli i mospërputhjes së domenit registrablepackages/core/src/styles.tsdhepackages/core/src/contrast.ts— stylesheet injectable dhe logjika e kontrastitpackages/core/src/safeColour.ts— predikatiisSafeColour
packages/webext — mbështjellësit e browser API-së
Këto module MUND të përdorin browser.* dhe janë i vetmi vend ku preken
WebExtension APIs. Sipërfaqja publike rieksportohet nga
packages/webext/src/index.ts. Këtu gjenden content.ts,
contentRegistration.ts, storage.ts, background.ts, toolbarAction.ts,
messageDisplay.ts, messageDisplayBackground.ts dhe options/options.ts.
extensions/<engine> — pikat e holla të hyrjes dhe manifestet
Çdo engine ka manifest.json-in dhe icons/ e vet. Chrome, Firefox dhe
Thunderbird mbajnë edhe një src/ me entry points. Edge, Opera dhe Safari
mbajnë vetëm manifest dhe ikona dhe ripërdorin src/ e Chrome-it. Për
shembull extensions/chrome/src/content.ts përmban vetëm
void createContentController().bootstrap();, ndërsa
extensions/chrome/src/background.ts thërret registerBackground,
registerContentReconciliation dhe registerToolbarToggle.
extensions/thunderbird/src/background.ts thërret registerBackground dhe
registerMessageDisplay. extensions/_template është scaffold, jo build target.
tooling, features dhe smoke tests
tooling përfshin build.mjs, version.mjs dhe icons.mjs. features
përfshin skenarët Gherkin, step definitions dhe doubles-et besnike në
features/support/webext.ts dhe features/support/world.ts. Çdo engine ka
gjithashtu extensions/<engine>/test/manifest.smoke.test.mjs, ndërsa
_template përdor template.smoke.test.mjs.
Rrjedha e zbulimit
Rruga e content-it fillon te një entry point engine, si
extensions/chrome/src/content.ts, i cili thërret
createContentController().bootstrap() nga packages/webext/src/content.ts.
Bootstrap
createContentController bootstrap ekzekutohet një herë për çdo frame.
Ai e pretendon frame-in para çdo await me claimBootstrap, ngarkon
config-un me loadConfigWithRetry, injekton stylesheet-in me injectStyles
vetëm pasi leximi të ketë sukses, dhe rikthen marker-in me
releaseBootstrap nëse ngarkimi dështon plotësisht. Kur config.enabled
është aktiv thërret start, ndërsa në çdo rast regjistron një listener
onConfigChanged.
Zgjidhja e rregullit të site-it aktiv
start dhe applyConfig përdorin resolveSiteRule për të vendosur nëse dhe
ku do të bëhet annotation. Fillimisht provohet vetë lokacioni i dokumentit me
resolveForHref, i cili filtron config.sites në rregullat e aktivizuara dhe
ia lë zgjedhjen selectMostSpecific. Nëse dokumenti nuk përputhet dhe
origjina e tij është opaque ose inherited, siç e kontrollon
hasOpaqueOrigin, përdoret fallback përmes readTopHref, readOpenerHref
dhe readReferrer, duke u mbrojtur nga leximet cross-origin.
Kufizimi te rrënja e përmbajtjes dhe vrojtimi i ndryshimeve
start ruan contentRoot, krijon annotator me createAnnotator(config) dhe
proceson çdo rrënjë të përputhur nga selectRoots(doc, contentRoot), i cili
e mbështjell querySelectorAll me try/catch që një selector i pavlefshëm të
dështojë në mënyrë të sigurt te []. Pastaj vëzhgon doc.body me
OBSERVER_OPTIONS. processMutations filtrin çdo mutacion sipas root-it
dhe processAddedNode mbulon nyje brenda root-it, vetë root-in, ose nyje që
e përmbajnë atë. applyConfig bën teardown kur s'ka rregull aktiv, restart
kur ndryshon contentRoot, dhe ndryshe vetëm reflow.
Shënimi i një lidhjeje të vetme
Annotator-i jeton te packages/core/src/linkProcessor.ts. createAnnotator
kthen një Annotator me një Map, një token data-ru nga
generateToken dhe config-un aktual. process(root) pastron entries jo më
të lidhura, mbledh a[href] me collectAnchors, zgjidh URL-të me
resolveAnnotatableUrl, përdor fast path-in isAnnotationIntact kur render-i
është i paprekur dhe llogarit mismatch-in me hostMismatch përpara se të
thërrasë annotateAnchor.
annotateAnchor renderon vetëm SAFE-DOM. Në mënyrën "inline" shton
<span class="reveal-urls-url"> me textContent dhe një <br>. Në mënyrën
"title" ruan title ekzistues, e zëvendëson me URL-në dhe, në rast
mospërputhjeje të theksuar, shton <span class="reveal-urls-warn"> me
REVEAL_URLS_WARN_LABEL. Nyjet e krijuara shënohen me data-ru="<token>",
dhe configFingerprint përdoret për të detyruar reflow kur duhet.
truncateUrl punon mbi [...displayHref] dhe përdor URL_ELLIPSIS.
Mospërputhja e host-it
packages/core/src/hostMismatch.ts hostMismatch krahason tekstin e
dukshëm të lidhjes me href-in e saj në nivel domeni registrable, jo në
nivel hosti të papërpunuar. extractHostCandidates i ndan token-et dhe i
ul me hostCandidate, i cili kërkon një pikë dhe një public suffix të
njohur nga tldts. Hosti i href-it dhe kandidatët reduktohen me
tldts.getDomain. Çdo kandidat me domen tjetër prodhon mismatch.
Stilet dhe kontrasti
packages/core/src/styles.ts mban REVEAL_URLS_CSS dhe helper-at
applyColour, applyFontOverrides dhe applyContrastBackdrop.
applyContrastBackdrop lexon ngjyrën e llogaritur të elementit dhe sfondin e
parë opaque duke ecur lart me getComputedStyle, pastaj përdor
needsWhiteBackdrop nga packages/core/src/contrast.ts për të vendosur nëse
duhet style.backgroundColor = "white". contrast.ts jep
parseColour, relativeLuminance dhe contrastRatio.
Hostet e konfigurueshme dhe kufizimi i përmbajtjes
Një SiteRule në packages/core/src/config.ts përcakton KU ekzekutohet
annotation me match dhe allFrames, dhe CILI container e kufizon atë me
contentRoot, bashkë me enabled dhe shenjën builtin. DEFAULT_SITES
mbulojnë Gmail, Proton me allFrames dhe të dy hostet e Outlook-it.
Config bashkon toggles globale, colours, font overrides dhe listën sites.
Mekanizmi i match pattern-it
packages/core/src/matchPattern.ts mban gramatikën restrictive
MATCH_PATTERN: vetëm http dhe https, wildcard host opsional *., glob path,
pa port, pa * scheme dhe pa <all_urls>. matchesPattern provon një URL
kundrejt një pattern-i të validuar dhe ia delegon rrugën pathGlobMatches.
parsePatternParts e ndan pattern-in në host, path, scheme dhe
wildcardHost.
Rregullat që mbivendosen zgjidhen sipas parimit "most specific wins".
compareSiteSpecificity rendit hostin ekzakt mbi wildcard-in, pastaj hostin
më të gjatë literal, pastaj path-in më literal me literalPathLength, e në
fund një ASCII tiebreak. selectMostSpecific kthen rregullin më specifik të
aktivizuar që përputhet me URL-në.
permissions.getAll() mund të raportojë grants në gramatikën e plotë të
WebExtension-it (<all_urls>, *://*/*, https://*/*, *://*.host/*,
https://host/*) që MATCH_PATTERN do t'i refuzonte. parseGrantedOrigin
i kthen ato në GrantedOriginParts, originCovers kontrollon mbulimin mbi
match, ndërsa matchAllowsOriginFallback raporton nëse path-i është saktësisht
ORIGIN_FALLBACK_PATH, pra /*.
Regjistrimi dinamik
packages/webext/src/contentRegistration.ts regjistron nga një dynamic
content script për çdo rregull të shtuar nga përdoruesi. Built-ins
shërbehen nga static content_scripts entries dhe nuk regjistrohen kurrë
dinamikisht. desiredContentScripts filtron config.sites, përdor
isOriginGranted, originCovers dhe contentScriptId, dhe e vendos
matchOriginAsFallback: true vetëm kur lejohet nga
matchAllowsOriginFallback(rule.match).
reconcileContentScriptsOnce lexon origjinat e miratuara, config-un e ruajtur
dhe regjistrimet live dhe i pajton ato me diff të vetëdijshëm për
properties. sameRegistration normalizon descriptor-in e dëshiruar dhe
read-back-un live. Meqë nuk ka updateContentScripts, një script i ndryshuar
hiqet dhe regjistrohet sërish. reconcileContentScripts përdor një promise
inFlight për të serializuar trigger-at që mund të ndodhin afër njëri-tjetrit.
Rrjedha e lejes së shprehur
Manifestet statike deklarojnë optional_host_permissions (http://*/*,
https://*/*) që përdoruesi të mund të japë në runtime leje për një host
shtesë. packages/webext/src/options/options.ts addSite validon të gjithë
rregullin kandidat përmes normaliseSiteRule, nxjerr origin-in me
matchOrigin dhe thërret browser.permissions.request nga click gesture-i i
butonit Add. Vetëm pas miratimit shton një rresht builtin:false.
Config, storage dhe modeli kanonik
Funneli i vetëm i validimit
normaliseConfig në packages/core/src/config.ts është rruga e vetme
kanonike e leximit. Ai konverton dhe kufizon çdo fushë, kthehet te vlera e
parazgjedhur kur ndesh diçka të pavlefshme dhe nuk hedh kurrë exception. Ai
ndërtohet nga validatorë si normaliseBoolean, normaliseRenderMode,
normaliseMaxLength, normaliseIgnoreHosts, normaliseMatchColour,
normaliseMismatchColour, normaliseCssSize, normaliseFontWeight,
normaliseContentRoot, normaliseMatchPattern dhe normaliseSites.
normaliseSites heq vlera të pavlefshme, i heq dublikatat sipas match,
detyrimisht vendos builtin: true kur një rregull përputhet me built-in dhe
risjell built-ins që mungojnë.
Ruajtja
packages/webext/src/storage.ts mbështjell WebExtension storage API.
configArea përdor browser.storage.sync kur është i disponueshëm dhe bën
fallback në browser.storage.local, p.sh. në Thunderbird. getConfig,
setConfig dhe onConfigChanged e kalojnë gjithmonë vlerën e tyre nëpërmjet
normaliseConfig, prandaj një store i prishur nuk mund të dorëzojë kurrë një
Config të pavlefshëm. Dy add-on-et ripërdorin të njëjtin funnel mbi
storage-et e tyre pritëse.
Modeli i config-ut kanonik dhe përditësimet e synuara
Tre rrugë shkrimi lexojnë config-un kanonik, ndryshojnë saktësisht një gjë dhe e shkruajnë sërish, pa ruajtur form edits të papërfunduara:
removeSitenëoptions.tsaddSitenëoptions.tstoggleEnablednëoptions.ts
toggleEnabled te packages/webext/src/toolbarAction.ts ndjek të njëjtin
model edhe nga ana e background-it.
Build-i sipas engine-it dhe kontrata e manifestit
tooling/build.mjs drejton një build të vetëm esbuild për çdo target të
WebExtension-it. Descriptor-i TARGETS përcakton SOURCE TARGET për secilin
target. Chrome, Firefox dhe Thunderbird kanë src/ e tyre. Edge, Opera dhe
Safari përdorin chrome si source. Outlook add-in ka source-in e vet.
ACTIVE_TARGETS, domethënë Chrome, Edge, Firefox, Opera dhe Thunderbird,
janë ato që ndërtohen me --all dhe --package. Safari dhe Outlook ndërtohen
vetëm kur emërtohen shprehimisht, ndërsa Gmail përdor make build-gmail.
Bundle-i i Gmail-it synon runtime-in Apps Script V8, i cili nuk ka as ES
modules dhe as URL natyror. Prandaj ndërtohet si ESM dhe pastaj i hiqet
export {…}, në mënyrë që funksionet trigger të mbeten globals në nivel
moduli. Një polyfill i vogël për URL përfshihet vetëm kur duhet.
buildTarget pastron dist/<target>/, kopjon manifest.json dhe icons/,
bën bundle të scripts si IIFE dhe faqes së opsioneve si ESM, dhe kopjon
options.html dhe options.css.
tooling/version.mjs stamp-on versionet. Ai merr MAJOR.MINOR nga root
package.json, gjen BUILD number më të lartë ekzistues dhe e shton atë në
çdo manifest target. Outlook add-in mban si manifest.json ashtu edhe
manifest.xml. tooling/icons.mjs, i ekzekutuar me make icons, bën
rasterise të assets/icon.svg në icon48.png dhe icon128.png.
Kontrata e manifestit përcaktohet te
extensions/<engine>/test/manifest.smoke.test.mjs, ku verifikohet forma e
saktë e secilit manifest dhe ekzistenca e çdo skedari të referuar.
Ndërkombëtarizimi (i18n)
Reveal URLs është i lokalizuar në shumë gjuhë përveç anglishtes. Burimi i
vetëm i këtij grupi është packages/core/src/locales.json, i cili përmban
SUPPORTED_LOCALES, emrat vendorë dhe gjuhën e parazgjedhur. Çdo string
jo-anglisht është machine-translated dhe pret rishikim njerëzor. Marker-i i
proveniencës ndryshon sipas formatit: _locales e mbajnë në description,
site catalogues e regjistrojnë në site/i18n/README.md, ndërsa docs të
përkthyera mbajnë komentin HTML në rreshtin e parë.
Janë tri sipërfaqe të pavarura lokalizimi:
- The options page (AD-1). Markup-u statik dërgohet në anglisht, por çdo
nyje e përkthyeshme mban
data-i18n="<key>"osedata-i18n-<attr>="<key>". Në runtime,packages/webext/src/options/locale.tsngarkon_locales/<code>/messages.json, rishkruan tekstin metextContentdhe vendos<html lang>. - The website chrome (AD-2).
tooling/site.mjse renderon chrome-in e faqes në anglisht medata-i18ndhe<html lang="en-GB">. Pastajsite/scripts/i18n.mjse zëvendëson atë me gjuhën e vizitorit ngasite/i18n/<code>.json. - The documentation pages (AD-8/AD-9). Faqet e dokumentacionit
renderohen për çdo locale në build time, jo në runtime. Ato përdorin
filename të lokalizuar nga
packages/core/src/site-page-names.json, sinl/handleiding.html. - Internal-link localisation (Finding 2). Lidhjet e brendshme të docs
mbajnë
data-doc-link="<page>"dhe runtime loader i ridrejton te varianti i të njëjtit locale, duke ruajtur#hashdhe?query.
Fshehja e “Sajteve aktive” në klientin e postës (AD-3)
Një target mail client, sot Thunderbird, i sheh tashmë të gjitha mesazhet e
renderuara, kështu që editor-i “Active sites” sipas host-it është i tepërt.
Vendimi bartet nga mailClient: true te tooling/build.mjs.
- Build-time.
copyAssetskontrollondescriptorFor(target).mailClientdhe, kur ai është i vërtetë, heq<section class="sites">meremoveSitesSection(html). - Runtime.
packages/webext/src/options/options.tse toleron mungesën e këtij seksioni, nuk e hedhOptionsFieldMissingErrordhe e rikthen vlerën e ruajtur tësitespara normalizimit që të mos humbasin hostet e konfiguruara.
Qëndrimi për sigurinë dhe privatësinë
- Pa rrjet dhe pa nxjerrje të dhënash. Asnjë modul nuk bën
fetch,XHRose thirrje të tjera rrjeti. Zgjerimi lexon vetëm config-un e ruajtur dhe DOM-in e faqes. - Vetëm SAFE-DOM. Annotation dhe stylesheet-i i injektuar përdorin vetëm
createElement,textContentdhestyle.*, joinnerHTML,insertAdjacentHTMLoseeval. - I gjithë input-i normalizohet. Çdo lexim dhe shkrim kalon nga
normaliseConfig. Ngjyrat kalojnë ngaisSafeColour, match patterns ngaMATCH_PATTERN, dhe content-root selectors kufizohen ngaCONTENT_ROOT_PATTERN. - Privilegji minimal. Origjinat built-in të webmail-it jetojnë te
host_permissions. Hostet shtesë janë opt-in përmesoptional_host_permissionsdhepermissions.request. - Mbledhja e të dhënave në Gecko. Manifestet e Firefox-it dhe
Thunderbird-it deklarojnë
browser_specific_settings.gecko.data_collection_permissions.required: ["none"]. - Licenca. Projekti është AGPL-3.0-only, siç e deklarojnë
LICENSEdhepackage.json.
Testimi
- Vitest unit tests mbulojnë të dy pakot:
packages/core/test/*dhepackages/webext/test/*. - Cucumber BDD scenarët jetojnë te
features/dhe përdorinfeatures/support/webext.tsdhefeatures/support/world.ts. - Per-target manifest smoke tests te
extensions/<engine>/test/manifest.smoke.test.mjspërdorinnode:testdhe kontrollojnë formën e manifestit dhe skedarët e referuar. - CI te
.github/workflows/ci.ymlndalon çdo push dhe pull request nëse nuk kalojnëmake test,make bdddhemake lint. Version-stamping dhe packaging jetojnë te.github/workflows/release.yml.