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:

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/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ë SiteRulepackages/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

normaliseConfigpackages/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:

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:

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.

Qëndrimi për sigurinë dhe privatësinë

Testimi