HTML 規格移除 MessagePort 的 close 事件,GC 語意退回舊版
WHATWG HTML Standard commit 5facb53 · 2026-10-02
HTML 規格把 MessagePort 的 close 事件與 onclose 屬性整個拿掉,等於撤銷 2024 年加入、但沒有任何瀏覽器出貨的「對端關閉通知」。這個 commit 是 Nicolò Ribaudo 在 2026-10-02 提交的 revert,標題為 Remove MessagePort's close event,同時關閉 issue #10201 與 #12797。
連帶被撤銷的還有一套新的垃圾回收(GC)規則,規格文字回到舊版:port 不容易被回收,也不再有「對端被回收就自動斷開」的行為。
原本的問題
兩個互相 entangle 的 MessagePort 中,一端消失時,另一端沒有可靠又即時的方法得知。這是 issue #1766 的核心:持有 port 的程式難以及時釋放與它綁定的資源。
2024-01-12 的commit cc2634f(Add a close event to MessagePort)加入了解法:port A 關閉時,在 port B 上觸發 close。規格說明還指出,即使沒呼叫 close(),只要 Document 被銷毀或 port 被 GC,事件也會觸發。
這個設計很快碰到安全疑慮。issue #10201(2024-03-14 建立)寫明,Chrome 因安全考量不會照規格實作:事件等於把「對端持有者發生過 GC」這個訊號寫進標準,網頁可以藉此觀察另一端的 GC 時機。
核心改動
這次 commit 的 diff 為 source 一個檔案、27 行新增、65 行刪除。刪掉的內容有三塊:
- IDL 中的
attribute EventHandler onclose,以及事件處理器表格裡的對應列。 - disentangle 流程最後的 fire an event named close at otherPort 這一步,與說明三種觸發情境的 note。
- 銷毀
Document時「對該文件擁有的每個 port 執行 disentangle」的步驟。
- attribute EventHandler onclose;
- Fire an event named close at otherPort.
- For each port in ports, disentangle port.GC 規則則換回舊寫法:entangled 的 port,user agent 必須視為「對端 port 對它有強參照」,或「它的 relevant global object 對它有強參照」,兩者擇一。新版取消了「被 GC 就 disentangle」與「有 message listener 才保持強參照」的條件式寫法。
| 加入 close 事件後(2024) | 本次 revert 後 | |
|---|---|---|
| 對端關閉通知 | 在另一端 port 觸發 close | 無,onclose 不存在 |
| Document 銷毀 | 明確 disentangle 該文件的 port | 不處理,靠對端自行發現 |
| port 被 GC | 被 GC 即 disentangle | entangled 時須維持強參照 |
影響範圍
commit message 說明,沒有任何瀏覽器出貨過 close 事件,所以線上行為不會因此改變;真正受影響的是照著規格文字寫程式或實作的人。
- 依賴
port.onclose或addEventListener('close', …)偵測對端結束的程式碼:本來就無法在主流瀏覽器運作,現在規格也不再承諾,不要把它當作清理資源的唯一依據。 - 做 broadcast 的程式(把多個
MessagePort放進陣列逐一送訊息):規格新增一節非規範性的「Broadcasting to many ports」,指出陣列會讓對端已消失的 port 無法被回收。 - 瀏覽器引擎的實作者:GC 規則回到上述兩選一的強參照寫法,不必再實作由 GC 觸發的 disentangle。
新增的那一節建議的做法是自訂簡單的存活確認協定:對端要回覆自己還在,逾時未回覆就視為消失,主動 close() 該 port,讓它可被回收。
// 規格建議的方向(示意)
// 定期 ping,逾時未收到 ack 就關閉
if (now - lastAck > timeout) port.close();這段程式只是依規格文字描述的協定所寫的示意,規格並未給出逾時數值。
為何選擇退回
commit message 提到各瀏覽器的 port GC 語意本來就不一致:Chromium 只在 port 已 start 時才讓它不可回收,Firefox 不回收,WebKit 只在有 message listener 時才不可回收。作者表示這個 revert 回到的舊語意,他認為與 Firefox 的行為相符,也是 #10201 討論的走向。
message 也列出日後若要重新提出的相關討論:#1766、#9933、#10201、#12797、#12957。對應的測試在 web-platform-tests 的 #63174;至於該測試的內容,commit 本身未說明。