Chrome Extension: Запуск скрипта самым первым в контексте страницы

Мне необходимо реализовать подмену WebSocket (что то похожее на это), но я столкнулся со следующей проблемой: некоторый скрипт страницы, на которой я хочу сделать подмену WebSocket, выполняется раньше моего и делает свою подмену. Возможно странно, ведь в manifest.json я прописал "run_at": "document_start". Внедрение скрипта, который выполняет подмену выглядит так:

scriptTag = document.createElement('script');
scriptTag.src = chrome.runtime.getURL('wrapper.js');
scriptTag.onload = function () { this.remove(); };
(document.head || document.documentElement).appendChild(scriptTag);

Как заставить браузер выполнить мой внедрённый скрипт гарантированно самым первым?


Ответы (1 шт):

Автор решения: Alexander Lonberg

Получить тело ответа и модифицировать через webRequest, скорее всего, не получится. Тут можно посмотреть поддержку webRequest.filterResponseData и webRequest.StreamFilter. Несмотря на ограничения, webRequest позволяет отслеживать/изменять некоторые заголовки и отменять запросы.

Отправной точкой для получения тела запроса, может быть debugger.

Небольшой пример (думаю, там можно будет что-то блокировать и менять):

background.js

const cRuntime = chrome.runtime
const cTabs = chrome.tabs
const cDebugger = chrome.debugger
const cBrowserAction = chrome.browserAction
const requiredVersion = '1.3'

const tabAttached = new Set()

// Здесь можем просматривать всякие параметры запроса
function onEvent({ tabId }, method, params) {
  // Событий разных много: responseReceived, loadingFinished, loadingFailed и т.п.
  // Для просмотра WebSocket-а достаточно этого
  if (method === 'Network.webSocketFrameReceived') {
    console.log(params.response)
  }
}

// Когда вкладка отключилась от debugger, например закрытие страницы
function onDetach({ tabId }) {
  tabAttached.delete(tabId)
  cDebugger.sendCommand({ tabId }, 'Network.disable', undefined, () => { })
  cDebugger.onDetach.removeListener(onDetach)
  cDebugger.onEvent.removeListener(onEvent)
  console.log('onDetach', tabId)
}

// Присоединяем таб к chrome.debugger
function attach(tabId) {
  if (tabAttached.has(tabId)) {
    return
  }
  cDebugger.attach({ tabId }, requiredVersion, () => {
    if (cRuntime.lastError) {
      console.error(cRuntime.lastError)
      return
    }
    // Активируем прослушивание трафика
    cDebugger.sendCommand({ tabId }, 'Network.enable', undefined, () => {
      if (cRuntime.lastError) {
        console.error(cRuntime.lastError)
        return
      }
      console.log(`Присоединили tabId:${tabId}`)
      tabAttached.add(tabId)
      cDebugger.onDetach.addListener(onDetach)
      cDebugger.onEvent.addListener(onEvent)
    })
  })
}

// Используем кнопку расширения для активации прослушивания
cBrowserAction.onClicked.addListener(async (tab) => {
  if (cRuntime.lastError) {
    console.error(cRuntime.lastError)
    return
  }
  if (/^http(s)?.+/.test(tab.url)) {
    attach(tab.id)
  }
})

manifest.json

{
  "manifest_version": 2,
  "name": "Demo debugging",
  "description": "Demo debugging",
  "version": "0.1.0",
  "author": "Alexander Lonberg",
  "browser_action": {},
  "permissions": [
    "*://*/*",
    "tabs",
    "debugger"
  ],
  "background": {
    "scripts": [
      "background-live-reload.js",
      "background.js"
    ]
  }
}

Расширение активирует прослушивание трафика страницы иконкой самого расширения.
Ищем любой сайт с запросами WebSocket-а, например такой ruТОЧКАtradingviewТОЧКАcom.
Открываем график и фоновую страницу DevTools расширения - смотрим в консоль.

Но у такого способа есть небольшой недостаток - оно указывает юзеру, что кто-то подключился к DevTools.

введите сюда описание изображения

→ Ссылка