Chrome vs Firefox vs Edge vs Safari: звіт про 9 розширень Skip to content

Навчання

Практичні знання про frontend, інструменти AI та розробку програмного забезпечення.

Chrome vs Firefox vs Edge vs Safari: чого ми навчилися, випустивши 9 розширень для браузерів

Опубліковано: 17 хв читання POLPROG Browser Extensions

Дев'ять розширень, чотири браузери й значно більше, ніж один файл `manifest.json`. Ми показуємо, де насправді закінчується спільний код, чому Safari потребує окремого процесу та які помилки виявляють лише Firefox і магазини розширень.

На папері сучасне розширення браузера майже автоматично повинно бути кросплатформним продуктом. 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 та iPadOSXcode, застосунок-контейнер, підпис та інша поведінка 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-а.

Стан варто поділити на три групи:

  1. тимчасовий стан, який може безпечно зникнути;
  2. стан, який можна відновити з активних вкладок або DOM;
  3. постійний стан, який потрібно записати в 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-навігацію,
  • зміну активної вкладки.

Більшість помилок виникає не всередині однієї функції, а на межі цих контекстів.

Приклад сценарію:

  1. користувач відкриває popup;
  2. popup запитує в content script поточний стан;
  3. сторінка щойно змінила відео без повного reload;
  4. старий video елемент було видалено;
  5. content script все ще тримає посилання на попередній елемент;
  6. 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 ми перевіряємо:

  1. чисту інсталяцію;
  2. оновлення з попередньої версії;
  3. появу іконки;
  4. відкриття popup;
  5. відсутність помилок ініціалізації;
  6. збереження та повторне читання налаштувань;
  7. роботу після закриття й перезапуску браузера;
  8. роботу після призупинення background;
  9. поведінку без дозволу на сторінку;
  10. поведінку на непідтримуваній сторінці.

Тест комунікації

Окремо тестуємо:

  • 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 повинен:

  1. перевірити версію;
  2. провалідувати базовий manifest;
  3. згенерувати платформний manifest;
  4. зібрати код;
  5. скопіювати правильні іконки;
  6. перевірити відсутність віддаленого коду;
  7. запустити тести;
  8. створити окремі artefact-и;
  9. створити контрольні суми;
  10. підготувати 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 правильною стратегією. Ми лише не вдаємо, що чотири браузери - це одне середовище.

Саме прийняття відмінностей дозволяє ділитися кодом без зниження якості.

Джерела

  1. POLPROG, головна сторінка та інформація про портфоліо розширень
  2. POLPROG, профіль видавця у Firefox Add-ons
  3. Apple Developer, Safari Extensions
  4. Apple Developer, Meet Safari Web Extensions
  5. Chrome for Developers, Migrate to a service worker
  6. Chrome for Developers, Extension service worker lifecycle
  7. MDN, Chrome incompatibilities
  8. MDN, Differences between API implementations
  9. MDN, browser_specific_settings
  10. Chrome for Developers, Events in extension service workers
  11. Chrome for Developers, Testing MV3 service worker suspension
  12. Chrome for Developers, Chrome Web Store privacy fields and permissions
  13. Microsoft Learn, Port a Chrome extension to Microsoft Edge
  14. Microsoft Learn, Publish a Microsoft Edge extension
  15. MDN, Build a cross-browser extension
  16. Mozilla Extension Workshop, Submitting an add-on
  17. Mozilla Extension Workshop, Signing and distribution overview
  18. Mozilla Extension Workshop, Add-on Policies
  19. Apple Developer, App Review Guidelines, Safari extensions
  20. Mozilla, Approach to Manifest V3
  21. Pantelaios і Kapravelos, Manifest V3 Unveiled
  22. Polčák та ін., Developers Insight on Manifest V3

Найважливіший урок із дев'яти розширень простий: WebExtensions дозволяє підтримувати один спільний продукт, але не скасовує потреби створити чотири правильні випуски. Chrome дає найкращу вихідну точку. Edge зазвичай є найпростішим технічним port. Firefox - найцінніший тест справжньої сумісності. Safari вимагає ставитися до розширення як до частини застосунку Apple, а не як до ще одного ZIP-варіанта.

Browser Extensions Dev Tools Chrome Firefox Safari Comparison

Часті запитання

Чи може одне розширення працювати в Chrome, Firefox, Edge і Safari?

Так. WebExtensions надає значну спільну площу API. Однак усе одно потрібні платформні тести, варіанти manifest, окреме пакування та адаптація процесу публікації.

Чи працює розширення Chrome в Edge без змін?

Часто воно працює після невеликих змін, оскільки Edge використовує Chromium. Проте все одно потрібні окремий запис у Microsoft Partner Center, декларації приватності, метадані й сертифікація.

Чи підтримує Firefox Manifest V3?

Так. Firefox підтримує Manifest V3, але зберігає власні рішення й відмінності. Нові розширення MV3 потребують ID у browser_specific_settings, а нові надсилання до AMO мають декларувати дозволи, пов'язані зі збором даних.

Чи потрібно підтримувати окремий код для Firefox?

Не весь код. Найкраще ділитися ядром і ізолювати відмінності в API-адаптері та конфігурації manifest.

Чому extension service worker втрачає стан?

Chrome може завершити неактивний extension service worker. Дані, потрібні пізніше, не повинні існувати лише у змінних пам'яті процесу.

Чи Safari автоматично конвертує розширення Chrome?

Apple надає конвертер, що створює проєкт Safari Web Extension, але не гарантує ідентичної поведінки. Функції все одно потрібно тестувати, Xcode налаштовувати, застосунок підписувати та проходити процес App Store.

Який браузер найскладніший для розробників розширень?

За нашим досвідом, Safari має найвищі загальні витрати через runtime, Xcode, підпис, застосунок-контейнер і перевірку App Store.

Чи потрібна політика приватності, якщо розширення нічого не надсилає?

Часто вона все одно корисна, а вимоги залежать від магазину й дозволів. Потрібно чітко пояснити, які дані розширення читає, де зберігає та чи залишають вони пристрій.

Як тестувати мультимедійні розширення?

Недостатньо лише відкрити popup. Потрібно перевірити реальний ефект: зміну аудіо, діапазон циклу, поведінку player, SPA-навігацію, зміну вкладки та відновлення стану після перезапуску.

Чи варто публікуватися у чотирьох магазинах?

Так, якщо цільові користувачі справді користуються цими браузерами, а команда готова підтримувати окремі release-процеси. Потрібно враховувати сертифікацію, метадані, тестування та майбутні оновлення, а не лише перший port.

Чи було це корисно?

Отримуйте нові статті електронною поштою

Один короткий лист на кожну нову статтю Навчання. Без спаму, відписка в один клік.

Ми використовуємо вашу пошту лише для надсилання нових статей. Без передачі третім сторонам.

Назад до Навчання

На цій сторінці