Skip to main content
Native messaging 讓擴充功能與使用者機器上的應用程式交換 JSON 訊息。瀏覽器會把應用程式當成子行程啟動,並透過 stdin 與 stdout 傳遞訊息。把它用在擴充功能平台做不到的工作:讀取本機檔案、與硬體溝通,或呼叫密碼管理器。 三個部分必須彼此一致:
  1. 擴充功能宣告 nativeMessaging 權限。
  2. 一個 host manifest,也就是向作業系統註冊的小型 JSON 檔案,指定應用程式與可以呼叫它的擴充功能。
  3. 應用程式實作以長度為前綴的 stdio 協定。

宣告權限

nativeMessaging 加入 manifest.json 的 permissions:
Extension.js 會把這個權限原封不動寫進建置後的 manifest。除此之外沒有任何建置階段的處理:host 本身永遠不會被打包。

撰寫 host manifest

host manifest 告訴瀏覽器應用程式的位置,以及誰可以啟動它:
  • name 是擴充功能傳給 connectNative() 的識別碼。使用小寫字母、數字、底線與句點。
  • path 在 macOS 與 Linux 上必須是絕對路徑。在 Windows 上可以相對於 manifest,而且必須指向可執行檔(Node.js 腳本要用 .bat 包裝器)。
  • type 永遠是 stdio
  • allowed_origins 列出可以呼叫這個 host 的擴充功能 ID。開啟開發人員模式後,在 chrome://extensions 讀取你的 ID。

安裝位置

Chromium 系瀏覽器會在各作業系統的固定位置尋找 manifest,檔名以 host 命名(com.example.ping.json): Chromium(開源版本)在 macOS 上使用 Chromium 而不是 Google/Chrome,在 Linux 上使用 ~/.config/chromium/NativeMessagingHosts//etc/chromium/native-messaging-hosts/。macOS 與 Linux 上的每位使用者目錄位於瀏覽器的使用者資料目錄內,這在 extension dev 期間很重要(見下文)。

最小的 Node.js host

每則訊息由一個 32 位元無號整數長度(採用原生位元組序,所有支援平台上都是 little-endian)開頭,後面接著該長度的 UTF-8 JSON 位元組。兩個方向都採用相同的封框方式。這個 host 會把每則訊息原樣回傳:
ping-host.js
在 macOS 與 Linux 上,把 path 指向一個可執行的包裝腳本,並讓它可執行:
ping-host
永遠不要把日誌寫到 stdout:瀏覽器會把該串流上的所有內容都當成訊息框解析。改成記錄到 stderr 或檔案。

從 background script 連線

需要持續對話時,使用 port。瀏覽器會在 connectNative() 時啟動 host 行程,並在 port 中斷連線時停止它:
background.js
只需要一次請求與回應時,sendNativeMessage() 每次呼叫都會啟動一個新的 host 行程:
從 host 傳到擴充功能的訊息上限是 1 MB。從擴充功能傳到 host 的訊息上限是 4 GB。重新載入擴充功能會中斷所有開啟中的 port,因此若連線必須存活,請在啟動路徑中重新連線。

Firefox 的差異

Firefox 使用相同的權限、相同的 API(browser.runtime.connectNative)與相同的 stdio 協定。差異在註冊端:
  • 你的擴充功能需要透過 browser_specific_settings.gecko.id 指定明確的 ID。使用 firefox: manifest 前綴,讓這個鍵只進入 Firefox 建置。
  • host manifest 以 allowed_extensions 取代 allowed_origins,列出該 ID。
Firefox 在自己的位置尋找,與 Firefox 設定檔無關:

extension dev 期間的 native messaging

host 是向作業系統註冊的,不會與你的程式碼一起打包。extension dev 不會複製也不會註冊任何東西,因此在開發階段能運作的 host,在商店安裝後行為也相同。 在 Chromium 上有一個設定檔細節需要注意。預設情況下,extension dev 會透過 --user-data-dir 以全新的受管理設定檔啟動瀏覽器。Chromium 會在使用中的使用者資料目錄內解析每位使用者的 host manifest,因此你為日常 Chrome 安裝的 manifest,對這個受管理設定檔是不可見的。從下列設置中擇一:
  • 以全系統方式安裝 host manifest(在 Windows 上則安裝到登錄檔)。這些位置不依賴設定檔。
  • 改為使用你真實的瀏覽器設定檔執行:
Firefox 與 Windows 的查找以作業系統使用者為單位,而不是設定檔,因此受管理的開發設定檔不需要額外步驟就能找到它們。

最佳實務

  • 驗證每一則訊息:host 以使用者的完整作業系統權限執行,因此要把擴充功能的輸入視為不可信任,反之亦然。
  • 保持 stdout 乾淨:host 裡一個多餘的 console.log 就會破壞訊息框串流。
  • 處理中斷連線:在 onDisconnect 中檢查 chrome.runtime.lastError,以區分 host 不存在與 host 當機。
  • 重複呼叫時優先使用 portsendNativeMessage() 每則訊息都要付出一次行程啟動的成本。

後續步驟