Chrome 請求過濾擴展實現

引子

接着 Chrome 擴展 : 入門,接下來開始實現一開始本身的想法:網絡請求過濾。簡單的說就是監聽某個網站的全部請求,把想要的請求在擴展插件中展現出來。擴展名爲 Capture Request 。html

需求具體化

上面的想法比較模糊,爲了達到這個目的,結合文檔的示例,要作的有:git

  1. 擴展要有對應的圖標及提示。
  2. 點擊工具欄擴展圖標,打開一個新的 Tab 頁面,用來展現請求的相關信息。
  3. 擴展監聽處於激活 Tab 的網站請求,能夠配置過濾監聽的網址。
  4. 對監聽的請求,支持根據 url 篩選並導出。

有些功能不方便直接在文檔找到,這個時候,建議在 Chrome 商店找一個開源擴展,根據效果看裏面用的一些 API ,而後找到對應文檔。這裏參考了 FeHelper 裏面的一些實現。須要注意到是 FeHelper 開發基於 manifest_version 版本爲 2 ,如下開發擴展基於的版本是推薦的版本爲 3 ,完整代碼見 Capture Requestgithub

實現

圖標相關信息配置

按照入門裏面介紹的信息,圖標可能出現的地方有工具欄、擴展管理頁、權限警告和 favicon 上,在 manifest.json 中配置的下面相關字段:web

{
  "name": "Capture Request",
  "description": "Capture Request",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_icon": {
      "16": "xxx.png",
      "32": "xxx.png",
    }
  },
  "icons":{
    "16": "xxx.png",
    "32": "xxx.png",
  }
}

什麼尺寸圖標用在什麼地方的詳細說明在這裏,文檔上推薦用 PNG 的圖片格式。按照這個來,發現有的圖標過小了會看起來明顯模糊,也能夠用比較大的尺寸,Chrome 會本身把圖片壓縮到須要的尺寸。chrome

點擊擴展打開新 Tab 頁面

入門裏面點擊擴展的展示形式是打開了一個彈窗,在文檔 Design the user interface 中介紹的形式有 Popup 、Tooltip、Omnibox、Context menu、Override pages ,最有可能就是 Override pages ,但試了一下發現沒有效果,因而去看別人的實現,發現能夠經過監聽點擊圖標事件打開新 Tab 。編程

但看似能夠直接用的 API ,實際上還有下面幾點要考慮:json

  1. 在哪裏監聽這事件?
  2. 何時監聽這個事件?
  3. 怎麼打開新 Tab ?
  4. 是否須要權限,若是須要,涉及那些權限?

入門裏面添加功能是經過後臺腳本,文檔開頭說的一句我的以爲很重要:緩存

擴展是經過基於事件編程來改變或加強 Chrome 瀏覽體驗。

從文檔中能夠發現,在後臺腳本中能夠解決上面提的第 一、2 兩個問題,須要的權限是 scripting網絡

打開 Tab 使用的 API 是 chrome.tabs ,須要的權限是 tabsapp

主要作法是在 manifest.json 中添加下面配置:

{
  ...
+ "permissions": [
+   "scripting",
+   "tabs",
+ ],
+ "background": {
+   "service_worker": "background.js"
+ },
  ...
}

而後新建後臺腳本文件 background.js ,並添加下面主要邏輯代碼:

chrome.action.onClicked.addListener(() => {
  chrome.tabs.create({
    url: 'page.html'
  });
});

監聽請求及配置

要把處於激活 Tab 網站上的請求顯示到打開的擴展頁面上,主要須要考慮的點有:

  1. 怎麼找到激活的 Tab ?
  2. 怎麼截獲網頁請求?
  3. 截獲的請求怎麼同步到擴展自定義頁面上?

經過上面打開 Tab 的效果實現,能夠聯想到相關的 API 應該也在 chrome.tabs 中,發現提供了 query 方法能夠解決第 1 個問題。

