Sulla carta, una moderna estensione per browser dovrebbe essere quasi automaticamente un prodotto multipiattaforma. Chrome, Firefox, Edge e Safari supportano WebExtensions, utilizzano un file manifest.json simile e mettono a disposizione meccanismi comparabili: content script, storage, comunicazione tra contesti, pulsante nella barra degli strumenti e pagine delle impostazioni.
È vero, ma solo fino a un certo punto.
Dopo aver pubblicato e portato nove estensioni POLPROG, sappiamo che codice condiviso non significa prodotto identico. Gran parte della logica di business può essere riutilizzata, ma il comportamento del browser, il ciclo di vita del processo in background, l'interfaccia, i permessi, il packaging e la revisione dello store rimangono problemi separati.
Il portfolio comprende:
- AutoScroll,
- ClickClean,
- DevScope,
- LeakLens,
- LoopIt,
- Loudly,
- OneMoreJump,
- TabZoo,
- TubePilot.
Non si tratta di nove copie dello stesso tipo di add-on. Il gruppo include strumenti per video, amplificazione audio, privacy, pulizia dei dati, sviluppo, gestione delle schede e un gioco leggero. I problemi, di conseguenza, non riguardavano una sola API. Hanno coinvolto service worker, popup, content script, audio, storage, icone, permessi e processo di pubblicazione.
POLPROG mantiene pubblicamente nove prodotti di tipo estensione, pubblicati inizialmente soprattutto per Chrome e Firefox e successivamente portati anche su Edge e Safari. [1][2]
Questo articolo non è un benchmark di laboratorio. Non abbiamo registrato dal primo commit ogni minuto speso per ciascuno store, quindi non affermeremo che Safari costa esattamente 2,7 volte più di Chrome o che il 94% del codice è condiviso. Sarebbe una precisione fittizia.
Possiamo però mostrare qualcosa di più utile: quali ipotesi si sono rivelate errate, dove sono comparsi problemi reali e come progetteremmo oggi l'intero processo partendo da zero.
Ultima verifica delle fonti: 17 luglio 2026.
Quattro browser, quattro ruoli diversi
| Piattaforma | Ruolo nel nostro processo | Vantaggio principale | Insidia più comune |
|---|---|---|---|
| Chrome | Base principale di compatibilità Manifest V3 | Documentazione più ampia e mercato delle estensioni più grande | Service worker sospeso e requisiti severi sui permessi |
| Edge | Porting basato su Chromium | Differenza minima di codice rispetto a Chrome | Partner Center, metadati e certificazione separati |
| Firefox | Implementazione indipendente di WebExtensions | Evidenzia bene le ipotesi dipendenti da Chromium | Differenze di API e manifest, firma e requisiti sui sorgenti |
| Safari | Prodotto separato nell'ecosistema Apple | Accesso agli utenti macOS, iOS e iPadOS | Xcode, app contenitore, firma e comportamento runtime differente |
MDN sottolinea che l'obiettivo di WebExtensions è consentire agli add-on di funzionare nei principali browser con modifiche minime, ma documenta anche differenze importanti tra Chrome, Firefox e Safari riguardo disponibilità delle API, manifest e gestione delle operazioni asincrone. [7][8]
La descrizione corrisponde bene alla nostra esperienza. Le “modifiche minime” sono realistiche per un add-on semplice. Più un'estensione interagisce con la pagina, i media, il ciclo di vita delle schede o il processo in background, meno utile diventa la parola “minime”.
Lezione 1: una codebase sì, un unico manifest non necessariamente
Il miglior punto di partenza è una logica condivisa con configurazioni di build separate.
Nella pratica, la struttura più comoda è quella in cui vengono condivisi:
- logica delle funzionalità,
- modelli dei dati,
- moduli di storage,
- validazione,
- gran parte dell'interfaccia del popup e delle impostazioni,
- localizzazioni,
- test unitari.
Rimangono separati:
- varianti del manifest,
- identificatori degli add-on,
- versioni minime dei browser,
- configurazione del background,
- parte dei permessi,
- icone e asset dello store,
- packaging,
- firma,
- script di pubblicazione.
Non significa mantenere quattro directory copiate manualmente. Questo porta rapidamente a divergenze. Un modello migliore utilizza sorgenti condivisi e un generatore o livello di configurazione che prepara un artefatto separato per ciascuna piattaforma.
Firefox, per esempio, richiede impostazioni proprie in browser_specific_settings. In Manifest V3 l'ID dell'estensione è necessario per la firma e, da novembre 2025, i nuovi add-on inviati ad AMO devono anche dichiarare le impostazioni relative alla raccolta dei dati. [9]
Safari richiede invece un progetto Xcode e un'applicazione che contenga l'estensione. Apple fornisce un convertitore WebExtension, ma il risultato deve comunque essere trattato come un progetto nativo destinato alla firma e alla distribuzione tramite App Store. [3][4]
Un manifest comune sarebbe quindi un risparmio artificiale. È più semplice mantenere alcune piccole differenze esplicite che un unico file pieno di eccezioni e condizioni.
Lezione 2: Manifest V3 non ha uniformato il comportamento dei browser
Manifest V3 ha migliorato parte della compatibilità e organizzato il modello delle estensioni, ma non ha creato un runtime identico.
Il cambiamento più visibile in Chrome è la sostituzione delle pagine persistenti in background con un extension service worker. Google lo descrive come un gestore centrale degli eventi avviato quando necessario. Il processo può essere terminato e le variabili conservate soltanto in memoria scompaiono con esso. [5][6]
Questo ha conseguenze dirette.
Codice come:
let activeSessions = new Map();
let currentTab = null;
let isEnabled = false;
non può essere l'unica fonte di verità se i dati servono dopo il riavvio del service worker.
Lo stato dovrebbe essere diviso in tre gruppi:
- stato temporaneo che può scomparire senza rischi;
- stato ricostruibile dalle schede attive o dal DOM;
- stato persistente che deve essere scritto in
storage.local,storage.sessiono in un'altra posizione appropriata.
La registrazione dei listener è altrettanto importante. Chrome raccomanda di dichiarare gli handler degli eventi nello scope globale dello script, affinché vengano registrati in modo sincrono quando il service worker viene riattivato. Un listener creato soltanto dopo un'inizializzazione asincrona può perdere proprio l'evento che ha risvegliato il worker. [10]
Durante lo sviluppo è facile non accorgersene. DevTools aperto può influenzare il ciclo di vita del service worker, mentre il bug compare solo nell'uso normale. Google ha descritto problemi simili in un'analisi delle migrazioni a MV3: la sospensione del processo può interrompere i timer ed eliminare lo stato conservato in memoria. [11]
La regola principale è quindi:
Il processo in background di un'estensione dovrebbe essere progettato come un gestore di eventi interrompibile, non come un piccolo server Node.js sempre attivo.
Lezione 3: Chrome è un buon punto di partenza, ma un cattivo unico ambiente di test
Chrome è la base naturale per un progetto Manifest V3. Offre ampia documentazione, esempi, strumenti di sviluppo e il più grande ecosistema di add-on.
Allo stesso tempo, testare esclusivamente in Chrome crea una falsa sensazione di compatibilità.
Se il codice funziona in Chrome ed Edge, conferma soprattutto la compatibilità con Chromium. Non conferma:
- comportamento corretto in Gecko,
- comportamento corretto in WebKit,
- resistenza a un differente ciclo di vita del popup,
- funzionamento con un altro modello di permessi,
- corretto ridimensionamento delle icone,
- corretto comportamento audio,
- semantica identica delle API.
Chrome Web Store richiede che un'estensione abbia uno scopo unico, limitato e facile da comprendere. Richiede inoltre il minimo insieme di permessi, la giustificazione di ciascuno e dichiarazioni coerenti sulle pratiche relative ai dati. Permessi troppo ampi possono portare al rifiuto. In Manifest V3 non è inoltre consentito caricare ed eseguire codice ospitato da remoto. [12]
Questi requisiti influenzano l'architettura prima di quanto possa sembrare. Aggiungere “per sicurezza” l'accesso a tutti i siti non è neutro. Successivamente bisognerà giustificarlo davanti al revisore e all'utente. Una soluzione migliore può essere activeTab, host permissions opzionali o accesso limitato a domini specifici.
Chrome ci ha quindi insegnato due cose opposte:
- è un buon punto per definire la variante base MV3;
- non dovrebbe determinare tutte le ipotesi del livello condiviso.
Lezione 4: Edge è il porting tecnico più semplice, ma non è un canale di distribuzione gratuito
Microsoft descrive ufficialmente il porting di un'estensione Chrome esistente verso Edge come uno scenario separato e supportato. Poiché entrambi i browser utilizzano Chromium, le differenze nel codice di base sono spesso ridotte. [13]
Nei nostri progetti Edge ha generalmente richiesto il minor numero di modifiche tecniche. Questo non significava però pubblicazione “con un clic”.
L'estensione viene caricata in un Partner Center separato, nel quale occorre:
- inviare un pacchetto ZIP dedicato,
- definire disponibilità e mercati,
- scegliere una categoria,
- descrivere lo scopo unico,
- giustificare ogni permesso,
- dichiarare l'uso di codice remoto,
- descrivere le pratiche relative ai dati,
- preparare listing per le diverse lingue,
- caricare immagini e screenshot separati,
- aggiungere note per il team di certificazione. [14]
Come Google, Microsoft richiede uno scopo chiaramente definito e il minor insieme necessario di permessi. Partner Center avverte inoltre che dichiarazioni incomplete o incoerenti possono allungare la certificazione o portare al rifiuto. [14]
Edge è quindi un buon esempio della differenza tra costo del porting e costo del canale.
Il codice può essere quasi identico, ma occorre comunque mantenere:
- una scheda prodotto separata,
- uno stato di revisione separato,
- metadati separati,
- varianti grafiche separate,
- un processo di aggiornamento separato,
- coerenza tra le dichiarazioni sulla privacy.
Per una sola estensione si tratta di un carico moderato. Per nove prodotti diventa un processo operativo ricorrente.
Lezione 5: Firefox individua meglio il codice scritto “per errore soltanto per Chrome”
Firefox utilizza WebExtensions, ma non è Chromium. È proprio questo a renderlo un ambiente di test così prezioso.
La differenza più nota riguarda namespace e codice asincrono. Storicamente Chrome utilizzava chrome e callback, mentre Firefox e Safari usavano browser e promise. In Manifest V3 Chrome ed Edge hanno aggiunto il supporto alle promise per molti metodi, ma non tutte le differenze sono scomparse. Mozilla raccomanda di scrivere il codice con browser e promise, utilizzando WebExtension browser API Polyfill quando necessario. [8][15]
Non conviene distribuire nel progetto condizioni come:
if (isFirefox) {
// ...
} else {
// ...
}
intorno a ogni chiamata API. È meglio creare un piccolo livello di adattamento:
export const extensionApi = {
storageGet: (keys) => browser.storage.local.get(keys),
storageSet: (value) => browser.storage.local.set(value),
sendMessage: (message) => browser.runtime.sendMessage(message),
};
e risolvere le differenze in un unico punto.
Firefox impone inoltre maggiore disciplina nella pubblicazione. Le estensioni devono essere firmate anche quando vengono distribuite in modo indipendente nella configurazione standard. AMO valida il pacchetto e codice minificato o difficile da leggere può richiedere l'invio dell'intero pacchetto sorgente. Mozilla può anche effettuare una revisione manuale dopo la pubblicazione e rifiutare la versione corrente o una precedente. [16][17][18]
Dal punto di vista della qualità è un vantaggio. Il processo obbliga il build a essere:
- riproducibile,
- documentato,
- privo di offuscamento non necessario,
- coerente con il comportamento dichiarato,
- verificabile senza conoscenze interne dell'autore.
Firefox ha inoltre evidenziato problemi nei punti in cui Chromium era più permissivo. Riguardavano gestione dei messaggi, attesa di una risposta promise, differenze nei popup e ipotesi sulla presenza di determinate proprietà API.
La lezione principale:
Firefox non è una casella aggiuntiva da selezionare dopo la fine del progetto. È un secondo runtime indipendente che dovrebbe entrare presto nei test.
Lezione 6: Safari non è un porting di store, ma una fase separata del prodotto
Apple supporta WebExtensions e mette a disposizione uno strumento per convertire estensioni da Chrome, Firefox o Edge. Nonostante ciò, il processo Safari è sostanzialmente differente. [3][4]
L'estensione diventa parte di un'applicazione creata in Xcode. Occorre gestire:
- il progetto dell'app,
- il target dell'estensione,
- il bundle identifier dell'app,
- un bundle identifier separato per l'estensione,
- la firma,
- profili e certificati,
- l'icona dell'applicazione,
- gli entitlement,
- l'archiviazione,
- App Store Connect,
- la revisione App Store.
Apple richiede che l'estensione funzioni con la versione corrente di Safari per il relativo sistema, non interferisca con l'interfaccia di Safari e non richieda accesso a più siti di quanto necessario. [19]
È qui che sono comparsi i problemi più concreti nel nostro portfolio.
Loudly: l'interfaccia funzionava, ma l'audio non veniva amplificato
Loudly modifica l'audio riprodotto in una pagina. Nella versione Safari si poteva vedere l'interfaccia, cambiare le impostazioni e avere l'impressione che il prodotto funzionasse. Il segnale audio reale, però, non veniva amplificato.
È una tipologia di bug peggiore di un crash evidente. Lo smoke test “il popup si apre” passa, ma la promessa principale del prodotto non viene mantenuta.
Conclusione: per le estensioni multimediali il test funzionale deve misurare l'effetto finale, non soltanto lo stato dell'interfaccia.
TabZoo: una schermata bianca al posto dell'applicazione
La versione Safari di TabZoo poteva mostrare una pagina bianca vuota. Il problema può derivare da un errore di inizializzazione, dai percorsi degli asset, da un contesto sbagliato o da un'eccezione prima del rendering.
In Chromium un errore simile può restare invisibile se l'ordine di avvio o la disponibilità delle API sono differenti.
Conclusione: ogni vista dell'estensione dovrebbe avere un meccanismo diagnostico minimo e una gestione degli errori di inizializzazione. Una pagina bianca non può essere lo stato finale del prodotto.
LoopIt: un clic apriva due interfacce
In LoopIt, facendo clic sull'icona in Safari comparivano un popup e un ulteriore pannello dell'estensione. Era un conflitto tra un popup configurato in modo dichiarativo e la gestione manuale del clic.
In un browser il comportamento sembrava corretto, in un altro causava una doppia reazione.
Conclusione: bisogna stabilire chiaramente chi gestisce il clic sul pulsante. O il pulsante ha default_popup, oppure l'evento viene gestito dal codice. Combinare i due meccanismi richiede una progettazione molto consapevole.
Questi tre casi mostrano perché l'esecuzione del convertitore Apple non completa il porting. Il convertitore trasferisce la struttura. Non conferma la semantica del comportamento.
Lezione 7: le estensioni video e audio sono più difficili di quanto suggerisca il popup
Quattro dei nostri prodotti dipendono fortemente dai contenuti multimediali:
- AutoScroll,
- LoopIt,
- Loudly,
- TubePilot.
Ognuno deve coordinare diversi mondi:
- il DOM della pagina,
- l'elemento audio o video,
- il content script,
- il popup,
- il processo in background,
- le impostazioni salvate,
- la navigazione SPA,
- il cambio della scheda attiva.
La maggior parte dei bug nasce non in una singola funzione, ma al confine tra questi contesti.
Uno scenario di esempio:
- l'utente apre il popup;
- il popup chiede al content script lo stato corrente;
- la pagina ha appena cambiato video senza un reload completo;
- il vecchio elemento video è stato rimosso;
- il content script conserva ancora un riferimento all'elemento precedente;
- il popup mostra un valore corretto, ma modifica un player inattivo.
Aggiungere un altro setTimeout non risolve il problema. Serve un modello di stato esplicito:
- identificare l'elemento attivo,
- osservare i cambiamenti del DOM,
- ricollegare gli handler,
- confermare l'esecuzione del comando,
- sincronizzare l'interfaccia soltanto dopo la risposta,
- ripristinare lo stato dopo il riavvio del processo in background.
Per servizi come YouTube bisogna inoltre considerare la navigazione SPA. Il cambio di video non provoca sempre il reload completo del documento, quindi una sola inizializzazione del content script non è sufficiente.
Lezione 8: icona e popup fanno parte dell'implementazione, non soltanto del branding
Un build multipiattaforma può essere tecnicamente corretto e apparire comunque sbagliato.
Nei nostri test sono comparse icone:
- troppo grandi rispetto allo spazio disponibile,
- prive di margine di sicurezza,
- a contatto con i bordi,
- visivamente più piccole delle icone di altre estensioni nonostante le stesse dimensioni del file,
- corrette nello store ma poco leggibili nella barra del browser.
Le dimensioni del PNG non risolvono il problema. Conta la dimensione visiva del simbolo all'interno del canvas.
Lo stesso vale per i popup. La stessa larghezza CSS può essere visualizzata in modo diverso a causa di:
- rendering dei font,
- stili predefiniti dei controlli dei form,
- comportamento della scrollbar,
- calcolo dell'altezza,
- margini di sistema,
- ridimensionamento dello schermo,
- modalità chiara e scura.
Per questo oggi prepariamo gli asset come insieme, non come singola immagine:
- SVG sorgente,
- varianti raster richieste dal manifest,
- safe area controllata,
- icona dell'app Safari,
- immagini per il listing,
- screenshot dello store.
L'interfaccia dell'estensione dovrebbe inoltre avere dimensioni minime e massime e mantenere la leggibilità quando cambia la lunghezza delle traduzioni.
Lezione 9: la pubblicazione è un prodotto operativo separato
Il più grande errore organizzativo sarebbe credere che il lavoro finisca dopo aver generato il file ZIP.
Ogni store ha un proprio modello di fiducia.
Chrome Web Store
Chrome richiede uno scopo singolo e chiaro, permessi minimi, dichiarazioni sui dati e un'informativa privacy. In Manifest V3 è vietato eseguire codice remoto. [12]
La descrizione del prodotto deve corrispondere al comportamento reale. Se il listing promette una funzione che il revisore non riesce a eseguire, non si tratta di un problema di marketing, ma di certificazione.
Firefox Add-ons
AMO valida automaticamente il pacchetto, può richiedere i sorgenti del codice compilato e mantiene la possibilità di una revisione manuale successiva. Vale la pena correggere gli avvisi di sicurezza e privacy anche quando tecnicamente si potrebbe proseguire. [16][17]
Microsoft Edge Add-ons
Partner Center richiede informazioni separate per disponibilità, privacy e listing linguistici. Ogni permesso dovrebbe essere giustificato e le informazioni devono essere coerenti con il manifest e con il comportamento dell'estensione. [14]
Safari e App Store
Safari aggiunge il livello applicazione, la firma, i metadati App Store e le linee guida applicabili alle app Apple. L'estensione deve funzionare sulla versione corrente di Safari e limitare l'accesso ai siti al minimo necessario. [19]
Di conseguenza, nove estensioni non significano nove listing. Con copertura completa di quattro piattaforme si arriva fino a 36 combinazioni prodotto-browser, senza contare versioni linguistiche e varianti separate per i dispositivi Apple.
È un motivo sufficiente per conservare i metadati nel repository.
Lezione 10: serve una matrice di test, non una nota “controllare l'estensione”
Per ogni estensione manteniamo lo stesso nucleo di test, quindi aggiungiamo i casi legati alla relativa funzione.
Smoke test minimo
Dopo ogni build verifichiamo:
- installazione pulita;
- aggiornamento dalla versione precedente;
- comparsa dell'icona;
- apertura del popup;
- assenza di errori di inizializzazione;
- salvataggio e rilettura delle impostazioni;
- funzionamento dopo chiusura e riavvio del browser;
- funzionamento dopo la sospensione del background;
- comportamento senza permesso per la pagina;
- comportamento su una pagina non supportata.
Test di comunicazione
Testiamo separatamente:
- popup → background,
- popup → content script,
- content script → background,
- risposta asincrona,
- assenza di scheda attiva,
- reload della scheda,
- navigazione SPA,
- reiniezione del content script,
- chiusura del popup prima della risposta.
Test specifico della funzione
Per Loudly non basta spostare il cursore. Bisogna confermare il cambiamento del livello audio.
Per LoopIt bisogna verificare che la riproduzione torni effettivamente nell'intervallo scelto.
Per AutoScroll bisogna testare layout differenti, fullscreen, cambio di video e scorrimento manuale.
Per ClickClean bisogna controllare l'effetto su veri dati di test, non solo il messaggio “completato”.
Per DevScope e LeakLens bisogna verificare il rapporto su siti con differenti policy CSP, numero di frame e set di header.
Test di piattaforma
Infine si aggiungono i casi specifici del browser:
- riavvio del service worker in Chromium,
- pacchetto Firefox firmato,
- host permissions in Firefox,
- installazione e attivazione dell'estensione in Safari,
- versioni macOS e iOS se supportate,
- aggiornamento tramite store anziché soltanto sideload locale.
Lezione 11: i test automatici non sostituiscono un vero browser QA
L'automazione è necessaria, ma ha dei limiti.
I test unitari coprono bene:
- logica di business,
- validazione,
- migrazioni dello storage,
- formattazione dei dati,
- regole di attivazione delle funzionalità.
I test di integrazione possono controllare:
- manifest,
- presenza dei file,
- comunicazione tra moduli,
- chiamate API di base,
- completezza delle localizzazioni.
I test E2E in Chromium individuano buona parte delle regressioni dell'interfaccia e dei content script.
Non confermano però che:
- Firefox interpreti tutti i permessi nello stesso modo,
- Safari modifichi correttamente l'audio,
- App Store avvii il target giusto,
- il popup non venga aperto due volte,
- l'icona appaia correttamente nella barra di sistema.
I bug più costosi nei nostri porting non erano facilmente individuabili con un test unitario. Erano problemi di integrazione tra estensione, browser, sito e sistema operativo.
Lezione 12: il livello di compatibilità dovrebbe descrivere capacità, non nomi dei browser
All'inizio del progetto è allettante una condizione come:
if (browserName === "safari") {
// comportamento speciale
}
A volte è inevitabile, ma non dovrebbe diventare il modello architetturale principale.
Domande migliori sono:
- l'ambiente supporta quella API?
- il popup è dichiarativo?
- il background è un service worker?
- un determinato metodo è disponibile?
- la funzione richiede un contenitore nativo?
- la pagina espone l'elemento multimediale necessario?
Il codice basato sulle capacità resiste meglio ai cambiamenti di versione ed è più facile da testare.
Un livello di piattaforma può esporre, per esempio:
export const capabilities = {
supportsSessionStorage: Boolean(browser.storage?.session),
supportsSidePanel: Boolean(browser.sidePanel),
usesServiceWorker: import.meta.env.BACKGROUND_MODE === "service-worker",
isSafariContainer: import.meta.env.TARGET === "safari",
};
Questo non elimina le differenze. Le rende esplicite e concentrate in un solo punto.
Lezione 13: un approccio privacy-first semplifica davvero la pubblicazione
Le estensioni POLPROG sono progettate senza analytics, tracking e backend esterno quando non sono necessari.
Non è soltanto una decisione di branding. Semplifica:
- descrizione del flusso dei dati,
- informativa privacy,
- giustificazione dei permessi,
- code review,
- rischio di perdita dei dati,
- coerenza tra listing e codice.
Questo non significa che un'estensione rispettosa della privacy non necessiti di documentazione. Se un add-on legge contenuto della pagina, cronologia, schede o cookie, deve spiegare chiaramente perché, anche quando nulla viene inviato a un server.
Chrome ed Edge richiedono la giustificazione dei permessi e la dichiarazione delle pratiche relative ai dati. Firefox richiede ai nuovi add-on informazioni sui permessi connessi alla raccolta di dati. Safari consiglia di limitare l'accesso ai siti al minimo necessario. [9][12][14][19]
Progettare con permessi minimi fin dal primo giorno è molto più semplice che eliminare permessi eccessivi dopo un rifiuto.
Lezione 14: la pipeline di release dovrebbe costruire prodotti, non un archivio final-final.zip
Con nove estensioni, il packaging manuale smette rapidamente di essere sicuro.
Una buona pipeline dovrebbe:
- controllare la versione;
- validare il manifest base;
- generare il manifest della piattaforma;
- eseguire il build;
- copiare le icone corrette;
- verificare l'assenza di codice remoto;
- eseguire i test;
- generare artefatti separati;
- creare checksum;
- preparare changelog e informazioni dello store.
Esempio di output:
dist/
loudly/
chrome/loudly-1.4.0.zip
edge/loudly-1.4.0.zip
firefox/loudly-1.4.0.xpi
safari/Loudly.xcarchive
La versione dovrebbe provenire da un'unica fonte di verità, ma gli artefatti non devono essere identici.
Conviene inoltre controllare automaticamente:
- file non consentiti,
- sourcemap,
- segreti,
- permessi inutilizzati,
- localizzazioni mancanti,
- percorso errato dell'icona,
- versioni incoerenti tra manifest.
L'automazione non elimina la revisione degli store. Elimina errori che non dovrebbero mai raggiungerli.
Quale browser si è rivelato più semplice?
La risposta dipende da ciò che si misura.
Il punto di partenza più semplice: Chrome
Più documentazione, esempi e strumenti. Una buona scelta per costruire la versione base MV3.
Il porting di codice più semplice: Edge
Se la versione Chrome è corretta, le differenze tecniche sono spesso minime. Rimane il lavoro di pubblicazione separato.
Il miglior test di indipendenza: Firefox
Non è sempre il più semplice, ma offre molto valore. Evidenzia il codice accidentalmente legato a Chromium e impone disciplina nel build.
Il maggiore sforzo complessivo: Safari
Non soltanto per l'API. Si aggiungono Xcode, app, firma, App Store e test sui dispositivi Apple. I problemi di Loudly, TabZoo e LoopIt hanno inoltre dimostrato che un'interfaccia funzionante non implica una funzione funzionante.
Cosa faremmo diversamente iniziando oggi?
1. Safari farebbe parte del piano dal primo sprint
Non dovrebbe ricevere subito tutte le funzionalità. Dovrebbe però esistere uno scheletro funzionante del progetto Xcode e test regolari del flusso di base.
2. Firefox entrerebbe prima nella CI
Non solo il build, ma anche test eseguiti in un vero Firefox.
3. Ogni funzionalità avrebbe un proprietario dello stato
Prima dell'implementazione stabiliremmo se lo stato appartiene al content script, al background, al popup o allo storage. Molti bug di comunicazione derivano dalla mancanza di una risposta chiara.
4. I permessi verrebbero progettati insieme alla funzione
Ogni nuovo permesso dovrebbe avere:
- giustificazione tecnica,
- testo per lo store,
- scenario di rifiuto,
- test del comportamento senza consenso.
5. Il listing sarebbe parte del repository
Descrizione, riepilogo breve, release note, termini di ricerca, informativa privacy e immagini dovrebbero avere una cronologia delle modifiche.
6. Misureremmo i dati dalla prima pubblicazione
La prossima fase del rapporto dovrebbe includere:
- tempo dall'invio alla pubblicazione,
- numero di rifiuti,
- motivo di ogni rifiuto,
- numero di modifiche specifiche della piattaforma,
- numero di regressioni rilevate soltanto fuori da Chrome,
- tempo necessario per preparare un aggiornamento,
- dimensione del codice condiviso e di quello specifico.
Oggi non ricostruiamo questi valori a memoria, perché vogliamo separare esperienza e statistica. In futuro consentiranno rapporti ancora più precisi.
Architettura consigliata per un'estensione multipiattaforma
src/
core/
domain/
storage/
validation/
platform/
api.ts
capabilities.ts
messaging.ts
background/
content/
popup/
options/
locales/
manifests/
chrome.json
edge.json
firefox.json
safari.json
store/
chrome/
edge/
firefox/
safari/
tests/
unit/
integration/
e2e/
manual/
L'aspetto più importante è la divisione delle responsabilità:
corenon conosce uno specifico browser;platformtraduce WebExtensions in un'interfaccia comune;- le superfici UI vengono condivise quando è ragionevole;
- manifest e asset dello store sono esplicitamente specifici della piattaforma;
- i test manuali hanno scenari scritti e non esistono soltanto nella memoria dell'autore.
Verdetto
La lezione più importante ottenuta da nove estensioni è semplice:
WebExtensions consente di mantenere un prodotto comune, ma non elimina la necessità di creare quattro release corrette.
Chrome offre il miglior punto di partenza. Edge è in genere il porting tecnico più semplice. Firefox è il test più prezioso della compatibilità reale. Safari richiede di trattare l'estensione come parte di un'app Apple, non come un'altra variante ZIP.
I problemi più grandi non sono comparsi dove mancava l'equivalente di un singolo metodo API. Sono comparsi all'intersezione di:
- ciclo di vita del processo,
- stato,
- sito web,
- interfaccia,
- permessi,
- packaging,
- revisione dello store.
Per questo un team efficace non parte dalla domanda: “Come copiamo l'estensione Chrome in altri tre store?”. Parte da:
- qual è il nucleo condiviso del prodotto?
- quali capacità dipendono dalla piattaforma?
- dove viene conservato lo stato?
- come verificheremo l'effetto reale di una funzione?
- come limiteremo i permessi?
- come riprodurremo ogni build?
- come testeremo un aggiornamento e non solo un'installazione pulita?
Dopo queste esperienze, consideriamo ancora una singola codebase la strategia corretta. Semplicemente non fingiamo che quattro browser siano un unico ambiente.
Accettare le differenze è proprio ciò che consente di condividere il codice senza ridurre la qualità.
Da leggere dopo
Fonti
- POLPROG, homepage e informazioni sul portfolio delle estensioni
- POLPROG, profilo del publisher su Firefox Add-ons
- Apple Developer, Safari Extensions
- Apple Developer, Meet Safari Web Extensions
- Chrome for Developers, Migrate to a service worker
- Chrome for Developers, Extension service worker lifecycle
- MDN, Chrome incompatibilities
- MDN, Differences between API implementations
- MDN,
browser_specific_settings - Chrome for Developers, Events in extension service workers
- Chrome for Developers, Testing MV3 service worker suspension
- Chrome for Developers, Chrome Web Store privacy fields and permissions
- Microsoft Learn, Port a Chrome extension to Microsoft Edge
- Microsoft Learn, Publish a Microsoft Edge extension
- MDN, Build a cross-browser extension
- Mozilla Extension Workshop, Submitting an add-on
- Mozilla Extension Workshop, Signing and distribution overview
- Mozilla Extension Workshop, Add-on Policies
- Apple Developer, App Review Guidelines, Safari extensions
- Mozilla, Approach to Manifest V3
- Pantelaios e Kapravelos, Manifest V3 Unveiled
- Polčák et al., Developers Insight on Manifest V3

