這是什麼意思?
從 Manifest V2 轉換至 Manifest V3 時,會發生重大變更。在資訊清單 v2 中,擴充功能會位於背景頁面。背景頁面會管理擴充功能和網頁之間的通訊。資訊清單 V3 則改用服務工作站。
本文將深入探討測試擴充功能服務 worker 的問題。具體來說,我們將探討如何確保服務工作程式遭到暫停時,產品仍能正常運作。
我們是誰?
eyeo 致力於為使用者、瀏覽器、廣告客戶和發布商提供平衡且可持續的線上價值交換機制。全球有超過 3 億名廣告篩選使用者允許顯示「可接受的廣告」,這是由 Google 獨立制定的廣告標準,用於判斷廣告是否符合可接受且非侵入性的標準。
我們的擴充功能引擎團隊提供廣告過濾技術,支援市面上最受歡迎的廣告封鎖瀏覽器擴充功能,例如 AdBlock 和 Adblock Plus,全球使用者超過 1 億人。此外,我們也提供這項技術的開放原始碼程式庫,讓其他廣告過濾瀏覽器擴充功能也能使用。
什麼是服務工作者?
擴充功能服務工作者是瀏覽器擴充功能的中央事件處理常式。這些程式會在背景中獨立執行。大致上來說,這沒什麼問題。我們可以在新的服務工作者中,執行背景頁面所需的大部分操作。不過,與背景頁面相比,這類頁面有幾項變更:
- 服務工作者會在未使用時終止。這需要我們保留應用程式狀態,而不是依賴全域變數。這表示系統中的任何進入點都必須在系統初始化前準備好才能呼叫。
- 必須先附加事件監聽器,才能等待任何非同步回呼。已暫停的服務工作程式仍可接收已訂閱的事件。如果事件的事件監聽器未在事件迴圈的第一個迴轉中註冊,當事件喚醒服務工作者時,事件監聽器就不會收到事件。
- 閒置終止作業可在計時器完成前中斷計時器。
服務工作者何時會遭到停權?
在 Chrome 119 中,我們發現服務工作程式已遭到暫停:
- 30 秒內未收到事件或呼叫擴充功能 API 後。
- 如果開發人員工具已開啟,或您使用的是 ChromeDriver 測試程式庫,則永遠不會發生這種情況 (請參閱功能要求)。
- 在 chrome://serviceworker-internals 中按一下「Stop」。
如需最新資訊,請參閱「Service Worker 生命週期」。
為什麼測試這項功能會造成問題?
理想情況下,官方指南應提供「如何以有效率的方式測試服務 worker」或實際測試範例。在測試服務工作者時,我們遇到了幾項挑戰:
- 我們在測試擴充功能中使用了狀態。服務工作程停止時,我們會失去其狀態和註冊事件。我們如何在測試流程中保留資料?
- 如果服務工作站可隨時暫停,我們就需要測試所有功能在遭到中斷時是否能正常運作。
- 即使我們在測試中導入隨機暫停服務工作者的機制,瀏覽器中也沒有可輕鬆暫停服務工作者的 API。我們已要求 W3C 團隊加入這項功能,但這項討論仍在進行中。
測試服務工作程式暫停
我們嘗試了幾種方法,在測試期間觸發服務工作程暫停:
做法 | 這項做法的問題 |
等待任意時間 (例如 30 秒) | 這會導致測試速度變慢且不可靠,尤其是在執行多項測試時。使用 WebDriver 時無法運作,因為 WebDriver 會使用 Chrome 的開發人員工具 API,且開發人員工具開啟時不會暫停服務工作者。即使我們可以略過這項檢查,仍必須檢查服務工作者是否已遭到停用,而我們沒有辦法執行這項檢查。 |
在服務工作者中執行無限迴圈 | 根據規格說明,這可能會導致終止,具體取決於瀏覽器如何實作這項功能。在這種情況下,Chrome 不會終止 service worker,因此我們無法測試 service worker 遭到暫停的情況。 |
在服務工作者中加入訊息,以便檢查是否已暫停 | 傳送訊息會喚醒服務工作者。這可用於檢查服務工作站是否處於休眠狀態,但對於需要在暫停服務工作站後立即進行檢查的測試,這會導致測試結果中斷。 |
使用 chrome.processes.terminate() 終止服務工作者程序 | 擴充功能的服務工作者會與擴充功能的其他部分共用處理程序,因此使用 chrome.process.terminate() 或 Chrome 的處理程序管理員 GUI 終止這個處理程序時,不僅會終止服務工作者,也會終止任何擴充功能頁面。 |
我們最後進行了測試,讓 Selenium WebDriver 開啟 chrome://serviceworker-internals/,然後按一下服務工作程式「停止」按鈕,藉此檢查程式碼如何回應服務工作程式遭到暫停的情況。
這是目前最佳的做法,但並非理想做法,因為我們的 Mocha 測試 (在擴充功能頁面上執行) 無法自行執行這項操作,因此需要回傳至 WebDriver 節點程式。也就是說,這些測試無法只使用擴充功能執行,必須使用 Selenium WebDriver觸發。
下圖說明我們如何透過不同流程與瀏覽器 API 進行通訊,以及新增「暫停服務工作者」機制對此有何影響。
在暫停服務工作者 (藍色) 的新流程中,我們已新增 Selenium WebDriver,透過使用者介面「點選」暫停,進而觸發瀏覽器 API 中的動作。
值得一提的是,有一個 Chrome 錯誤,如果使用 Selenium WebDriver 執行這項操作,就會導致服務工作者無法重新啟動。這個問題已在 Chrome 116 中修正,而且幸運的是,也有解決方法:將 Chrome 設為在每個分頁上自動開啟開發人員工具,即可讓服務工作者正確啟動。
這是我們在測試時採用的方法,雖然這不是理想做法,因為點選按鈕可能不是穩定的 API,而且開啟開發人員工具 (適用於舊版瀏覽器) 似乎會影響效能。
我們如何涵蓋整個功能?模糊測試
有了測試暫停功能的機制後,我們必須決定如何將其納入自動化測試套件。我們在以下環境中執行標準測試:在每次與背景頁面互動前,WebDriver 會點選 chrome://serviceworker-internals/ 頁面上的「Stop」,暫停服務工作者。
我們會執行大部分測試,但並非全部,因為暫停機制並未完全穩定,有時會導致不穩定。此外,在模糊模式下執行所有測試套件需要花費大量時間。因此,我們並未涵蓋所有「類似」的案例,而是選擇最關鍵的路徑,在模糊模式中進行測試。值得一提的是,在「模糊」模式下執行功能測試,意味著我們必須增加測試的逾時時間,因為暫停及重新啟動服務 worker 需要額外時間。
這些測試可做為粗略的第一輪測試,用來找出程式碼失敗的許多位置,但不一定能找出服務暫停可能導致問題的所有細微方式。
我們在內部將這類測試稱為「模糊測試」。傳統上,模糊測試是指向程式丟擲無效的輸入內容,並確保程式能以合理方式回應,或至少不會當機。在我們的案例中,「無效輸入」是指服務工作者隨時可能遭到停權,而我們預期的「合理行為」則是廣告篩選功能必須照常運作。這並非無效輸入,因為這是 Manifest V3 的預期行為,但在 Manifest V2 中會無效,因此這似乎是合理的用語。
摘要
服務工作是 Manifest V3 中 (除了 declarativeNetRequest 規則) 最大的變更之一。遷移至 Manifest V3 可能需要在瀏覽器擴充功能中進行許多程式碼變更,並採用新的測試方法。開發人員也必須為含有持續性狀態的擴充功能做好準備,以便妥善處理服務工作程意外暫停的情況。
很遺憾,目前沒有任何 API 能以簡單的方式處理符合我們用途的暫停作業。由於我們想在早期測試擴充功能的程式碼庫在遇到暫停機制時的穩定性,因此必須繞過這個問題。其他面臨類似問題的擴充功能開發人員可以使用這個解決方法,雖然在開發和維護階段會耗費時間,但值得這麼做,因為這樣就能確保擴充功能能在服務工作者經常遭到暫停的環境中順利運作。
雖然基本支援測試服務工作程暫停功能,但我們希望未來能有更好的平台支援測試服務工作程,因為這麼做可以大幅減少測試執行時間和維護工作。