截獲請求的方法經過網上搜索,發現文檔 chrome.webRequest ,裏面詳細的介紹了擴展中請求的生命週期及觸發的事件,通過對比思考,我的最後決定監聽 onResponseStarted 事件,須要的權限是 webRequest 。這樣就解決了第 2 個問題。

參照入門裏面改變顏色的方式,相似的能夠把請求緩存到 chrome.storage ,而後在擴展頁面獲取,須要的權限是 storage 。關於數據同步,能夠經過監聽 chrome.storage.onChanged 事件拿到變更的最新數據。這樣就解決了第 3 個問題。

在調試的過程當中,發現存在本地的數據使用 chrome.storage.sync 時,請求達到必定量後,會報錯。看了文檔發現這種方式的最大值有必定的限制,不太適合存儲大量請求數據的場景,使用 chrome.storage.local 更加合適。

配置過濾請求的方式可直接按照入門裏面的配置方式處理,但有一點須要注意的是,每當配置更新的時候,須要從新監聽 onResponseStarted 事件。

主要作法是在 manifest.jsonpermissions 字段中添加 webRequeststorage

background.js 中添加主要代碼:

// 儲存請求數據默認值
let requestList = []
// 網址過濾的默認值
let urlPattern = '<all_urls>'

// 監聽請求事件的處理程序
const handlerResponseStarted = (details) => {
  // 找處處於激活狀態的 Tab
  chrome.tabs.query({ active: true }, (tab) => {
    requestList.unshift(details)
    chrome.storage.local.set({ requestList });
    return { cancel: true };
  })
}

// 監聽 storage 改變事件
chrome.storage.onChanged.addListener((changeObj, areaName) => {
  const { urlPattern } = changeObj
  // 因爲在 page.html 裏面也監聽了,因此要判斷是否是 urlPattern 變更了
  if (areaName !== 'local' || !urlPattern) {
    console.warn('urlPattern does not change')
    return;
  }
  const { newValue } = urlPattern
  const hasAddListen = chrome.webRequest.onResponseStarted.hasListener(handlerResponseStarted)
  if (hasAddListen) {
    chrome.webRequest.onResponseStarted.removeListener(handlerResponseStarted);
  }
  chrome.webRequest.onResponseStarted.addListener(
    handlerResponseStarted,
    { urls: [newValue] },
  );
})

爲擴展頁面 page.html 添加腳本文件 pages.js ,添加關鍵邏輯:

chrome.storage.onChanged.addListener((changeObj, areaName) => {
    const { requestList } = changeObj
    // 因爲在 background.js 裏面也監聽了,因此要判斷是否是 requestList 變更了
    if (areaName !== 'local' || !requestList) {
      console.warn('requestList does not change')
      return;
    }
    const { newValue } = requestList || { newValue: [] }
    const newItem = newValue[0] || null
    if (!newItem) {
      console.warn('no data')
      return;
    }
    // 顯示數據的邏輯
    showData(newItem)
  })

導出數據

截獲了想要的數據,有須要導出到本地使用的場景,參考 FeHelper 裏面的實現,找到了文檔 chrome.downloads ,須要的權限是 downloads

主要作法是在 manifest.jsonpermissions 字段中添加 downloads

pages.js 添加關鍵邏輯:

let localFilterList = []; // 頁面篩選後的數據
  // 點擊導出的按鈕
  const exportEle = document.querySelector('#operate-export')
  exportEle.addEventListener('click', () => {
    if (!localFilterList.length) {
      alert('無有效數據')
      return;
    }
    const txt = JSON.stringify(localFilterList)
    let blob = new Blob([txt], { type: 'application/octet-stream' });
    // 文件名稱獲取時間的秒數,可按照本身喜愛定義
    let dt = (new Date()).getSeconds();
    chrome.downloads.download({
      url: URL.createObjectURL(blob),
      saveAs: true,
      conflictAction: 'overwrite',
      filename: dt + '.json'
    });
  })

參考資料

相關文章
相關標籤/搜索