Перейти к основному содержимому

4.14. Расширения для браузера

Разработчику Архитектору Инженеру

Расширения для браузера

Что такое расширение для браузера

Расширение для браузера — это программный модуль, предназначенный для интеграции в веб-браузер с целью расширения его возможностей. Основная задача расширения — модификация или дополнение поведения браузера без изменения его исходного кода. Расширения работают внутри среды, ограниченной правилами безопасности браузера, и имеют доступ к определённым интерфейсам, предоставляемым платформой браузера через стандартные API.

С точки зрения пользователя, расширение проявляется как дополнительный функционал: от изменения внешнего вида страницы до глубокой интеграции с сетевыми ресурсами и локальными ресурсами компьютера. Расширения могут отображать иконки в панели инструментов, добавлять контекстные пункты в меню, вмешиваться в загрузку ресурсов, перехватывать события ввода, сохранять данные в локальное хранилище и взаимодействовать с внешними сервисами через сетевые запросы.

С технической стороны, расширение представляет собой набор файлов, объединённых в архив определённой структуры — чаще всего это ZIP-архив без сжатия (расширение .crx у Chromium-браузеров, .xpi у Firefox), содержащий манифест, сценарии, таблицы стилей, HTML-файлы, изображения и другие ресурсы. Ключевым элементом любого расширения является манифест — JSON-файл, в котором описаны метаданные расширения: его имя, версия, описание, список необходимых разрешений, точки входа, типы предоставляемых компонентов (вкладки, всплывающие окна, фоновые скрипты и так далее).

Расширения разрабатываются с учётом архитектуры конкретного браузера, но современные браузеры стремятся к унификации API, что делает возможным написание кросс-платформенных расширений. Chromium-совместимые браузеры (Google Chrome, Microsoft Edge, Opera, Brave, Vivaldi и другие) используют общую модель расширений на базе Manifest V3, Firefox частично поддерживает аналогичную модель, сохраняя при этом собственную экосистему и некоторые уникальные возможности.

Расширения не требуют установки отдельного программного обеспечения на операционную систему. Они устанавливаются непосредственно через интерфейс браузера, чаще всего из официального магазина дополнений (Chrome Web Store, Mozilla Add-ons), хотя возможна и ручная установка в режиме разработчика. Управление расширениями осуществляется в специальном разделе настроек браузера, где пользователь может включать, отключать, удалять расширения или изменять их права доступа.

Расширения делятся на категории по функциональному назначению:

  • Инструменты для разработчиков — отладка, инспектирование сетевых запросов, генерация фикстур, эмуляция устройств.
  • Безопасность и приватность — блокировка рекламы и трекеров, защита от фишинга, управление паролями, двухфакторная аутентификация.
  • Производительность и автоматизация — заполнение форм, сокращение URL, управление вкладками, массовые действия с содержимым страниц.
  • Контент-ориентированные — перевод текста, чтение текста вслух, улучшение читаемости, ночной режим, фильтрация изображений.
  • Интеграции с сервисами — отправка выделенного текста в облачные заметки, публикация в социальные сети, взаимодействие с CRM и системами управления задачами.

Каждая категория использует характерный набор API браузера и применяет специфичные подходы к организации кода.

Важной особенностью расширений является их изоляция. Браузер создаёт отдельную песочницу для каждого расширения. Контентные скрипты работают в изолированном контексте на веб-страницах, фоновые скрипты — в отдельном процессе, а страницы расширения (попапы, настройки) — в своём окружении, недоступном для веб-контента. Такая изоляция повышает безопасность: уязвимости в одном расширении не приводят к компрометации других расширений или основного контента страниц.

Расширения подчиняются политике безопасности браузера, в том числе политике Content Security Policy (CSP), которая ограничивает источники, из которых можно загружать скрипты, стили и другие ресурсы. Это предотвращает внедрение вредоносного кода через сторонние домены. Разработчики расширений обязаны соблюдать CSP при построении архитектуры приложения, в том числе избегать использования eval, innerHTML с динамическими строками, инлайновых обработчиков событий и сторонних CDN без явного разрешения в манифесте.

Модель разрешений расширений основана на принципе минимальной привилегии: расширение запрашивает только те права, которые ему необходимы для выполнения заявленных функций. Пользователь видит список запрашиваемых разрешений перед установкой и может отказаться от установки, если набор разрешений кажется избыточным. Разрешения делятся на категории: доступ к вкладкам, управление историей, чтение и изменение данных на сайтах, доступ к cookie, работа с буфером обмена, взаимодействие с устройствами ввода и другие. Некоторые разрешения требуют дополнительного подтверждения от пользователя — например, доступ к вкладкам с конфиденциальными сайтами (банкинг, госуслуги) может сопровождаться отдельным диалоговым окном.

Расширения могут обновляться автоматически через магазин дополнений. При обновлении сохраняются настройки пользователя и локальные данные, но разработчик обязан соблюдать обратную совместимость манифеста и API. При переходе между мажорными версиями манифеста (например, с Manifest V2 на Manifest V3) требуется переработка архитектуры расширения — браузеры предоставляют миграционные руководства и инструменты анализа.


Как работает расширение для браузера

Расширение функционирует как многокомпонентное приложение, размещённое внутри среды браузера. Его работа строится на совместном использовании нескольких типов модулей, каждый из которых отвечает за конкретный аспект поведения. Эти модули существуют в отдельных контекстах выполнения и взаимодействуют между собой по строго определённым каналам. Архитектура расширения зависит от версии манифеста: Manifest V2 использует фоновые страницы и фоновые скрипты, Manifest V3 заменяет их на сервис-воркеры, что вносит существенные изменения в модель выполнения и сохранения состояния.

