Chrome 不相容性

WebExtension API 旨在提供所有主流瀏覽器的相容性,因此擴充套件程式應該能在任何瀏覽器上以最小的改動執行。

然而,Chrome(以及基於 Chromium 的瀏覽器)、Firefox 和 Safari 之間存在顯著差異。特別是:

  • WebExtension API 在不同瀏覽器上的支援程度不同。詳情請參閱JavaScript API 瀏覽器支援

  • manifest.json 鍵在不同瀏覽器上的支援程度不同。詳情請參閱 manifest.json 頁面上的“瀏覽器相容性”部分

  • 擴充套件 API 名稱空間

    • 在 Firefox 和 Safari 中:透過 browser 名稱空間訪問擴充套件 API。為了與 Chrome 相容,也支援 chrome 名稱空間。
    • 在 Chrome 中:透過 chrome 名稱空間訪問擴充套件 API。(參見 Chrome bug 798169
  • 非同步 API

    • 在 Firefox 和 Safari 中:非同步 API 使用 Promise 實現。
    • 在 Chrome 中:在 Manifest V2 中,非同步 API 使用回撥實現。在 Manifest V3 中,大多數適當的方法都支援 Promise。(參見 Chrome bug 328932)Manifest V3 中支援回撥是為了向後相容。

本頁的其餘部分將詳細介紹這些和其他不相容性。

JavaScript API

chrome.* 和 browser.* 名稱空間

  • 在 Firefox 和 Safari 中:透過 browser 名稱空間訪問 API。

    js
    browser.browserAction.setIcon({ path: "path/to/icon.png" });
    
  • 在 Chrome 中:透過 chrome 名稱空間訪問 API。

    js
    chrome.browserAction.setIcon({ path: "path/to/icon.png" });
    

回撥和 Promise

  • 在 Firefox 和 Safari(所有版本)以及 Chrome(從 Manifest V3 開始)中:非同步 API 使用 Promise 返回值。

    js
    function logCookie(c) {
      console.log(c);
    }
    
    function logError(e) {
      console.error(e);
    }
    
    let setCookie = browser.cookies.set({
      url: "https://mdn.club.tw/",
    });
    setCookie.then(logCookie, logError);
    
  • 在 Chrome 中:在 Manifest V2 中,非同步 API 使用回撥返回值,並使用 runtime.lastError 傳遞錯誤。在 Manifest V3 中,支援回撥以實現向後相容性,同時在大多數適當的方法上支援 Promise

    js
    function logCookie(c) {
      if (chrome.runtime.lastError) {
        console.error(chrome.runtime.lastError);
      } else {
        console.log(c);
      }
    }
    
    chrome.cookies.set({ url: "https://mdn.club.tw/" }, logCookie);
    

Firefox 同時支援 chrome 和 browser 名稱空間

作為移植輔助,WebExtensions 的 Firefox 實現支援使用回撥的 chrome 和使用 Promise 的 browser。這意味著許多 Chrome 擴充套件程式在 Firefox 中無需更改即可工作。

注意:Firefox 和 Safari 支援 browser 名稱空間。在 Chrome bug 798169 解決之前,Chrome 不提供 browser 名稱空間。

如果您選擇編寫使用 browser 和 Promise 的擴充套件程式,Firefox 提供了一個 polyfill,應該能讓它在 Chrome 中執行:https://github.com/mozilla/webextension-polyfill

部分支援的 API

JavaScript API 瀏覽器支援頁面包含所有在 Firefox 中有任何支援的 API 的相容性表格。如果 API 方法、屬性、型別或事件的支援存在注意事項,這些表格中會用星號 "*" 表示。選擇星號會展開表格以顯示解釋注意事項的註釋。

這些表格是根據儲存在 GitHub 中的 JSON 檔案中的相容性資料生成的。

本節的其餘部分描述了在構建跨瀏覽器擴充套件程式時可能需要考慮的主要相容性問題。此外,請記住檢視瀏覽器相容性表格,因為它們可能包含額外的相容性資訊。

Notifications API

對於 notifications.create(),當 type 為 "basic" 時

  • 在 Firefox 中iconUrl 是可選的。
  • 在 Chrome 中iconUrl 是必需的。

當用戶點選通知時

  • 在 Firefox 中:通知會立即清除。
  • 在 Chrome 中:情況並非如此。

如果您在快速連續呼叫 notifications.create() 多次

  • 在 Firefox 中:通知可能不會顯示。在 notifications.create() 回撥函式中等待後續呼叫不足以防止這種情況。

代理 API

Firefox 和 Chrome 都包含代理 API。然而,這兩個 API 的設計是不相容的。

Firefox 和 Chrome 提供了不相容的 API 來處理側邊欄

  • 在 Firefox(和 Opera)中:側邊欄使用 sidebar_action manifest 鍵指定,並使用 sidebarAction API 進行操作。
  • 在 Chrome 中:可以使用 side_panel manifest 鍵指定初始側邊欄。然後,sidePanel API 可以操作面板。

Tabs API

使用 tabs.executeScript()tabs.insertCSS()

  • 在 Firefox 中:傳入的相對 URL 相對於當前頁面 URL 解析。
  • 在 Chrome 中:相對 URL 相對於擴充套件程式的基 URL 解析。

要實現跨瀏覽器工作,您可以將路徑指定為絕對 URL,從擴充套件程式的根目錄開始,如下所示:

/path/to/script.js

呼叫 tabs.remove()

  • 在 Firefox 中tabs.remove() Promise 在 beforeunload 事件之後完成。
  • 在 Chrome 中:回撥不等待 beforeunload

WebRequest API

  • 在 Firefox 中

    • 請求只能在其原始 URL 使用 http:https: 方案時進行重定向。

    • activeTab 許可權不允許攔截當前選項卡中的網路請求。(參見 bug 1617479

    • 系統請求(例如,擴充套件程序升級或搜尋欄建議)不會觸發事件。

    • 如果擴充套件程式想要將公共(例如,HTTPS)URL 重定向到擴充套件程式頁面,擴充套件程式的 manifest.json 檔案必須包含一個 web_accessible_resources 鍵,其中包含擴充套件程式頁面的 URL。

      注意: 任何網站都可以連結或重定向到該 URL,擴充套件程式應將任何輸入(例如 POST 資料)視為來自不受信任的來源,就像普通網頁一樣。

    • 一些 browser.webRequest.* API 允許返回非同步解析 webRequest.BlockingResponse 的 Promise。

  • 在 Chrome 中:只有 webRequest.onAuthRequired 透過提供 'asyncBlocking' 來支援非同步 webRequest.BlockingResponse,透過回撥而不是 Promise。

Windows API

  • 在 Firefox 中:windows API 的 onFocusChanged 在焦點更改時會觸發多次。

不支援的 API

DeclarativeContent API

其他不相容性

CSS 中的 URL

  • 在 Firefox 中:注入的 CSS 檔案中的 URL 相對於 CSS 檔案解析。
  • 在 Chrome 中:注入的 CSS 檔案中的 URL 相對於 它們被注入的頁面解析。

後臺頁面中對話方塊的支援

web_accessible_resources

  • 在 Firefox 中:資源被分配一個隨機的 UUID,該 UUID 在每個 Firefox 例項中都會更改:moz-extension://«random-UUID»/«path»。這種隨機性可能會阻止您執行某些操作,例如將擴充套件程式的 URL 新增到其他域的 CSP 策略中。
  • 在 Chrome 中:當資源列在 web_accessible_resources 中時,它可以透過 chrome-extension://«your-extension-id»/«path» 訪問。擴充套件程式 ID 對於擴充套件程式是固定的。

Manifest "key" 屬性

  • 在 Firefox 中:由於 Firefox 對 web_accessible_resources 使用隨機 UUID,因此不支援此屬性。Firefox 擴充套件程式可以透過 browser_specific_settings.gecko.id manifest 鍵(參見 browser_specific_settings.gecko)固定其擴充套件程式 ID。
  • 在 Chrome 中:使用未打包的擴充套件程式時,manifest 可能包含一個 "key" 屬性,以在不同機器上固定擴充套件程式 ID。這主要在使用 web_accessible_resources 時有用。

內容指令碼 HTTP(S) 請求

  • 在 Firefox 中:當內容指令碼發出 HTTP(S) 請求時,您必須提供絕對 URL。
  • 在 Chrome 中:當內容指令碼向相對 URL(例如 /api)發出請求(例如,使用 fetch())時,它會發送到 https://example.com/api

內容指令碼環境

  • 在 Firefox 中:內容指令碼環境的全域性作用域不嚴格等於 windowFirefox bug 1208775)。更具體地說,全域性作用域(globalThis)像往常一樣由標準 JavaScript 特性組成,並且 window 作為全域性作用域的原型。大多數 DOM API 都透過 window 從頁面繼承,透過 Xray vision 來保護內容指令碼不受網頁修改的影響。內容指令碼可能會遇到來自其全域性作用域的 JavaScript 物件或來自網頁的 Xray 包裝版本。
  • 在 Chrome 中:全域性作用域是 window,並且可用的 DOM API 通常獨立於網頁(除了共享底層 DOM)。內容指令碼不能直接訪問網頁中的 JavaScript 物件。

內容指令碼頁面事件處理程式

  • 在 Firefox 中:每個世界不維護單獨的事件處理程式。這意味著最近請求 element.onclick = xxx 的內容指令碼會覆蓋頁面或其他擴充套件程式的事件處理程式。
  • 在 Chrome 中:每個世界維護單獨的事件處理程式,因此 Chrome 為頁面和每個請求的擴充套件程式維護事件處理程式。

要解決這種不一致性,請使用 addEventListener() 註冊事件監聽器。有關更多資訊,請參閱 Firefox bug 1965975

從內容指令碼在網頁中執行程式碼

  • 在 Firefox 中:eval 在內容指令碼的上下文中執行程式碼,而 window.eval 在頁面的上下文中執行程式碼。請參閱在內容指令碼中使用 eval
  • 在 Chrome 中:evalwindow.eval 始終在內容指令碼的上下文中執行程式碼,而不是在頁面的上下文中。

在內容指令碼之間共享變數

  • 在 Firefox 中:您無法透過在一個指令碼中將變數分配給 this.{variableName},然後在另一個指令碼中嘗試使用 window.{variableName} 訪問它們來在內容指令碼之間共享變數。這是 Firefox 沙盒環境造成的限制。此限制可能會被移除;請參閱 Firefox bug 1208775

導航期間內容指令碼的生命週期

  • 在 Firefox 中:使用者導航離開後,內容指令碼仍然注入在網頁中。但是,視窗物件屬性會被銷燬。例如,如果內容指令碼設定了 window.prop1 = "prop",然後使用者導航離開並返回到該頁面,window.prop1 將變為 undefined。此問題在 Firefox bug 1525400 中跟蹤。

    要模擬 Chrome 的行為,請監聽 pageshowpagehide 事件。然後模擬內容指令碼的注入或銷燬。

  • 在 Chrome 中:當用戶導航離開網頁時,內容指令碼會被銷燬。如果使用者點選後退按鈕通過歷史記錄返回到該頁面,內容指令碼會再次注入到網頁中。

“每標籤頁”縮放行為

  • 在 Firefox 中:縮放級別在頁面載入和標籤頁內的導航中保持不變。
  • 在 Chrome 中:縮放更改在導航時重置;導航標籤頁始終以其每來源縮放因子載入頁面。

參見 tabs.ZoomSettingsScope

manifest.json 鍵

manifest.json 頁面包含一個描述 manifest.json 鍵的瀏覽器支援的表格。如果給定鍵的支援存在注意事項,表格中會用星號 "*" 表示。選擇星號會展開表格以顯示解釋注意事項的註釋。

這些表格是根據儲存在 GitHub 中的 JSON 檔案中的相容性資料生成的。

原生訊息傳遞

基於連線的訊息傳遞引數

在 Linux 和 Mac 上:Chrome 將一個引數傳遞給原生應用程式,該引數是啟動它的擴充套件程式的來源,形式為 chrome-extension://«extensionID/»(需要尾部斜槓)。這使得應用程式能夠識別擴充套件程式。

在 Windows 上:Chrome 傳遞兩個引數

  1. 擴充套件程式的來源
  2. 啟動應用程式的 Chrome 原生視窗的控制代碼

allowed_extensions

  • 在 Firefox 中:manifest 鍵名為 allowed_extensions
  • 在 Chrome 中:manifest 鍵名為 allowed_origins

應用程式 manifest 位置

  • 在 Chrome 中:應用程式 manifest 預期在不同的位置。請參閱 Chrome 文件中的原生訊息主機位置

應用程式永續性

  • 在 Firefox 中:當原生訊息連線關閉時,如果子程序沒有分離,Firefox 會終止它們。在 Windows 上,瀏覽器將原生應用程式的程序放入 作業物件 並終止該作業。假設原生應用程式啟動了其他程序並希望它們在原生應用程式終止後仍然保持開啟。在這種情況下,原生應用程式必須使用 CreateProcess 而不是 ShellExecute,並使用 CREATE_BREAKAWAY_FROM_JOB 標誌啟動附加程序。

資料克隆演算法

一些擴充套件 API 允許擴充套件程式將資料從擴充套件程式的一部分發送到另一部分,例如 runtime.sendMessage()tabs.sendMessage()runtime.onMessageruntime.portpostMessage() 方法和 tabs.executeScript()

結構化克隆演算法支援比 JSON 序列化演算法更多的型別。一個值得注意的例外是帶有 toJSON 方法的 (DOM) 物件。DOM 物件預設不可克隆也不可 JSON 序列化,但透過 toJSON() 方法,它們可以 JSON 序列化(但仍無法使用結構化克隆演算法進行克隆)。不可結構化克隆但可 JSON 序列化的物件示例包括 URLPerformanceEntry 的例項。

依賴 JSON 序列化演算法的 toJSON() 方法的擴充套件程式可以使用 JSON.stringify() 後跟 JSON.parse() 來確保訊息可以交換,因為解析後的 JSON 值總是可以結構化克隆的。