Несколько дней назад я опубликовал пост о том, как начать создавать расширение Chrome с помощью React. Этот пост был лишь отправной точкой для расширения.

В этом посте мы немного углубимся, и я объясню, как добавить в расширение возможности веб-перехвата с помощью API разработчика Chrome.

Примечание. Этот пост основан на предыдущем сообщении, поэтому обязательно прочтите предыдущий пост перед чтением.

Добавление фонового скрипта

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

Чтобы создать фоновый сценарий, вам нужно сделать две вещи - добавить файл сценария для фонового сценария и обновить манифест расширения для поддержки этого сценария.

В расширении добавьте к общедоступной папке папку app и добавьте в нее файл background.js. Сначала файл будет пустым. Затем добавьте в манифест расширения следующее свойство:

"background": {
    "scripts": ["app/background.js"]
}

Теперь проект будет выглядеть так:

Снова запустите сборку с помощью команды npm run build и перейдите к списку расширений Chrome. Используйте Ctrl + R или нажмите ссылку Обновить в элементе списка расширений, чтобы обновить расширение до последней версии.

В расширении вы увидите новую строку со ссылкой:

Inspect views: background page

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

Теперь мы готовы двигаться дальше и реализовать некоторые функции фонового скрипта.

Работа с Chrome webRequest API

Как было сказано в начале поста, мы захотим сделать наше расширение прокси-сервером для сетевых запросов. Чтобы перехватывать HTTP-запросы, вы должны знать API Chrome webRequest.

Chrome webRequest API - это набор полезных событий и функций, которые позволяют нам отслеживать и анализировать веб-трафик. Вы можете подключиться к жизненному циклу веб-запроса с помощью встроенных событий, а затем решить, что делать. Например, блокировщики рекламы используют такой механизм перехвата для отмены запросов к известным рекламным доменам.

Чтобы использовать API, вы должны объявить разрешение webRequest как часть манифеста расширения. В манифесте просто добавьте в массив разрешений webRequest и домен, который вы хотите отслеживать:

"permissions": [
  "webRequest",
  "*://developer.mozilla.org/"
]

Примечание. Если вы не укажете разрешения, вы не сможете использовать API в своем расширении.

Наиболее полезные события, которые вы, вероятно, захотите прослушать:

  • onBeforeRequest - запускается перед отправкой запроса.
  • onCompleted - запускается при успешном завершении запроса.
  • onErrorOccured - срабатывает при ошибке запроса.

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

В файл background.js добавьте следующий код:

(function() {
    const tabStorage = {};
    const networkFilters = {
        urls: [
            "*://developer.mozilla.org/*"
        ]
    };

    chrome.webRequest.onBeforeRequest.addListener((details) => {
        const { tabId, requestId } = details;
        if (!tabStorage.hasOwnProperty(tabId)) {
            return;
        }

        tabStorage[tabId].requests[requestId] = {
            requestId: requestId,
            url: details.url,
            startTime: details.timeStamp,
            status: 'pending'
        };
        console.log(tabStorage[tabId].requests[requestId]);
    }, networkFilters);

    chrome.webRequest.onCompleted.addListener((details) => {
        const { tabId, requestId } = details;
        if (!tabStorage.hasOwnProperty(tabId) || !tabStorage[tabId].requests.hasOwnProperty(requestId)) {
            return;
        }

        const request = tabStorage[tabId].requests[requestId];

        Object.assign(request, {
            endTime: details.timeStamp,
            requestDuration: details.timeStamp - request.startTime,
            status: 'complete'
        });
        console.log(tabStorage[tabId].requests[details.requestId]);
    }, networkFilters);

    chrome.webRequest.onErrorOccurred.addListener((details)=> {
        const { tabId, requestId } = details;
        if (!tabStorage.hasOwnProperty(tabId) || !tabStorage[tabId].requests.hasOwnProperty(requestId)) {
            return;
        }

        const request = tabStorage[tabId].requests[requestId];
        Object.assign(request, {
            endTime: details.timeStamp,           
            status: 'error',
        });
        console.log(tabStorage[tabId].requests[requestId]);
    }, networkFilters);

    chrome.tabs.onActivated.addListener((tab) => {
        const tabId = tab ? tab.tabId : chrome.tabs.TAB_ID_NONE;
        if (!tabStorage.hasOwnProperty(tabId)) {
            tabStorage[tabId] = {
                id: tabId,
                requests: {},
                registerTime: new Date().getTime()
            };
        }
    });
    chrome.tabs.onRemoved.addListener((tab) => {
        const tabId = tab.tabId;
        if (!tabStorage.hasOwnProperty(tabId)) {
            return;
        }
        tabStorage[tabId] = null;
    });
}());

Давайте разберемся, что делает код.

Вначале мы объявляем хранилище в памяти для хранения информации о запросах на вкладках. Мы также объявляем объект сетевого фильтра, который позже будет использоваться любым событием webRequest для фильтрации только желаемых запросов. В примере кода, только если кто-то перейдет в Mozilla Developer Network (MDN), мы запустим наш код.

После объявления переменных мы добавляем слушателей к событиям onBeforeRequest, onCompleted и onErrorOccured. В каждом случае мы добавляем некоторые данные запроса, которые собираемся использовать позже, в наше хранилище. Обратите внимание, что для каждой вкладки мы создаем собственный сборщик запросов.

И последнее, но не менее важное: мы используем chrome.tabs.onActivated для отслеживания создания вкладки и chrome.tabs.onRemoved для отслеживания удаления вкладки. Для каждой вкладки мы добавим новый объект вкладки в tabStorage или удалим его. Когда веб-запрос будет отправлен, мы сможем добавить его детали к уже доступному объекту вкладки. Это пока что в фоновом скрипте.

Теперь вы можете скомпилировать расширение, перезагрузить его в списке расширений Chrome, а затем открыть фоновый скрипт для отладки. Для этого используйте ссылку на фоновую страницу в списке расширений Chrome. Затем перейдите на веб-страницу в MDN и просмотрите в консоли собранную информацию.

Резюме

В этом посте я объяснил, как добавить фоновый скрипт к расширению Chrome. Я также показал вам, как использовать фоновый скрипт для отслеживания веб-запросов с помощью API webRequest. В следующем посте я покажу вам, как отражать данные, которые мы собрали, во всплывающем пользовательском интерфейсе с помощью React.

Увидимся в следующем посте!