Основные компоненты расширения

Фоновый модуль — центральный управляющий элемент расширения. В Manifest V2 это фоновая страница (обычно HTML-файл с подключёнными скриптами) или просто фоновый скрипт, который загружается при старте расширения и остаётся активным, пока браузер работает. В Manifest V3 фоновый модуль реализуется как сервис-воркер — скрипт, исполняемый в отдельном процессе без DOM-окружения, не имеющий прямого доступа к глобальному объекту window, и активируемый по событиям. Сервис-воркер не работает постоянно: браузер запускает его по мере необходимости (например, при получении сетевого запроса, таймера или сообщения от контентного скрипта) и завершает после периода бездействия. Это требует от разработчика проектировать логику с учётом потенциального завершения и повторного запуска процесса.

Фоновый модуль имеет полный доступ к API расширений: управление вкладками, история, закладки, хранилище, сетевые запросы, уведомления. Он служит координатором: принимает события от браузера, инициирует действия, отправляет команды контентным скриптам и обрабатывает их ответы. Хранение долгоживущих данных (настроек, состояния сессии) осуществляется через chrome.storage (или browser.storage в Firefox), а не через localStorage, поскольку последний изолирован по доменам и недоступен фоновому модулю напрямую.

Контентные скрипты — модули, внедряемые в DOM веб-страниц, соответствующих заданным правилам в манифесте (например, matches: ["https://*.example.com/*"]). Они выполняются в контексте страницы, но в изолированной «песочнице»: у них есть собственный глобальный объект, отделённый от основного контента страницы. Это позволяет безопасно манипулировать DOM, перехватывать события ввода, изменять стили, добавлять элементы интерфейса без риска конфликта имён или утечки данных между расширением и веб-приложением.

Контентные скрипты не имеют прямого доступа к большинству API браузера (например, chrome.tabs, chrome.storage.local). Для получения данных или инициации действий они обмениваются сообщениями с фоновым модулем через chrome.runtime.sendMessage / chrome.runtime.onMessage. Аналогично, фоновый модуль может инициировать выполнение кода в контентном скрипте с помощью chrome.tabs.sendMessage. Такой подход обеспечивает чёткое разделение зон ответственности: контентный скрипт отвечает за взаимодействие с DOM, фоновый модуль — за логику приложения и системные вызовы.

Страницы расширения — HTML-интерфейсы, принадлежащие самому расширению. К ним относятся:

  • Всплывающее окно (popup) — отображается при клике на иконку расширения в панели инструментов. Имеет ограниченный размер (обычно до 800×600 пикселей) и живёт только пока открыто. При закрытии все JavaScript-объекты и DOM-состояние уничтожаются.
  • Страница настроек (options page) — автономная страница, открываемая из менеджера расширений. Предназначена для настройки поведения расширения. Сохранение параметров производится в chrome.storage.sync или chrome.storage.local.
  • Сторонние страницы (side panels, devtools panels) — специализированные интерфейсы, встраиваемые в боковую панель или панель разработчика браузера.

Все страницы расширения имеют доступ к полному набору API расширений (в отличие от контентных скриптов), могут взаимодействовать с фоновым модулем и использовать собственные стили и скрипты. Их среда изолирована от веб-контента: куки, localStorage, sessionStorage принадлежат расширению и не пересекаются с данными сайтов.

Обмен данными между компонентами

Взаимодействие внутри расширения реализуется через систему событий и сообщений. Основные механизмы:

  • Однократные сообщения (sendMessage / onMessage) — подходят для запрос-ответных сценариев. Контентный скрипт может запросить у фонового модуля текущие настройки, получить результат и применить его к DOM.
  • Долгоживущие соединения (connect / onConnect) — устанавливают постоянный канал связи, позволяющий отправлять несколько сообщений без повторного согласования. Полезны для сценариев, требующих потоковой передачи данных (например, передача прогресса обработки или потокового анализа DOM).
  • Хранилище как общее состояниеchrome.storage предоставляет синхронизированный (sync) и локальный (local) режимы хранения. Изменения в хранилище можно отслеживать через chrome.storage.onChanged, что позволяет компонентам реагировать на обновления без явной отправки сообщений.
  • Передача данных через URL-параметры — страницы расширения могут принимать параметры в URL (например, popup.html?tabId=123), что позволяет передавать идентификаторы вкладок или другие контекстные данные при открытии интерфейса.

В Manifest V3 усилена роль асинхронных вызовов. Большинство API-методов теперь возвращают промисы, а не принимают callback-функции. Это требует от разработчика использования async/await или цепочек .then() для обработки результатов.

Жизненный цикл расширения

Жизненный цикл расширения состоит из нескольких фаз:

  1. Установка — происходит при первом добавлении расширения в браузер. Срабатывает событие runtime.onInstalled, в котором можно инициализировать хранилище, создать контекстные меню, зарегистрировать прослушиватели. При установке расширение получает выделенное пространство имён для данных, изолированное от других расширений.

  2. Активация — в Manifest V2 фоновый скрипт запускается сразу после установки или перезапуска браузера. В Manifest V3 активация происходит по событиям: при первом обращении к расширению (например, клике по иконке), по таймеру (alarms), сетевому запросу (webRequest), изменению вкладки и другим триггерам, разрешённым в манифесте.

  3. Выполнение — в процессе работы расширение реагирует на события браузера: создание/обновление/закрытие вкладки, навигация, нажатие клавиш, щелчок мыши, получение уведомления. События обрабатываются фоновым модулем, который инициирует соответствующие действия — внедрение контентного скрипта, обновление иконки, отображение уведомления.

  4. Обновление — при обновлении расширения через магазин срабатывает runtime.onInstalled с причиной "update". Разработчик может выполнить миграцию данных, очистить устаревшие настройки, зарегистрировать новые API-прослушиватели.

  5. Отключение и удаление — при отключении расширения вручную фоновые процессы завершаются, контентные скрипты удаляются из страниц, иконки скрываются. При удалении срабатывает runtime.onInstalled с причиной "uninstall", после чего все данные расширения в chrome.storage.local, chrome.storage.sync, IndexedDB, кэше и файлах удаляются. Пользователь может сохранить данные вручную перед удалением, если расширение предоставляет экспорт.

