前端前線 2026 年 10 月 5 日

2026-10-05 — 字串版 setTimeout 的 Trusted Types 違規改為同步丟例外

primary=https://github.com/whatwg/html/commit/6992cb519a02db34f653ddd54c50fad2565285f5

HTML 規格修正:字串版 setTimeout 的 Trusted Types 檢查改為同步丟例外

WHATWG HTML Standard commit 6992cb519a02 · 2026-09-26

當 setTimeout 或 setInterval 收到字串、而 Trusted Types 判定違規時,HTML 規格原本把檢查放在計時器到期後的 task 裡,現在改為在呼叫當下就同步丟出例外。規格文字因此追上了瀏覽器的實際行為,而不是反過來要求瀏覽器改。

這是 WHATWG HTML 的 commit 6992cb519a02db34f653ddd54c50fad2565285f5,作者 Shannon Booth,日期為 2026-09-26,只動到 source 一個檔案(21 行新增、21 行刪除)。

原本的問題

Trusted Types 要求危險的字串 sink(例如把字串當成程式碼執行)必須收到 TrustedScript,否則視違規處理。字串版的 setTimeout("doSomething()", 1000) 就是這種 sink,因為字串最終會被當成腳本執行。

舊版規格的 timer initialization steps 把「取得符合 Trusted Types 的字串」這一步,放在計時器 task 內部執行。commit message 說得很直接:該步驟在 task 裡取得字串,同時又「可能丟出例外」。照規格字面讀,違規會在延遲之後、於 task 中才浮現,呼叫端的 try/catch 接不到。

這和實作不一致。commit message 指出,所有實作看起來都已經收斂到同步丟例外,規格才是落後的一方。

核心改動

改動是把一段步驟從 task 內搬到演算法最前面。轉換發生在 task 建立之前,所以例外會直接從 setTimeout 呼叫處冒出來。搬移後的條件是:handler 不是 Function,而且沒有給 previousId。

// 規格行為對照(概念示意,非規格原文)
// 舊:違規在 task 內才被檢查
setTimeout(untrustedString, 0);   // 呼叫端看不到例外

// 新:違規在呼叫當下同步丟出
try {
  setTimeout(untrustedString, 0);
} catch (e) {
  // 這裡接得到 Trusted Types 違規
}

被搬動的步驟內容沒有變:用 global 是不是 Window 決定 globalName(Window 或 WorkerGlobalScope),用 repeat 決定 methodName(setInterval 或 setTimeout),兩者以空格串成 sink,再以 TrustedScript 與 "script" 呼叫 get trusted type compliant string。也就是 sink 名稱仍是 Window setTimeout 這類字串,違規報告看起來一樣。

另外,task 內原本那段已被刪掉,只剩下 Assert:此時 handler 必為字串。因為轉換已提前完成,task 裡不再需要處理失敗。

前後對照

項目舊規格新規格
檢查時機計時器 task 內task 建立之前
違規的呈現延遲後在 task 中出現setTimeout 呼叫處同步丟出
try/catch 能否接到規格字面上不能可以
與瀏覽器實作不一致一致(依 commit message)

影響範圍

commit message 說明實作已經是同步丟例外,所以對現有瀏覽器的行為多半沒有新的變化,主要影響的是規格讀者與測試。以下是需要留意的對象:

  • 瀏覽器引擎與 web-platform-tests 維護者:規格現在明確要求同步行為,與既有實作對齊,之前依舊規格寫的測試或註解需要對照。
  • 啟用 Trusted Types 的網站:若有程式用 try/catch 包住字串版 setTimeout 來攔截違規,這是規格明文支持的寫法;若原本以為違規只會出現在延遲之後的全域錯誤,應檢查錯誤處理是否真的走到同步路徑。
  • 仍在用字串版計時器的舊程式:最穩妥的做法仍是改成傳 Function,這種情況根本不會經過 Trusted Types 檢查,因為條件只針對非 Function 的 handler。

另外,previousId 的條件值得注意:setInterval 重複觸發時會帶 previousId,所以只有第一次排程才做轉換,之後的重複排程不會再檢查一次。

commit 沒有附帶新的 IDL 或 API 變更,也沒有說明對應的 issue 編號或測試異動,這些資訊在 commit 內文中未提供。

原始來源:WHATWG HTML commit 6992cb519a02


End of article
0
Would love your thoughts, please comment.x
()
x