На папері сучасне розширення браузера майже автоматично повинно бути кросплатформним продуктом. Chrome, Firefox, Edge і Safari підтримують WebExtensions, використовують подібний файл manifest.json і надають схожі механізми: content scripts, сховище, комунікацію між контекстами, кнопку на панелі інструментів та сторінки налаштувань.
Це правда, але лише до певної межі.
Після випуску й портовання дев'яти розширень POLPROG ми знаємо, що спільний код не означає один і той самий продукт. Більшість бізнес-логіки можна перевикористовувати, однак поведінка браузера, життєвий цикл фонового процесу, інтерфейс, дозволи, пакування й перевірка магазину залишаються окремими проблемами.
До портфоліо входять:
- AutoScroll,
- ClickClean,
- DevScope,
- LeakLens,
- LoopIt,
- Loudly,
- OneMoreJump,
- TabZoo,
- TubePilot.
Це не дев'ять копій одного типу доповнення. У наборі є інструменти для відео, підсилення звуку, приватності, очищення даних, розробки, керування вкладками та легка гра. Тому проблеми не обмежувалися одним API. Вони стосувалися service worker-ів, popup-вікон, content scripts, аудіо, сховища, іконок, дозволів і процесу публікації.
POLPROG публічно підтримує дев'ять продуктів-розширень, спочатку випущених переважно для Chrome і Firefox, а згодом портованих також на Edge і Safari. [1][2]
Ця стаття не є лабораторним benchmark. Ми не вимірювали від першого commit-а кожну хвилину, витрачену на конкретний магазин, тому не будемо стверджувати, що Safari коштує рівно у 2,7 раза дорожче за Chrome або що 94% коду є спільними. Це була б удавана точність.
Натомість ми можемо показати дещо корисніше: які припущення виявилися хибними, де виникали реальні збої та як сьогодні ми спроєктували б увесь процес з нуля.
Джерела востаннє перевірено: 17 липня 2026 року.
Чотири браузери, чотири різні ролі
| Платформа | Роль у нашому процесі | Найбільша перевага | Найпоширеніша пастка |
|---|---|---|---|
| Chrome | Основна база сумісності з Manifest V3 | Найширша документація та найбільший ринок розширень | Призупинення service worker-а та суворі вимоги до дозволів |
| Edge | Порт на базі Chromium | Невелика різниця в коді порівняно з Chrome | Окремі Partner Center, метадані й сертифікація |
| Firefox | Незалежна реалізація WebExtensions | Добре виявляє припущення, залежні від Chromium | Відмінності API та manifest, підпис і вимоги до джерел |
| Safari | Окремий продукт в екосистемі Apple | Доступ до користувачів macOS, iOS та iPadOS | Xcode, застосунок-контейнер, підпис та інша поведінка runtime |
MDN наголошує, що мета WebExtensions - дозволити доповненням працювати в основних браузерах після мінімальних змін. Водночас документація описує важливі відмінності між Chrome, Firefox і Safari у доступності API, manifest-файлах та обробці асинхронних операцій. [7][8]
Це добре відповідає нашому досвіду. «Мінімальні зміни» реалістичні для простого доповнення. Що більше розширення взаємодіє зі сторінкою, мультимедіа, життєвим циклом вкладок або фоновим процесом, то менш корисним стає слово «мінімальні».
Урок 1: одна codebase - так, один manifest - не обов'язково
Найкраща вихідна точка - спільна логіка з окремими конфігураціями build.
На практиці найзручнішою виявилася структура, у якій спільними є:
- логіка функцій,
- моделі даних,
- модулі storage,
- валідація,
- більша частина інтерфейсу popup і налаштувань,
- локалізації,
- unit tests.
Окремими залишаються:
- варіанти manifest,
- ідентифікатори доповнень,
- мінімальні версії браузерів,
- конфігурація background,
- частина дозволів,
- іконки та store assets,
- пакування,
- підпис,
- скрипти публікації.
Це не означає підтримку чотирьох вручну скопійованих каталогів. Такий підхід швидко призводить до розбіжностей. Краща модель - спільні джерела плюс генератор або конфігураційний шар, який готує окремий artefact для кожної платформи.
Firefox, наприклад, потребує власних налаштувань у browser_specific_settings. У Manifest V3 ідентифікатор розширення потрібний для підпису, а з листопада 2025 року нові доповнення, що надсилаються до AMO, також мають декларувати налаштування, пов'язані зі збором даних. [9]
Safari, своєю чергою, потребує проєкту Xcode та застосунку, який містить розширення. Apple надає конвертер WebExtension, але результат усе одно потрібно розглядати як нативний проєкт, призначений для підпису й розповсюдження через App Store. [3][4]
Отже, спільний manifest був би лише уявною економією. Кілька невеликих і явних відмінностей легше підтримувати, ніж один файл, заповнений винятками та умовами.
Урок 2: Manifest V3 не уніфікував поведінку браузерів
Manifest V3 покращив частину сумісності й упорядкував модель розширень, але не створив один ідентичний runtime.
Найпомітніша зміна в Chrome - заміна постійних background pages на extension service worker. Google описує його як центральний обробник подій, який запускається за потреби. Процес може бути завершений, а змінні, що зберігаються лише в пам'яті, зникають разом із ним. [5][6]
Це має прямі наслідки.
Код на кшталт:
let activeSessions = new Map();
let currentTab = null;
let isEnabled = false;
не може бути єдиним джерелом правди, якщо ці дані потрібні після повторного запуску service worker-а.
Стан варто поділити на три групи:
- тимчасовий стан, який може безпечно зникнути;
- стан, який можна відновити з активних вкладок або DOM;
- постійний стан, який потрібно записати в
storage.local,storage.sessionабо інше відповідне місце.
Не менш важлива реєстрація listener-ів. Chrome рекомендує оголошувати обробники подій у глобальному scope скрипту, щоб вони реєструвалися синхронно, коли service worker прокидається. Listener, створений лише після асинхронної ініціалізації, може пропустити саме ту подію, яка пробудила worker. [10]
Під час розробки це легко не помітити. Відкриті DevTools можуть впливати на життєвий цикл service worker-а, а помилка з'явиться лише під час звичайного використання. Google описував схожі проблеми під час міграцій на MV3: призупинення процесу може зупинити таймери та видалити стан, що зберігався в пам'яті. [11]
Головне правило:
Фоновий процес розширення слід проєктувати як перериваний обробник подій, а не як невеликий сервер Node.js, що працює безперервно.
Урок 3: Chrome - хороша вихідна точка, але погане єдине тестове середовище
Chrome є природною базою для проєкту Manifest V3. Він має розгалужену документацію, приклади, інструменти розробника та найбільшу екосистему доповнень.
Водночас тестування лише в Chrome створює хибне відчуття сумісності.
Якщо код працює в Chrome і Edge, це насамперед підтверджує сумісність із Chromium. Це не підтверджує:
- правильну поведінку в Gecko,
- правильну поведінку у WebKit,
- стійкість до іншого життєвого циклу popup,
- роботу з іншою моделлю дозволів,
- правильне масштабування іконок,
- правильну поведінку аудіо,
- однакову семантику API.
Chrome Web Store вимагає, щоб розширення мало одну вузьку й зрозумілу мету. Також потрібний мінімальний набір дозволів, обґрунтування кожного з них і узгоджені декларації щодо роботи з даними. Надто широкі дозволи можуть призвести до відхилення. Manifest V3 також забороняє завантажувати та виконувати віддалено розміщений код. [12]
Ці вимоги впливають на архітектуру раніше, ніж може здаватися. Додавати доступ до всіх сайтів «про запас» - не нейтральне рішення. Пізніше його доведеться пояснювати reviewer-у та користувачеві. Кращим рішенням можуть бути activeTab, optional host permissions або доступ лише до конкретних доменів.
Отже, Chrome навчив нас двох протилежних речей:
- це хороше місце для визначення базового варіанта MV3;
- він не повинен визначати всі припущення спільного шару.
Урок 4: Edge - найпростіший технічний port, але не безкоштовний канал розповсюдження
Microsoft офіційно описує портовання наявного розширення Chrome до Edge як окремий підтримуваний сценарій. Оскільки обидва браузери використовують Chromium, відмінності в основному коді часто невеликі. [13]
У наших проєктах Edge зазвичай потребував найменше технічних змін. Однак це все одно не означало публікацію «в один клік».
Розширення надсилається до окремого Partner Center, де потрібно:
- завантажити власний ZIP-пакет,
- налаштувати доступність і ринки,
- вибрати категорію,
- описати єдину мету,
- обґрунтувати кожен дозвіл,
- задекларувати використання віддаленого коду,
- описати практики роботи з даними,
- підготувати listing-и для різних мов,
- завантажити окремі зображення й screenshot-и,
- додати примітки для команди сертифікації. [14]
Як і Google, Microsoft вимагає чітко визначеної мети й найменшого необхідного набору дозволів. Partner Center також попереджає, що неповні або неузгоджені декларації можуть подовжити сертифікацію або призвести до відхилення. [14]
Edge добре демонструє різницю між вартістю port-а та вартістю каналу.
Код може бути майже ідентичним, але все одно потрібно підтримувати:
- окремий запис продукту,
- окремий статус перевірки,
- окремі метадані,
- окремі графічні варіанти,
- окремий процес оновлення,
- узгоджені декларації приватності.
Для одного розширення це помірне навантаження. Для дев'яти продуктів воно перетворюється на регулярний операційний процес.
Урок 5: Firefox найкраще виявляє код, написаний «випадково лише для Chrome»
Firefox використовує WebExtensions, але не є Chromium. Саме тому він настільки цінний як тестове середовище.
Найвідоміша відмінність стосується namespace та асинхронного коду. Історично Chrome використовував chrome і callback-и, тоді як Firefox і Safari - browser і promises. У Manifest V3 Chrome і Edge додали підтримку promises для багатьох методів, але не всі відмінності зникли. Mozilla радить писати код із browser та promises і за потреби використовувати WebExtension browser API Polyfill. [8][15]
Не варто розкидати по всьому проєкту умови:
if (isFirefox) {
// ...
} else {
// ...
}
біля кожного виклику API. Краще створити невеликий адаптаційний шар:
export const extensionApi = {
storageGet: (keys) => browser.storage.local.get(keys),
storageSet: (value) => browser.storage.local.set(value),
sendMessage: (message) => browser.runtime.sendMessage(message),
};
і вирішувати відмінності в одному місці.
Firefox також вимагає більшої дисципліни під час публікації. Розширення мають бути підписані навіть при самостійному розповсюдженні у стандартній конфігурації. AMO перевіряє пакет, а мінімізований або складний для читання код може вимагати надсилання повного пакета джерел. Mozilla також може виконати ручну перевірку після публікації та відхилити поточну або попередню версію. [16][17][18]
З погляду якості це перевага. Процес змушує build бути:
- відтворюваним,
- задокументованим,
- без непотрібної обфускації,
- узгодженим із заявленою поведінкою,
- придатним до перевірки без внутрішніх знань автора.
Firefox також виявляв проблеми в місцях, де Chromium був поблажливішим. Вони стосувалися обробки повідомлень, очікування promise-відповіді, відмінностей popup та припущень про наявність окремих властивостей API.
Головний урок:
Firefox - це не додатковий checkbox після завершення проєкту. Це другий незалежний runtime, який потрібно залучати до тестування рано.
Урок 6: Safari - не port до ще одного магазину, а окремий етап продукту
Apple підтримує WebExtensions і надає інструмент для конвертації розширень із Chrome, Firefox або Edge. Попри це, процес Safari принципово відрізняється. [3][4]
Розширення стає частиною застосунку, створеного в Xcode. Потрібно керувати:
- проєктом застосунку,
- target-ом розширення,
- bundle identifier застосунку,
- окремим bundle identifier розширення,
- підписом,
- профілями й сертифікатами,
- іконкою застосунку,
- entitlements,
- архівуванням,
- App Store Connect,
- перевіркою App Store.
Apple вимагає, щоб розширення працювало з актуальною версією Safari для відповідної системи, не втручалося в інтерфейс Safari й не просило доступу до більшої кількості сайтів, ніж потрібно. [19]
Саме тут виникли найконкретніші проблеми нашого портфоліо.
Loudly: інтерфейс працював, але звук не підсилювався
Loudly змінює аудіо, що відтворюється на сторінці. У версії Safari можна було побачити інтерфейс, змінювати налаштування й отримати враження, що продукт працює. Однак реальний аудіосигнал не підсилювався.
Це гірший клас помилки, ніж очевидний crash. Smoke test «popup відкривається» проходить, але головна обіцянка продукту не виконується.
Висновок: для мультимедійних розширень функціональний тест має вимірювати кінцевий результат, а не лише стан інтерфейсу.
TabZoo: білий екран замість застосунку
Версія Safari для TabZoo могла показувати порожню білу сторінку. Така проблема може бути спричинена помилкою ініціалізації, шляхами до assets, неправильним контекстом або exception до render-а.
У Chromium подібна помилка може залишитися непомітною, якщо порядок запуску чи доступність API відрізняються.
Висновок: кожне представлення розширення повинно мати мінімальний діагностичний механізм та обробку помилок ініціалізації. Порожня сторінка не може бути фінальним станом продукту.
LoopIt: один клік відкривав два інтерфейси
У LoopIt натискання іконки в Safari викликало появу popup і додаткової панелі розширення. Це був конфлікт між декларативно налаштованим popup і ручною обробкою кліку.
В одному браузері поведінка виглядала правильно, в іншому виникала подвійна реакція.
Висновок: потрібно чітко визначити, хто володіє кліком кнопки. Або кнопка має default_popup, або подія обробляється програмно. Поєднання обох механізмів потребує дуже свідомого дизайну.
Ці три випадки показують, чому запуск конвертера Apple не завершує port. Конвертер переносить структуру. Він не підтверджує семантику поведінки.
Урок 7: відео- та аудіорозширення складніші, ніж здається з popup
Чотири наші продукти сильно залежать від мультимедіа:
- AutoScroll,
- LoopIt,
- Loudly,
- TubePilot.
Кожен із них має координувати кілька світів:
- DOM сторінки,
- audio або video елемент,
- content script,
- popup,
- фоновий процес,
- збережені налаштування,
- SPA-навігацію,
- зміну активної вкладки.
Більшість помилок виникає не всередині однієї функції, а на межі цих контекстів.
Приклад сценарію:
- користувач відкриває popup;
- popup запитує в content script поточний стан;
- сторінка щойно змінила відео без повного reload;
- старий video елемент було видалено;
- content script все ще тримає посилання на попередній елемент;
- popup показує правильне значення, але змінює неактивний player.
Додати ще один setTimeout - не рішення. Потрібна явна модель стану:
- ідентифікувати активний елемент,
- спостерігати за змінами DOM,
- повторно підключати handler-и,
- підтверджувати виконання команди,
- синхронізувати UI лише після відповіді,
- відновлювати стан після перезапуску фонового процесу.
Для сервісів на кшталт YouTube також потрібно враховувати SPA-навігацію. Зміна відео не завжди викликає повне перезавантаження документа, тому одноразової ініціалізації content script недостатньо.
Урок 8: іконка та popup є частиною реалізації, а не лише branding
Кросплатформний build може бути технічно правильним і водночас виглядати погано.
У наших тестах траплялися іконки:
- занадто великі для доступної області,
- без безпечного відступу,
- притиснуті до країв,
- візуально менші за інші іконки розширень попри однаковий розмір файлу,
- правильні в магазині, але нечіткі на панелі браузера.
Розмір PNG не вирішує проблему. Важливий візуальний розмір символу всередині canvas.
Те саме стосується popup. Однакова CSS-ширина може відображатися по-різному через:
- render шрифтів,
- стандартні стилі елементів форм,
- поведінку scrollbar,
- обчислення висоти,
- системні відступи,
- масштабування екрана,
- світлий і темний режими.
Тому сьогодні ми готуємо assets як набір, а не як одне зображення:
- вихідний SVG,
- raster-варіанти, потрібні manifest,
- контрольовану safe area,
- іконку застосунку Safari,
- графіку listing,
- screenshot-и магазину.
Інтерфейс розширення також повинен мати мінімальні та максимальні розміри й залишатися читабельним при зміні довжини перекладів.
Урок 9: публікація - це окремий операційний продукт
Найбільша організаційна помилка - вважати, що робота закінчується після створення ZIP.
Кожен магазин має власну модель довіри.
Chrome Web Store
Chrome вимагає чіткої єдиної мети, мінімальних дозволів, декларацій щодо даних і політики приватності. У Manifest V3 віддалено виконуваний код заборонений. [12]
Опис продукту має відповідати реальній поведінці. Якщо listing обіцяє функцію, яку reviewer не може запустити, це не маркетингова, а сертифікаційна проблема.
Firefox Add-ons
AMO автоматично перевіряє пакет, може вимагати джерела для зібраного коду й зберігає можливість пізнішої ручної перевірки. Попередження безпеки й приватності варто виправляти навіть тоді, коли технічно можна продовжити. [16][17]
Microsoft Edge Add-ons
Partner Center вимагає окремих даних для доступності, приватності та мовних listing-ів. Кожен дозвіл має бути обґрунтований, а інформація повинна відповідати manifest і поведінці розширення. [14]
Safari та App Store
Safari додає шар застосунку, підпис, метадані App Store й правила для застосунків Apple. Розширення повинно працювати на актуальній версії Safari та обмежувати доступ до сайтів необхідним мінімумом. [19]
Отже, дев'ять розширень не означають дев'ять listing-ів. За повного покриття чотирьох платформ може виникнути до 36 комбінацій продукт-браузер, без урахування мовних версій і окремих варіантів для пристроїв Apple.
Це достатня причина зберігати метадані в repository.
Урок 10: потрібна матриця тестів, а не нотатка «перевірити розширення»
Для кожного розширення ми підтримуємо однакове тестове ядро, а потім додаємо випадки, пов'язані з конкретною функцією.
Мінімальний smoke test
Після кожного build ми перевіряємо:
- чисту інсталяцію;
- оновлення з попередньої версії;
- появу іконки;
- відкриття popup;
- відсутність помилок ініціалізації;
- збереження та повторне читання налаштувань;
- роботу після закриття й перезапуску браузера;
- роботу після призупинення background;
- поведінку без дозволу на сторінку;
- поведінку на непідтримуваній сторінці.
Тест комунікації
Окремо тестуємо:
- popup → background,
- popup → content script,
- content script → background,
- асинхронну відповідь,
- відсутність активної вкладки,
- reload вкладки,
- SPA-навігацію,
- повторне інжектування content script,
- закриття popup до отримання відповіді.
Тест конкретної функції
Для Loudly недостатньо пересунути slider. Потрібно підтвердити зміну рівня аудіо.
Для LoopIt потрібно перевірити, що відтворення справді повертається до вибраного діапазону.
Для AutoScroll потрібно тестувати різні layout-и, fullscreen, зміну відео та ручне прокручування.
Для ClickClean треба перевірити ефект на реальних тестових даних, а не лише повідомлення «готово».
Для DevScope і LeakLens потрібно перевірити звіт на сторінках із різними CSP-правилами, кількістю frame-ів і наборами заголовків.
Платформний тест
Наприкінці додаються випадки, специфічні для браузера:
- перезапуск service worker-а в Chromium,
- підписаний пакет Firefox,
- host permissions у Firefox,
- встановлення й активація розширення в Safari,
- версії macOS та iOS, якщо продукт їх підтримує,
- оновлення через магазин, а не лише локальний sideload.
Урок 11: автоматичні тести не замінюють реальний browser QA
Автоматизація необхідна, але має обмеження.
Unit tests добре покривають:
- бізнес-логіку,
- валідацію,
- міграції storage,
- форматування даних,
- правила ввімкнення функцій.
Інтеграційні тести можуть перевіряти:
- manifest,
- наявність файлів,
- комунікацію між модулями,
- базові виклики API,
- повноту локалізацій.
E2E-тести в Chromium знаходять значну частину регресій інтерфейсу й content scripts.
Вони не підтверджують, що:
- Firefox однаково інтерпретує всі дозволи,
- Safari правильно змінює аудіо,
- App Store запускає правильний target,
- popup не відкривається двічі,
- іконка правильно виглядає на системній панелі.
Найдорожчі помилки наших port-ів було складно виявити unit test-ом. Це були інтеграційні проблеми між розширенням, браузером, сайтом та операційною системою.
Урок 12: шар сумісності має описувати можливості, а не назви браузерів
На початку проєкту спокусливо написати:
if (browserName === "safari") {
// спеціальна поведінка
}
Іноді це неминуче, але не повинно бути основною архітектурною моделлю.
Кращі запитання:
- чи підтримує середовище це API?
- чи popup декларативний?
- чи background є service worker?
- чи доступний конкретний метод?
- чи потребує функція нативного контейнера?
- чи надає сторінка потрібний медіаелемент?
Код, заснований на можливостях, стійкіший до змін версій і простіший для тестування.
Платформний шар може, наприклад, надавати:
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",
};
Це не усуває відмінності. Воно робить їх явними й концентрує в одному місці.
Урок 13: privacy-first справді спрощує публікацію
Розширення POLPROG проєктуються без аналітики, tracking і зовнішнього backend там, де вони не потрібні.
Це не лише branding-рішення. Воно спрощує:
- опис потоків даних,
- політику приватності,
- обґрунтування дозволів,
- code review,
- ризик витоку,
- відповідність між listing і кодом.
Це не означає, що privacy-first розширення не потребує документації. Якщо доповнення читає вміст сторінки, історію, вкладки або cookies, потрібно чітко пояснити навіщо, навіть якщо нічого не надсилається на сервер.
Chrome і Edge вимагають обґрунтування дозволів та декларацій щодо даних. Firefox вимагає від нових доповнень інформацію про дозволи, пов'язані зі збором даних. Safari рекомендує обмежувати доступ до сайтів необхідним мінімумом. [9][12][14][19]
Проєктувати з мінімальними дозволами від першого дня набагато легше, ніж видаляти надмірні права після відхилення.
Урок 14: release pipeline має створювати продукти, а не архів final-final.zip
За дев'яти розширень ручне пакування швидко перестає бути безпечним.
Хороший pipeline повинен:
- перевірити версію;
- провалідувати базовий manifest;
- згенерувати платформний manifest;
- зібрати код;
- скопіювати правильні іконки;
- перевірити відсутність віддаленого коду;
- запустити тести;
- створити окремі artefact-и;
- створити контрольні суми;
- підготувати changelog та інформацію для магазину.
Приклад результату:
dist/
loudly/
chrome/loudly-1.4.0.zip
edge/loudly-1.4.0.zip
firefox/loudly-1.4.0.xpi
safari/Loudly.xcarchive
Версія має походити з одного джерела правди, але artefact-и не зобов'язані бути ідентичними.
Також варто автоматично перевіряти:
- заборонені файли,
- sourcemap-и,
- secrets,
- невикористані дозволи,
- відсутні локалізації,
- неправильний шлях до іконки,
- невідповідність версій між manifest-файлами.
Автоматизація не прибирає перевірку магазину. Вона прибирає помилки, які ніколи не повинні до нього потрапити.
Який браузер виявився найпростішим?
Відповідь залежить від того, що саме вимірювати.
Найпростіший старт: Chrome
Найбільше документації, прикладів та інструментів. Хороший вибір для базової версії MV3.
Найпростіший port коду: Edge
Якщо версія Chrome правильна, технічні відмінності часто невеликі. Окрема робота з публікацією все одно залишається.
Найкращий тест незалежності: Firefox
Не завжди найпростіший, але дуже цінний. Він виявляє код, випадково прив'язаний до Chromium, і змушує підтримувати дисципліну build.
Найбільші загальні витрати: Safari
Не лише через API. Додаються Xcode, застосунок, підпис, App Store і тестування на пристроях Apple. Проблеми Loudly, TabZoo та LoopIt також показали, що працездатний інтерфейс не означає працездатну функцію.
Що ми зробили б інакше, починаючи сьогодні?
1. Safari входив би до плану з першого sprint
Необов'язково одразу з усім набором функцій. Але мав би існувати робочий skeleton проєкту Xcode й регулярний тест основного flow.
2. Firefox раніше з'явився б у CI
Не лише build, а й тести, запущені в реальному Firefox.
3. Кожна функція мала б власника стану
Перед реалізацією ми визначали б, чи належить стан content script, background, popup або storage. Багато комунікаційних помилок виникає через відсутність чіткої відповіді.
4. Дозволи проєктувалися б разом із функцією
Кожен новий дозвіл повинен мати:
- технічне обґрунтування,
- текст для магазину,
- сценарій відмови,
- тест поведінки без згоди.
5. Listing був би частиною repository
Опис, коротке резюме, release notes, пошукові терміни, політика приватності й графіка повинні мати історію змін.
6. Ми вимірювали б дані від першого випуску
Наступна версія звіту повинна містити:
- час від надсилання до публікації,
- кількість відхилень,
- причину кожного відхилення,
- кількість платформних змін,
- кількість регресій, виявлених лише поза Chrome,
- час підготовки оновлення,
- обсяг спільного та платформного коду.
Сьогодні ми не відтворюємо ці цифри з пам'яті, бо хочемо відокремити досвід від статистики. У майбутньому вони дозволять створювати ще точніші звіти.
Рекомендована архітектура кросплатформного розширення
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/
Найважливішим є розподіл відповідальності:
coreне знає конкретного браузера;platformперетворює WebExtensions на спільний інтерфейс;- UI-поверхні спільні там, де це доцільно;
- manifest-и й store assets явно платформні;
- ручні тести мають записані сценарії й не існують лише в пам'яті автора.
Вердикт
Найважливіший урок із дев'яти розширень простий:
WebExtensions дозволяє підтримувати один спільний продукт, але не скасовує потреби створити чотири правильні випуски.
Chrome дає найкращу вихідну точку. Edge зазвичай є найпростішим технічним port. Firefox - найцінніший тест справжньої сумісності. Safari вимагає ставитися до розширення як до частини застосунку Apple, а не як до ще одного ZIP-варіанта.
Найбільші проблеми виникали не там, де бракувало еквівалента окремого методу API. Вони з'являлися на перетині:
- життєвого циклу процесу,
- стану,
- вебсторінки,
- інтерфейсу,
- дозволів,
- пакування,
- перевірки магазину.
Тому ефективна команда не починає з питання: «Як скопіювати розширення Chrome до ще трьох магазинів?». Вона починає з питань:
- що є спільним ядром продукту?
- які можливості залежать від платформи?
- де зберігається стан?
- як перевірити реальний ефект функції?
- як обмежити дозволи?
- як відтворити кожен build?
- як протестувати оновлення, а не лише чисте встановлення?
Після цього досвіду ми все ще вважаємо одну codebase правильною стратегією. Ми лише не вдаємо, що чотири браузери - це одне середовище.
Саме прийняття відмінностей дозволяє ділитися кодом без зниження якості.
Читайте далі
Джерела
- POLPROG, головна сторінка та інформація про портфоліо розширень
- POLPROG, профіль видавця у 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 і Kapravelos, Manifest V3 Unveiled
- Polčák та ін., Developers Insight on Manifest V3