Безопасность и ограничения

Безопасность расширений обеспечивается на нескольких уровнях:

  • Песочница DOM — контентные скрипты не могут напрямую обращаться к переменным и функциям основного скрипта страницы. Для доступа к данным страницы требуется явное внедрение кода через script-теги или использование window.postMessage.
  • Политика CSP — по умолчанию запрещает выполнение инлайновых скриптов, eval, динамической загрузки ресурсов с произвольных доменов. Разрешённые источники указываются в поле content_security_policy манифеста.
  • Изоляция хранилищ — данные одного расширения недоступны другим расширениям или веб-сайтам.
  • Явное согласие на разрешения — пользователь видит список запрашиваемых прав и может отказаться от установки. При изменении списка разрешений при обновлении расширение временно отключается до подтверждения новых прав.
  • Ограничения Manifest V3 — удаление webRequest блокирующего режима в пользу declarativeNetRequest, замена фоновых страниц на событийно-управляемые сервис-воркеры, запрет на удалённый хостинг кода (весь исполняемый код должен быть в пакете расширения). Эти меры снижают риск выполнения неаудированного кода и повышают прозрачность поведения расширения.

Как разработать расширение для браузера

Разработка расширения начинается с анализа задачи и выбора архитектуры. Корректная постановка цели определяет необходимый набор разрешений, типы компонентов и модель взаимодействия с пользователем и внешними ресурсами. Прежде чем приступить к коду, следует определить:

  • Какие действия должен выполнять пользователь для активации функционала (клик по иконке, выделение текста, горячая клавиша, автоматическое срабатывание на определённых сайтах).
  • Нужен ли постоянный фоновый процесс или достаточно событийной активации.
  • Требуется ли доступ к данным страницы (только чтение, или также модификация DOM).
  • Планируется ли синхронизация данных между устройствами.
  • Какие внешние ресурсы будут использоваться (сетевые API, файлы, устройства ввода).

Эти вопросы напрямую влияют на структуру проекта и содержание манифеста.

Структура проекта расширения

Минимальное расширение состоит из трёх файлов:

my-extension/
├── manifest.json # обязательный манифест
├── background.js # фоновый сервис-воркер (Manifest V3)
└── popup.html # всплывающее окно

Для более сложных проектов рекомендуется следующая структура:

my-extension/
├── manifest.json
├── icons/
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
├── background/
│ └── service-worker.js
├── popup/
│ ├── index.html
│ ├── style.css
│ └── script.js
├── content-scripts/
│ ├── inject.js
│ └── dom-helper.js
├── options/
│ ├── index.html
│ └── options.js
├── _locales/
│ ├── en/
│ │ └── messages.json
│ └── ru/
│ └── messages.json
└── lib/
└── utils.js

Каждый каталог отражает ответственность компонентов. Такая организация упрощает поддержку, тестирование и локализацию.

Манифест: основа расширения

Файл manifest.json — точка входа. В Manifest V3 он должен содержать поля:

  • manifest_version: 3 (обязательно для новых расширений в Chrome и Edge).

  • name: отображаемое имя расширения (до 45 символов).

  • version: строка в формате x.y.z, где каждая часть — неотрицательное целое число.

  • description: краткое пояснение функционала (до 132 символов).

  • icons: объект с путями к иконкам разных размеров (рекомендуются 16, 48, 128 пикселей).

  • action (ранее browser_action): конфигурация иконки на панели инструментов — default_popup, default_title, default_icon.

  • background: указание на сервис-воркер:

    "background": {
    "service_worker": "background/service-worker.js",
    "type": "module"
    }

    Поле type: "module" разрешает использование import/export в фоновом скрипте.

  • content_scripts: массив правил внедрения контентных скриптов:

    "content_scripts": [{
    "matches": ["https://*.wikipedia.org/*"],
    "js": ["content-scripts/inject.js"],
    "css": ["content-scripts/style.css"],
    "run_at": "document_idle"
    }]

    Параметр run_at определяет момент внедрения: document_start (до парсинга DOM), document_end (после построения DOM, до загрузки ресурсов), document_idle (после полной загрузки, по умолчанию).

  • permissions: массив строк с запрашиваемыми разрешениями, например:
    ["storage", "tabs", "activeTab", "scripting"].
    activeTab даёт временный доступ к текущей вкладке лишь после явного действия пользователя (клик, горячая клавиша), что соответствует принципу минимальных привилегий.

  • host_permissions: отдельный массив для прав на доступ к URL (например, ["https://api.example.com/*"]), необходимый для fetch, XMLHttpRequest и declarativeNetRequest.

  • web_accessible_resources: ресурсы, доступные из контентных скриптов или веб-страниц (например, изображения, JavaScript-библиотеки, которые внедряются через <script src=...>):

    "web_accessible_resources": [{
    "resources": ["lib/utils.js", "assets/icon.svg"],
    "matches": ["<all_urls>"]
    }]
  • declarative_net_request: правило для блокировки или перенаправления запросов без фонового скрипта (альтернатива устаревшему webRequest):

    "declarative_net_request": {
    "rule_resources": [{
    "id": "ruleset_1",
    "enabled": true,
    "path": "rules.json"
    }]
    }

    Файл rules.json содержит JSON-массив правил, совместимых со спецификацией Google.

  • options_page: путь к странице настроек.

  • commands: горячие клавиши (например, _execute_action для открытия popup по сочетанию).

  • sandbox: изолированные страницы без доступа к API расширений, но с разрешением на выполнение ненадёжного кода (например, рендер Markdown от пользователя).

