Розробка кросбраузерного розширення (WebExtension API)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кросбраузерного розширення (WebExtension API)
Складна
~2-3 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка кроссбраузерного розширення (WebExtension API)

WebExtension API — це стандарт, який підтримують Chrome, Firefox, Edge, Opera та (частково) Safari. Єдина кодова база з умовними патчами під браузерні різниці — правильний підхід до кроссбраузерної розробки. Копіювати та підтримувати окремі версії для кожного браузера — антипаттерн.

Сумісність API за браузерами

API Chrome Firefox Edge Opera Safari
storage.local MV2/3 MV2/3 MV2/3 MV2/3 14+
storage.sync 15+
tabs 14+
scripting (MV3) 101+ 92+ 15.4+
declarativeNetRequest 113+ 92+ 15.4+
sidePanel 114+
webRequest Тільки MV2 Тільки MV2 Тільки MV2

Полід: webextension-polyfill

Mozilla розробила webextension-polyfill — вона перетворює chrome.* (Callback) на browser.* (Promise) та згладжує різниці між браузерами:

npm install webextension-polyfill
import browser from 'webextension-polyfill';

// Одинаковий код для Chrome та Firefox
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
await browser.storage.local.set({ data: 'value' });
const result = await browser.storage.local.get('data');

Єдиний маніфест: MV2 vs MV3

Підтримувати два маніфести паралельно — стандартна практика для максимального охоплення:

// scripts/build.js
const fs = require('fs');
const target = process.env.TARGET_BROWSER; // 'chrome', 'firefox', 'safari'

const manifests = {
  chrome: {
    manifest_version: 3,
    background: { service_worker: 'background.js' },
    action: { default_popup: 'popup.html' },
  },
  firefox: {
    manifest_version: 2,
    background: { scripts: ['background.js'], persistent: false },
    browser_action: { default_popup: 'popup.html' },
    browser_specific_settings: {
      gecko: { id: '[email protected]', strict_min_version: '109.0' },
    },
  },
  safari: {
    manifest_version: 3,
    background: { service_worker: 'background.js' },
    action: { default_popup: 'popup.html' },
    browser_specific_settings: {
      safari: { strict_min_version: '15.4' },
    },
  },
};

const base = JSON.parse(fs.readFileSync('./manifest.base.json', 'utf8'));
const merged = { ...base, ...manifests[target] };
fs.writeFileSync('./dist/manifest.json', JSON.stringify(merged, null, 2));

Структура проекту

my-extension/
├── src/
│   ├── background/
│   │   ├── index.ts
│   │   └── handlers/
│   ├── content/
│   │   ├── index.ts
│   │   └── modules/
│   ├── popup/
│   │   ├── App.tsx
│   │   └── index.tsx
│   ├── options/
│   │   └── App.tsx
│   └── shared/
│       ├── storage.ts      # обгортка над browser.storage
│       ├── messaging.ts    # типізовані повідомлення
│       └── browser.ts      # визначення поточного браузера
├── manifest.base.json
├── manifests/
│   ├── mv2.json
│   └── mv3.json
├── vite.config.ts
└── package.json

Типізовані повідомлення

Сувора типізація повідомлень між контекстами — запорука відсутності тихих помилок:

// shared/messages.ts
export type Message =
  | { type: 'GET_PAGE_DATA'; url: string }
  | { type: 'SET_BADGE'; count: number; color?: string }
  | { type: 'OPEN_OPTIONS' }
  | { type: 'EXTRACT_TEXT'; selector: string };

export type MessageResponse<T extends Message> =
  T extends { type: 'GET_PAGE_DATA' } ? { title: string; meta: Record<string, string> } :
  T extends { type: 'EXTRACT_TEXT' }  ? { text: string } :
  void;

// Типізований sendMessage
export async function sendMessage<T extends Message>(
  message: T
): Promise<MessageResponse<T>> {
  return browser.runtime.sendMessage(message) as Promise<MessageResponse<T>>;
}

// Типізований onMessage
export function onMessage<T extends Message['type']>(
  type: T,
  handler: (msg: Extract<Message, { type: T }>, sender: browser.runtime.MessageSender) => Promise<MessageResponse<Extract<Message, { type: T }>>>
) {
  browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
    if (msg.type === type) {
      handler(msg, sender).then(sendResponse);
      return true;
    }
  });
}

Визначення браузера та умовні шляхи

// shared/browser.ts
export type BrowserName = 'chrome' | 'firefox' | 'safari' | 'edge' | 'opera';

export function detectBrowser(): BrowserName {
  // @ts-ignore
  if (typeof browser !== 'undefined' && browser.runtime?.id) {
    const ua = navigator.userAgent;
    if (ua.includes('Firefox/')) return 'firefox';
    if (ua.includes('OPR/')) return 'opera';
    if (ua.includes('Edg/')) return 'edge';
    // Safari визначається через маніфест
    if (!ua.includes('Chrome')) return 'safari';
  }
  return 'chrome';
}

export const IS_FIREFOX = detectBrowser() === 'firefox';
export const IS_SAFARI = detectBrowser() === 'safari';
export const SUPPORTS_SIDE_PANEL = detectBrowser() === 'chrome';

Збирання для декількох браузерів із Vite

// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

const browser = process.env.TARGET_BROWSER ?? 'chrome';
const manifestFile = browser === 'firefox'
  ? './manifests/mv2.json'
  : './manifests/mv3.json';

export default defineConfig({
  define: {
    __BROWSER__: JSON.stringify(browser),
    __IS_FIREFOX__: browser === 'firefox',
  },
  plugins: [crx({ manifest: require(manifestFile) })],
  build: {
    outDir: `dist/${browser}`,
  },
});
// package.json scripts
{
  "build:chrome":  "TARGET_BROWSER=chrome  vite build",
  "build:firefox": "TARGET_BROWSER=firefox vite build",
  "build:edge":    "TARGET_BROWSER=edge    vite build",
  "build:safari":  "TARGET_BROWSER=safari  vite build",
  "build:all":     "npm run build:chrome && npm run build:firefox && npm run build:edge"
}

CI/CD: збирання та публікація всіх версій

# .github/workflows/publish.yml
name: Publish Extensions
on:
  push:
    tags: ['v*']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build:all

      - name: Publish to Chrome Web Store
        uses: trstringer/manual-approval@v1
        env:
          CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
          CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
          CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}

      - name: Sign & Publish to Firefox AMO
        run: |
          npx web-ext sign \
            --source-dir dist/firefox \
            --api-key ${{ secrets.AMO_JWT_ISSUER }} \
            --api-secret ${{ secrets.AMO_JWT_SECRET }} \
            --channel listed

Терміни

Кроссбраузерне розширення (Chrome + Firefox + Edge) з єдиною кодовою базою, типізацією, автосборкою та публікацією в три магазини — 10–16 робочих днів залежно від функціональності. Додавання Safari (Xcode-обгортка + App Store) додає 5–8 днів. Терміни ревю магазинів — окремо та паралельно.