Полный манифест проверяется браузером при загрузке расширения. Ошибки в синтаксисе или недопустимые значения приводят к отказу в загрузке.

Разработка компонентов

Фоновый сервис-воркер инициализирует слушатели событий. Пример минимального service-worker.js:

// Регистрация прослушивателя клика по иконке
chrome.action.onClicked.addListener((tab) => {
// Выполнение скрипта на активной вкладке
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => {
alert('Расширение активировано!');
}
});
});

// Обработка сообщений от контентных скриптов
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === 'getSettings') {
chrome.storage.local.get(['theme', 'language'], (data) => {
sendResponse({ settings: data });
});
return true; // сохранить канал для асинхронного ответа
}
});

В Manifest V3 фоновый скрипт не может использовать setTimeout, setInterval, XMLHttpRequest в глобальной области. Все операции должны быть привязаны к событиям или использоваться внутри обработчиков.

Контентный скрипт работает с DOM. Пример inject.js:

// Создание кнопки в интерфейсе страницы
const button = document.createElement('button');
button.textContent = 'Сохранить выделение';
button.style.position = 'fixed';
button.style.bottom = '20px';
button.style.right = '20px';
button.style.zIndex = '10000';
document.body.appendChild(button);

button.addEventListener('click', async () => {
const selection = window.getSelection().toString().trim();
if (selection) {
// Отправка данных в фоновый модуль
const response = await chrome.runtime.sendMessage({
type: 'saveNote',
content: selection
});
if (response.success) {
button.textContent = '✓ Сохранено';
setTimeout(() => button.textContent = 'Сохранить выделение', 1500);
}
}
});

Контентные скрипты не должны полагаться на глобальные переменные страницы. Для доступа к window-свойствам (например, window.location.href) используют прямые вызовы — они изолированы, но имеют доступ к DOM-дереву и свойствам окна.

Всплывающее окно — полноценное веб-приложение. Пример popup/index.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { width: 300px; padding: 16px; font-family: sans-serif; }
.note { margin: 8px 0; padding: 6px; background: #f0f0f0; border-radius: 4px; }
</style>
</head>
<body>
<h3>Сохранённые заметки</h3>
<div id="notes"></div>
<script src="script.js"></script>
</body>
</html>

Соответствующий script.js:

document.addEventListener('DOMContentLoaded', async () => {
const result = await chrome.storage.local.get(['notes']);
const notes = result.notes || [];
const container = document.getElementById('notes');

notes.forEach(note => {
const el = document.createElement('div');
el.className = 'note';
el.textContent = note.text.substring(0, 100);
container.appendChild(el);
});
});

Важно: скрипты всплывающего окна загружаются каждый раз при открытии. Долгоживущее состояние должно храниться в chrome.storage, а не в JavaScript-переменных.

Инструменты разработки и отладка

Для отладки расширений используется встроенная панель разработчика Chrome/Edge/Firefox:

  • chrome://extensions → включить «Режим разработчика» → «Загрузить распакованное расширение».
  • После загрузки появляется карточка расширения с кнопками:
    • Просмотреть фоновую страницу (в V2) или Инспектировать views (в V3 — для popup, options).
    • Обновить — мгновенная перезагрузка без перезапуска браузера.
  • В панели разработчика popup или options-страницы доступны все инструменты: Elements, Console, Sources, Network.
  • Для отладки контентных скриптов нужно открыть целевую веб-страницу, вызвать DevTools (F12), переключиться на вкладку SourcesPage → выбрать content-scripts/inject.js.
  • Логирование из фонового скрипта видно в консоли фонового контекста (открывается через «Инспектировать views» → background page).
  • События chrome.runtime.onInstalled, ошибки загрузки манифеста и нарушения CSP отображаются в консоли на странице chrome://extensions.

Firefox предоставляет аналогичный интерфейс по адресу about:debugging#/runtime/this.

Тестирование

Тестирование расширений включает:

  • Ручное тестирование на разных сайтах, в том числе с CSP, CORS, динамической подгрузкой контента.
  • Проверку разрешений: попытка выполнить действия без необходимых прав должна завершаться ошибкой, а не молчаливым игнорированием.
  • Тестирование жизненного цикла: установка, обновление, отключение, удаление — все этапы должны корректно обрабатываться.
  • Кросс-браузерную проверку: несмотря на унификацию API, детали реализации различаются. Например, Firefox допускает eval при явном указании unsafe-eval в CSP, Chrome — нет.

Автоматизированное тестирование возможно с помощью:

  • web-ext (инструмент Mozilla для сборки и тестирования в Firefox),
  • Puppeteer или Playwright (для end-to-end сценариев: установка расширения как временного компонента и эмуляция действий пользователя),
  • Jest или Mocha с моками chrome.* API (для unit-тестов логики).

Упаковка и публикация

Для публикации в магазине требуется:

  1. Создание учётной записи разработчика:

    • Chrome Web Store — одноразовый платёж $5.
    • Mozilla Add-ons — бесплатная регистрация.
  2. Подготовка артефактов:

    • Иконки в форматах PNG (128×128 для магазина, 48×48 для списка расширений).
    • Скриншоты интерфейса (рекомендовано 3–5 изображений, 1280×800).
    • Локализованные описания и названия (через _locales).
    • Политика конфиденциальности — обязательна при доступе к данным сайтов или сетевым запросам.
  3. Сборка пакета:

    • В Chrome: на странице chrome://extensions → «Упаковать расширение» → выбор папки и, при необходимости, приватного ключа (.pem).
    • Полученный .crx-файл не используется для загрузки в магазин — вместо него отправляется ZIP-архив с исходными файлами.
  4. Проверка перед отправкой:

    • Использование web-ext lint (для Firefox) или chrome-extension-linter для выявления ошибок манифеста и нарушений политик.
    • Проверка на соответствие требованиям магазина (отсутствие минифицированного кода без исходников, явное указание источника всех библиотек).
  5. Модерация:

    • В Chrome: от 1 до 7 дней. Расширения с доступом к webRequest, tabs, history проходят ручную проверку.
    • В Firefox: автоматическая проверка + ручной аудит при наличии чувствительных разрешений.

После одобрения расширение становится доступно для установки. Обновления проходят ту же процедуру, но обычно быстрее.

Этические и технические ограничения

Современные магазины расширений запрещают:

  • Сбор данных без явного согласия и описания в политике конфиденциальности.
  • Тихое изменение поведения браузера (например, замена поисковой системы по умолчанию без подтверждения).
  • Внедрение рекламы в контент сайтов.
  • Использование удалённо загружаемого исполняемого кода (все JS-файлы должны быть в пакете).
  • Обфускацию кода, затрудняющую аудит.

Нарушение этих правил ведёт к блокировке расширения и приостановке аккаунта разработчика.


Эволюция моделей манифеста: Manifest V2 и Manifest V3

Manifest V3 — это не просто обновление формата, а фундаментальная перестройка архитектуры расширений, направленная на повышение безопасности, производительности и прозрачности. Переход от V2 к V3 затрагивает ключевые компоненты: фоновую логику, сетевые запросы, хранение кода и управление разрешениями.

Замена фоновых страниц на сервис-воркеры

В Manifest V2 фоновый скрипт мог работать постоянно, поддерживая долгоживущие соединения, таймеры и глобальное состояние. Это давало гибкость, но создавало риски: утечки памяти, скрытые майнеры, фоновые сборщики данных без ведома пользователя.

Manifest V3 вводит событийно-управляемую модель: сервис-воркер запускается только при наступлении события (например, щелчок по иконке, сетевой запрос, изменение вкладки), выполняет необходимые действия и завершается. Долгоживущие операции должны быть перестроены с использованием:

  • chrome.alarms вместо setInterval для периодических задач,
  • chrome.storage вместо глобальных переменных для сохранения состояния между запусками,
  • chrome.action.setBadgeText / setBadgeBackgroundColor для отображения статуса без постоянного процесса.

Эта модель снижает потребление ресурсов и делает поведение расширения предсказуемым.

Декларативная обработка сетевых запросов

В Manifest V2 API webRequest позволяло перехватывать, модифицировать и блокировать HTTP(S)-запросы в реальном времени. Фоновый скрипт получал полный доступ к заголовкам, телу и URL, что открывало возможности для продвинутых блокировщиков рекламы, инструментов отладки, модификации API-ответов.

Manifest V3 заменяет блокирующий webRequest на declarativeNetRequest — механизм, основанный на статических правилах, объявленных в JSON-файле. Правила загружаются один раз при старте расширения и обрабатываются в нативном коде браузера, минуя JavaScript. Это ускоряет обработку и исключает выполнение произвольного кода при каждом запросе.

Типичное правило:

{
"id": 1,
"priority": 1,
"action": { "type": "block" },
"condition": {
"urlFilter": "ads.example.com",
"resourceTypes": ["script", "image"]
}
}

Ограничения declarativeNetRequest:

  • Максимум 30 000 правил на расширение (в Chrome),
  • Невозможность динамически генерировать правила на основе содержимого страницы,
  • Отсутствие доступа к телу запроса/ответа.

Для сценариев, требующих анализа содержимого (например, детектор фишинга по тексту формы), разработчики используют комбинацию content_scripts и chrome.scripting, избегая прямой модификации трафика.

Запрет на удалённый код

Manifest V3 запрещает выполнение кода, загружаемого с удалённых серверов, включая:

  • eval(), new Function(),
  • innerHTML со строками, содержащими <script>,
  • динамическую загрузку скриптов через import() из внешних URL.

Весь исполняемый код должен быть включён в пакет расширения. Исключение — данные (JSON, конфигурации, правила фильтрации), которые можно загружать динамически и интерпретировать без выполнения как код.

Это требование защищает пользователей от ситуаций, когда расширение, прошедшее модерацию, позже загружает вредоносный модуль с сервера злоумышленника.

Обратная совместимость и миграция

Firefox сохраняет поддержку Manifest V2 дольше, чем Chromium-браузеры, и предоставляет плавный переход: расширения могут использовать гибридные подходы (например, фоновую страницу + декларативные правила). Однако долгосрочная стратегия всех браузеров — унификация вокруг Manifest V3.

Google предоставляет инструмент Manifest V3 Migration Guide и Extension Validator, который анализирует код и предлагает конкретные шаги по замене устаревших API. Основные этапы миграции:

  1. Замена background.scripts на background.service_worker.
  2. Перенос логики из webRequest.onBeforeRequest в declarativeNetRequest.
  3. Замена chrome.extension.getBackgroundPage() на chrome.runtime.sendMessage.
  4. Обновление CSP: удаление 'unsafe-eval', 'unsafe-inline'.
  5. Переработка долгоживущих состояний под модель «сохранить → завершить → восстановить».

Особенности реализации в Firefox

Хотя Firefox поддерживает основные API Chromium, есть принципиальные отличия, влияющие на архитектуру:

  • Поддержка XUL и legacy-дополнений прекращена, но сохранена гибкость в разрешениях: Firefox по-прежнему допускает блокирующий webRequest в Manifest V3 при явном запросе разрешения webRequestBlocking (в Chrome это невозможно).
  • Расширенный доступ к внутренностям браузера: через browser.experiments (только для подписанных расширений) можно взаимодействовать с ядром Gecko, но это требует дополнительной верификации.
  • Локализация через Fluent: помимо JSON-сообщений, Firefox поддерживает систему Fluent (.ftl), позволяющую строить грамматически корректные фразы с учётом числа, рода, падежа.
  • Кросс-браузерная разработка: для совместимости с Firefox и Chrome рекомендуется:
    • Использовать browser вместо chrome (автозамена через webextension-polyfill),
    • Проверять наличие API перед вызовом: if (browser.declarativeNetRequest) { ... },
    • Указывать отдельные manifest.json для каждой платформы или применять препроцессоры (например, web-ext-config.js).

Firefox также строже относится к приватности: расширения, отправляющие данные на внешние серверы, обязаны проходить дополнительную проверку на соответствие политике Recommended Extensions.


Обеспечение надёжности и сопровождаемости

Расширения — это полноценные программные продукты, и к ним применимы те же принципы инженерного качества, что и к веб- и десктоп-приложениям.

Архитектурные практики

  • Модульность кода: фоновая логика, контентные скрипты и UI-компоненты разделяются по ответственности. Используются ES-модули (import/export) даже в контентных скриптах (через type: "module" в content_scripts).
  • Типизация: применение TypeScript с типами @types/chrome и webextension-polyfill снижает количество ошибок и упрощает рефакторинг.
  • Изоляция побочных эффектов: сетевые запросы, работа с хранилищем, манипуляции с DOM выносятся в отдельные сервисы с чётким интерфейсом.
  • Восстанавливаемость состояния: фоновый сервис-воркер должен корректно восстанавливать контекст после перезапуска. Например, при обработке цепочки запросов используется идемпотентность: повторная отправка команды не приводит к дублированию эффекта.

Логирование и диагностика

  • Встроенная консоль браузера (console.log) подходит для отладки, но недостаточна для production-расширений.
  • Рекомендуется внедрить централизованный логгер с уровнями (debug, info, warn, error) и возможностью экспорта логов через popup-интерфейс.
  • Для критических ошибок — отправка анонимизированных отчётов в аналитику (только с согласия пользователя и с описанием в политике конфиденциальности).

Версионирование и обратная совместимость

  • Версия расширения в manifest.json должна соответствовать SemVer:
    • PATCH — исправление ошибок без изменения API,
    • MINOR — добавление функций с сохранением совместимости,
    • MAJOR — нарушение обратной совместимости (например, изменение формата данных в storage).
  • При изменении структуры данных в хранилище реализуется миграция в runtime.onInstalled:
    chrome.runtime.onInstalled.addListener(({ reason, previousVersion }) => {
    if (reason === 'update' && previousVersion) {
    const [major] = previousVersion.split('.').map(Number);
    if (major < 2) migrateFromV1ToV2();
    }
    });

Документирование расширений как программных продуктов

Для проектов уровня «Вселенная IT» важно рассматривать расширения не как набор скриптов, а как документированные программные компоненты. Рекомендуется включать в сопроводительную документацию:

  • Функциональное описание: что делает расширение, на каких сайтах активно, какие действия требуются от пользователя.
  • Схема архитектуры: диаграмма компонентов и потоков данных (например, в формате Mermaid):
  • Список разрешений с пояснением необходимости:
    • activeTab — для внедрения скрипта в текущую вкладку после явного действия,
    • storage — для сохранения пользовательских настроек между сессиями.
  • Руководство по разработке: требования к окружению, команды сборки, порядок запуска в режиме отладки.
  • Политика обработки данных: какие данные собираются, как хранятся, передаются ли третьим лицам.

Справочник API расширений по браузерам

1. Управление расширением и его состоянием

runtime

Предоставляет информацию о расширении, управление жизненным циклом, обмен сообщениями.

БраузерПоддержка
Chrome / EdgeПолная поддержка с Manifest V2 (v26+), обновления в V3: runtime.getURL(), runtime.onMessage, runtime.sendMessage, runtime.onInstalled, runtime.openOptionsPage(), runtime.getPlatformInfo(). В V3 runtime.getBackgroundPage() упразднён.
FirefoxПолная поддержка. Дополнительно: runtime.getBrowserInfo() — возвращает название, версию, движок (Gecko), идентификатор сборки.
SafariЧастичная: runtime.getURL(), runtime.onMessage, runtime.sendMessage. Нет onInstalled, openOptionsPage, getPlatformInfo.
Примечанияruntime.id — уникальный идентификатор расширения, стабилен между установками. Используется для изоляции данных.

management

Позволяет получать список установленных расширений и управлять ими (только для внутренних целей браузера, не для пользовательских расширений).

БраузерПоддержка
Chrome / EdgeТолько чтение: management.getAll(), management.getSelf(). Запись (setEnabled, uninstallSelf) разрешена только если расширение установлено в «режиме разработчика» или через политики предприятия.
FirefoxАналогично Chrome, но uninstallSelf() доступен всегда, если пользователь подтверждает действие.
SafariНе поддерживается.
ПримечанияИспользуется в системных и enterprise–расширениях для аудита. В обычных расширениях — редко.

2. Взаимодействие с вкладками и окнами

tabs

Управление вкладками: создание, обновление, закрытие, получение информации, внедрение кода.

БраузерПоддержка
Chrome / EdgeПолная: tabs.create(), tabs.update(), tabs.remove(), tabs.query(), tabs.sendMessage(), tabs.reload(), tabs.captureVisibleTab(). В Manifest V3 tabs.executeScript() заменён на scripting.executeScript().
FirefoxПолная, включая tabs.discard() (освобождение ресурсов без закрытия) и tabs.hide() / tabs.show() (для многопрофильных расширений).
SafariЧастичная: tabs.query(), tabs.sendMessage(), tabs.create(), tabs.update(). Нет captureVisibleTab, reload({ bypassCache: true }).
ПримечанияДля доступа к URL вкладки требуется разрешение tabs или activeTab. Без него доступна только pendingUrl (в Chrome) или null (в Firefox).

windows

Управление окнами браузера.

БраузерПоддержка
Chrome / EdgeПолная: windows.create(), windows.update(), windows.remove(), windows.getCurrent(), windows.getLastFocused(). Поддержка типов окон: normal, popup, devtools, panel.
FirefoxПоддержка normal, popup, detached_panel. Нет devtools как отдельного типа.
SafariТолько windows.getCurrent(), windows.getLastFocused(). Остальные методы отсутствуют.
Примечанияwindows.update(windowId, { focused: true }) возвращает фокус на окно — полезно для вспомогательных инструментов.

scripting

Замена tabs.executeScript() в Manifest V3. Позволяет внедрять функции и файлы в вкладки.

БраузерПоддержка
Chrome / EdgeПолная с Chrome 88+/Edge 88+: executeScript(), getRegisteredContentScripts(), registerContentScripts(), unregisterContentScripts(). Поддержка world: 'MAIN' для доступа к глобальному контексту страницы (обход песочницы).
FirefoxПоддержка с Firefox 90+, но без world: 'MAIN'. Вместо этого — contentScriptOptions и внедрение через <script>-тег.
SafariПоддержка с Safari 15.4+ (macOS 12.3+, iOS 15.4+). Только executeScript(), без регистрации скриптов.
ПримечанияКод, передаваемый в func, должен быть сериализуемым. Замыкания, ссылки на внешние переменные — недопустимы. Используйте args для передачи данных.

3. Работа с хранилищами

storage

Хранение данных: локальное, синхронизированное, управляемое.

БраузерПоддержка
Chrome / Edgestorage.local (до 5 МБ по умолчанию, расширяемо), storage.sync (100 КБ, синхронизация через аккаунт Google), storage.managed (политики предприятия). Событие onChanged.
FirefoxАналогично: local, sync (до 10 МБ), managed. Дополнительно: storage.session (временное хранилище, аналог sessionStorage, но для расширения).
SafariТолько storage.local. sync отсутствует — синхронизация возможна только через iCloud, но не встроена в API.
Примечанияstorage.sync не гарантирует мгновенную синхронизацию. Данные могут дублироваться при конфликтах. Для критичных данных — использовать local + собственный сервер синхронизации.

cookies

Чтение и запись HTTP-кук.

БраузерПоддержка
Chrome / EdgeПолная: cookies.get(), cookies.set(), cookies.remove(), cookies.getAll(). Требуется разрешение cookies + host_permissions на домен.
FirefoxПолная, с поддержкой firstPartyDomain (для защиты от кросс-сайтовых атак).
SafariОграниченная: только get(), getAll(), remove(). Запись кук (set) запрещена — политика приватности Safari.
ПримечанияВ Safari блокировка записи кук делает невозможным реализацию расширений, имитирующих вход на сайты через подстановку токенов. Альтернатива — использование webRequest для модификации заголовков (если разрешено).

indexedDB

Полноценная клиентская БД. Доступна из фонового скрипта и страниц расширения.

БраузерПоддержка
ВсеПолная, как в веб-приложениях. indexedDB.open() работает в фоновом сервис-воркере (V3), popup, options.
ПримечанияindexedDB изолирован по extension://<id>/. Данные не видны веб-сайтам и другим расширениям. Используется для хранения больших объёмов структурированных данных (например, кэш статей, история действий).

4. Сетевые запросы и фильтрация

webRequest

Перехват и модификация HTTP(S)-трафика.

БраузерПоддержка
Chrome / EdgeВ Manifest V2: полная поддержка (onBeforeRequest, onHeadersReceived, onCompleted). В Manifest V3: только неблокирующий режим — можно читать, но не изменять/блокировать. Блокировка передана в declarativeNetRequest.
FirefoxВ Manifest V3: сохранён блокирующий режим при явном запросе разрешения "webRequestBlocking". Это ключевое преимущество для адблокеров и инструментов тестирования.
SafariЧастичная: webRequest.onBeforeSendHeaders, onHeadersReceived — только для чтения. Модификация заголовков возможна через Content Blocker (декларативный механизм), но не через JS.
ПримечанияВ Chrome/Edge использование webRequest в V3 вызывает предупреждение в консоли: «Blocking webRequest listeners are deprecated».

declarativeNetRequest

Декларативная фильтрация трафика.

БраузерПоддержка
Chrome / EdgeПолная с Chrome 84+/Edge 84+. До 30 000 правил в rule_resources, до 50 000 — с разрешением declarativeNetRequestFeedback. Поддержка regexFilter (с ограничениями), динамических правил через .updateDynamicRules().
FirefoxПоддержка с Firefox 99+, но без regexFilter. Правила объявляются в background-скрипте через browser.declarativeNetRequest.updateDynamicRules().
SafariНе поддерживается. Вместо этого — Content Blocker (JSON-файл с правилами, загружаемый в настройках расширения). Максимум 50 000 правил, только блокировка и скрытие элементов по CSS-селекторам.
ПримечанияdeclarativeNetRequest не может изменять тело ответа. Для этого требуется webRequest (Firefox) или обработка на стороне контентного скрипта.

5. Пользовательский интерфейс и взаимодействие

action (ранее browserAction / pageAction)

Управление иконкой расширения на панели инструментов.

БраузерПоддержка
Chrome / EdgeПолная: setPopup(), setBadgeText(), setBadgeBackgroundColor(), setTitle(), onClicked.
FirefoxПолная. Дополнительно: action.enable(tabId), action.disable(tabId) — управление по вкладкам.
SafariПоддержка setPopup(), onClicked. Нет значков (badges), заголовков по вкладкам.
Примечанияaction.setBadgeText({ text: "99+" }) полезно для уведомлений без всплывающих окон. В Safari для индикации используется анимация иконки.

contextMenus

Добавление пунктов в контекстное меню.

БраузерПоддержка
Chrome / EdgeПолная: create(), update(), remove(), removeAll(). Поддержка типов: page, selection, link, editable, image, video, audio.
FirefoxПолная, плюс contexts: ["frame"], ["bookmark"], ["tab"].
SafariПоддержка только page, selection, link, image. Нет динамического обновления — меню перестраивается при каждом щелчке правой кнопкой.
ПримечанияТребуется разрешение contextMenus. В Firefox можно указать documentUrlPatterns и targetUrlPatterns отдельно.

notifications

Системные уведомления.

БраузерПоддержка
Chrome / EdgeПолная: create(), clear(), onClicked, onButtonClicked. Поддержка кнопок, изображений, приоритета.
FirefoxБез кнопок и изображений в уведомлениях (ограничение ОС). Работает только текст и иконка.
SafariТребуется явное разрешение notifications + пользователь должен подтвердить запрос. Поддержка кнопок с macOS 12+.
ПримечанияВ macOS уведомления от Safari появляются в Центре уведомлений, но не вызывают звук без настройки в системе.

6. Безопасность и приватность

privacy

Управление настройками приватности браузера.

БраузерПоддержка
Chrome / EdgeЧастичная: privacy.network.networkPredictionEnabled, privacy.services.passwordSavingEnabled, privacy.websites.referrerEnabled. Требуется разрешение privacy.
FirefoxНет privacy API. Управление через browser.experiments (только для подписанных расширений).
SafariНе поддерживается.
ПримечанияИзменения через privacy влияют на весь браузер, а не только на расширение. Использование крайне ограничено — только для enterprise-инструментов.

permissions

Динамический запрос разрешений.

БраузерПоддержка
Chrome / EdgeПолная: permissions.request(), permissions.contains(), permissions.remove(), onAdded, onRemoved. Позволяет запрашивать доступ к сайтам (origins) и API (permissions) по мере необходимости.
FirefoxПолная. Дополнительно: permissions.request({ permissions: ["downloads.shelf"] }) — доступ к панели загрузок.
SafariТолько permissions.request({ origins: [...] }). Запрос API-разрешений (например, tabs) невозможен — все права должны быть указаны в манифесте.
ПримечанияДинамические разрешения повышают доверие: пользователь даёт доступ только когда это действительно нужно. Рекомендуется использовать вместо запроса всех прав при установке.

7. Специфичные API

alarms

Таймеры, не зависящие от жизненного цикла сервис-воркера.

БраузерПоддержка
ВсеПолная: create(), get(), getAll(), clear(), onAlarm. Минимальный интервал — 1 минута (в Chrome/Edge), 1 секунда (в Firefox при periodInMinutes: 0.0167).
ПримечанияЕдинственный способ выполнять периодические задачи в Manifest V3. Используется для синхронизации, напоминаний, фоновой проверки.

downloads

Управление загрузками.

БраузерПоддержка
Chrome / EdgeПолная: download(), search(), pause(), resume(), cancel(), onCreated, onChanged. Требуется разрешение downloads.
FirefoxПолная, плюс downloads.erase(), downloads.open(), downloads.show().
SafariНе поддерживается.
ПримечанияЗагрузка файлов возможна только по HTTPS (в Chrome/Edge). В Firefox — и по HTTP, но с предупреждением.

i18n

Интернационализация.

БраузерПоддержка
ВсеПолная: getMessage(), getAcceptLanguages(), detectLanguage(). Локализация через _locales/<lang>/messages.json.
FirefoxДополнительно: поддержка Fluent (.ftl) через browser.i18n.getMessage() с параметрами.
Примечания@@extension_id, @@ui_locale, @@bidi_dir — служебные сообщения для динамических URL и направления текста.

Обобщение поддержки ключевых возможностей

ФункцияChrome / EdgeFirefoxSafari
Manifest V3 (фоновый сервис-воркер)ДаДа (гибридный режим)Да
Блокирующий webRequestНетДа (с разрешением)Нет
declarativeNetRequestДаДа (ограниченно)Нет (Content Blocker)
Динамические разрешенияДаДаЧастично (только origins)
Синхронизация данных (storage.sync)ДаДаНет
Запись кук (cookies.set)ДаДаНет
Доступ к world: 'MAIN'ДаНетНет
Автоматическая публикация через CIДа (Chrome Web Store API)Да (web-ext sign)Нет (требуется Xcode, ручная загрузка)