refactor: decouple from site-specific CDN references #5

Merged
kfj001 merged 11 commits from feature/no_mention_of_pornhub into master 2026-08-30 17:07:52 -07:00
16 changed files with 1438 additions and 540 deletions

No files matched your search

-9
View File
@@ -1,9 +0,0 @@
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
{
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
"permissions": {
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
"allow": [
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
"Bash(npm run *)",
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
"Bash(node --check /home/kyle/Documents/goon_plugin/src/viewer/index.js)",
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
"Bash(open -a \"Microsoft Edge\" --args --remote-debugging-port=9222 --profile-directory=Default)"
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
]
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
}
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
}
Review

[SHOULD-FIX] F3
A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored.
Suggested fix: Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.

[SHOULD-FIX] F3 A tool-local settings file is committed to the shared repo and now carries absolute local paths to the author's machine: 'Bash(node /Users/kyle/.claude/jobs/1462df8d/tmp/transform2.mjs)' and 'Bash(open -a "Microsoft Edge" ...)'. These are machine-specific and should not live in the shared codebase; .claude/settings.local.json is typically user-scoped and gitignored. **Suggested fix:** Drop .claude/settings.local.json from the PR (it is unrelated to the refactor) and/or add it to .gitignore so per-machine tool settings never get committed.
+2 -1
View File
@@ -2,4 +2,5 @@ node_modules/
dist/ dist/
.vscode-insiders/ .vscode-insiders/
/icons /icons
/icons/* /icons/*
.claude/settings.local.json
+2 -2
View File
@@ -44,8 +44,8 @@ describe('Config: build integrity', () => {
if (codeOnly.includes('textContent')) { if (codeOnly.includes('textContent')) {
throw new Error( throw new Error(
'content.js uses inline script injection (textContent). ' + 'content.js uses inline script injection (textContent). ' +
'Sites like Pornhub block inline scripts via CSP. Use script.src instead.' 'Some sites block inline scripts via CSP. Use script.src instead.'
); );
} }
if (!codeOnly.includes('script.src') || !codeOnly.includes('chrome.runtime.getURL')) { if (!codeOnly.includes('script.src') || !codeOnly.includes('chrome.runtime.getURL')) {
+31
View File
@@ -185,4 +185,35 @@ describe('Data flow: popup -> storage -> viewer', () => {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
throw new Error('Entry with invalid mediaType should be rejected'); throw new Error('Entry with invalid mediaType should be rejected');
} }
}); });
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
it('an HLS entry carrying a scan-time referer passes schema + round-trips', () => {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// The referer is bound to the entry at scan time so it survives the
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// watch-later flow (the source tab may be long closed). Because the schema
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// sets additionalProperties:false, a `referer` the store carries must be an
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// explicit property or storage-layer validation would reject the entry.
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
const scrapedEntry = {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
url: 'https://pageA.example.com/watch',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
src: 'https://cdn.example.com/hls/live/master.m3u8',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
hlsManifest: 'https://cdn.example.com/hls/live/master.m3u8',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
mediaType: 'hls',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
poster: 'https://pageA.example.com/thumb.jpg',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
tab: 'Referer-protected stream',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
referer: 'https://pageA.example.com/watch',
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
};
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
const valid = validate(scrapedEntry);
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
if (!valid) {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
throw new Error(`Entry with referer fails schema: ${JSON.stringify(validate.errors)}`);
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
}
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// The referer must survive the storage round-trip intact so the viewer can
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
// send it to the proxy when the user opens a previously-scanned entry.
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
const roundTripped = JSON.parse(JSON.stringify(scrapedEntry));
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
if (roundTripped.referer !== 'https://pageA.example.com/watch') {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
throw new Error('referer lost during round-trip');
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
}
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
if (!validate(roundTripped)) {
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
throw new Error('Round-tripped entry with referer fails schema');
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
}
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
});
Review

[FYI] F5
Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency.
Suggested fix: Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.

[FYI] F5 Consistent with the PR's goal ('no references in src/'), src/ is clean, but the NEW test files still reference the very providers being removed — phncdn.com in data_flow.test.mjs and referer_proxy.test.mjs, and thisvid.com in hls_qa.test.mjs / simple_video.test.mjs. These read as real-world fixtures, not logic coupling, so they do not reintroduce the coupling the PR removes; flagging for awareness / consistency. **Suggested fix:** Optional: swap the real provider hostnames in test fixtures for neutral 'example.com' hosts to keep the spirit of the refactor consistent across the test suite.
}); });
+115 -21
View File
@@ -14,8 +14,8 @@ const viewerSource = fs.readFileSync(
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
); );
const interceptorSource = fs.readFileSync( const interceptorSource = fs.readFileSync(
path.join(__dirname, '../src/inject/interceptors.js'), path.join(__dirname, '../src/inject/interceptors.js'),
'utf-8' 'utf-8'
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
); );
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
/* ========================================================================== */ /* ========================================================================== */
/* Helpers */ /* Helpers */
@@ -940,8 +940,8 @@ describe('HLS QA: Configuration and resource management', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
it('should vendor HLS bundle locally (not use CDN — avoids CSP blocks)', () => { it('should vendor HLS bundle locally (not use CDN — avoids CSP blocks)', () => {
// Viewer loads hls.js from extension origin via chrome.runtime.getURL('hls.min.js') // Viewer loads hls.js from extension origin via chrome.runtime.getURL('hls.min.js')
// instead of a CDN — sites like Pornhub restrict script-src to 'self', blocking // instead of a CDN — sites that restrict script-src to 'self' block
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// cross-origin <script src> injection from CDN URLs. // cross-origin <script src> injection from CDN URLs.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!viewerSource.includes("chrome.runtime.getURL('hlds.js')")) { if (!viewerSource.includes("chrome.runtime.getURL('hlds.js')")) {
throw new Error('viewer.js should load hlds.js from extension origin, not CDN'); throw new Error('viewer.js should load hlds.js from extension origin, not CDN');
} }
@@ -1027,21 +1027,48 @@ describe('HLS QA: iframe and multi-frame scenarios', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
} }
}); });
it('should scrape video elements from all frames', () => { it('should scrape video elements from all frames', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const indexSource = fs.readFileSync( const indexSource = fs.readFileSync(
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
path.join(__dirname, '../src/viewer/index.js'), path.join(__dirname, '../src/viewer/index.js'),
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'utf-8' 'utf-8'
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
); );
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// Verify scrapeTab runs in all frames // Verify scrapeTab runs in all frames
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const scrapeMatch = indexSource.match(/scrapeTab[\s\S]*?executeScript[\s\S]*?\}/); const scrapeMatch = indexSource.match(/scrapeTab[\s\S]*?executeScript[\s\S]*?\}/);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!scrapeMatch) { if (!scrapeMatch) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error('scrapeTab function not found'); throw new Error('scrapeTab function not found');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
} }
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!scrapeMatch[0].includes('allFrames: true')) { if (!scrapeMatch[0].includes('allFrames: true')) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error('scrapeTab must use allFrames: true'); throw new Error('scrapeTab must use allFrames: true');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
} }
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}); });
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
it('deep probe + scrape must run in the MAIN world (see page globals)', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// The deep probe and scraper read page globals (window.Hls, player
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// instances, video._goon_hls_manifest) and window.__goon_plugin_data,
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// which the main-world interceptors write. The isolated world cannot see
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// these, so a missing `world: 'MAIN'` makes every main-world strategy find
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// 0 manifests — the "detects nothing on pages with just <video> elements"
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// regression. Guard the exact executeScript calls that run each function.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const indexSource = fs.readFileSync(
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
path.join(__dirname, '../src/viewer/index.js'),
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'utf-8'
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// Capture each call's target + func block (world: 'MAIN' lives on the
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// target: line, which precedes func:).
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const probeMatch = indexSource.match(/executeScript\(\{[\s\S]*?func: m3u8DeepProbeFn/);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!probeMatch) throw new Error('deep probe executeScript call not found');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!probeMatch[0].includes("world: 'MAIN'")) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error('deep probe (m3u8DeepProbeFn) must use world: "MAIN" to see page globals');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const scrapeMatch = indexSource.match(/executeScript\(\{[\s\S]*?func: scrapeFn/);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!scrapeMatch) throw new Error('scrape executeScript call not found');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!scrapeMatch[0].includes("world: 'MAIN'")) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error('scrape (scrapeFn) must use world: "MAIN" to see page globals');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
it('should inject interceptors into all frames (content_scripts.all_frames)', () => { it('should inject interceptors into all frames (content_scripts.all_frames)', () => {
const manifestSource = fs.readFileSync( const manifestSource = fs.readFileSync(
@@ -1211,6 +1238,73 @@ describe('HLS QA: Edge cases', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (intercepted.length !== 1) { if (intercepted.length !== 1) {
throw new Error(`Expected 1 intercept for /manifest URL, got ${intercepted.length}`); throw new Error(`Expected 1 intercept for /manifest URL, got ${intercepted.length}`);
} }
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}); });
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}); });
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
/* ========================================================================== */
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
/* Test: deep probe must not report route-template / nav "manifests" */
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
/* ========================================================================== */
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
describe('HLS QA: route-template / nav URL rejection', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const probeMatch = indexSource.match(/function m3u8DeepProbeFn\(\)[\s\S]*?^}/m);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!probeMatch) throw new Error('Could not find m3u8DeepProbeFn in index.js');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const probeFnSource = probeMatch[0];
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
function runProbeWin(url, globals) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const dom = new JSDOM(
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'<!DOCTYPE html><html><body><video></video></body></html>',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
{ url, runScripts: 'dangerously' });
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const win = dom.window;
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
for (const [k, v] of Object.entries(globals)) win[k] = v;
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const result = win.eval(`(${probeFnSource})();`);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!Array.isArray(result)) throw new Error('m3u8DeepProbeFn did not return an array');
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
return result;
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// Reproduces a provider whose pageContext.langUrls holds server-side route
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// TEMPLATES / nav links (e.g. /playlists/, /playlist/%1%/video/%2%/) that the
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// old /playlist|/manifest substring check misclassified as "manifests".
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const TEMPLATE_URLS = [
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/liked/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/popular/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/commented/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/updated/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/largest/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlist/%1%/video/%2%/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/playlists/?category=%DIR%',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/members/%ID%/playlists/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
'https://example.com/my/playlists/%ID%/',
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
];
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
it('route templates / nav links are NOT reported as manifests', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const result = runProbeWin('https://example.com/videos/redneck/', {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
pageContext: { langUrls: TEMPLATE_URLS },
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
kvsplayer: { config: { langUrls: TEMPLATE_URLS } },
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
__goon_plugin_data: { manifests: [], sources: [] },
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const urls = result.map(m => m.url);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const leaked = TEMPLATE_URLS.filter(u => urls.includes(u));
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (leaked.length > 0) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error(`template/nav URLs leaked as manifests: ${leaked.join(', ')}`);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
it('a real .m3u8 with a %2B token is still found (no over-rejection)', () => {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// %2B is a valid percent-encoded token, not a placeholder — it must survive.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const real = 'https://cdn.example.com/live/master.m3u8?h=a%2Bb%2Fc&e=1234';
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const result = runProbeWin('https://example.com/videos/redneck/', {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
hls: { url: real },
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
pageContext: { langUrls: TEMPLATE_URLS },
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (!result.some(m => m.url === real)) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error(`real manifest with %2B token was dropped: ${real} — got: ${result.map(m => m.url).join(', ')}`);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
// And the templates still must not leak in the same run.
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
const leaked = TEMPLATE_URLS.filter(u => result.some(m => m.url === u));
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
if (leaked.length > 0) {
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
throw new Error(`templates leaked alongside real manifest: ${leaked.join(', ')}`);
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
}
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
});
Review

[BLOCKING] F1
This new suite hangs the entire npm test runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does for (const key of Object.keys(window)) { walk(window[key], ...) } and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. walk (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because tests/run.mjs loads hls_qa.test.mjs, npm test never completes -> the repo's canonical test command is broken.
Suggested fix: Give the walker a visited set (WeakSet) so each object is walked at most once: const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.

[BLOCKING] F1 This new suite hangs the entire `npm test` runner (>30s at 100% CPU, exit 124; reproduced in isolation). runProbeWin evaluates m3u8DeepProbeFn in a live JSDOM window; Strategy H (src/viewer/index.js:484) does `for (const key of Object.keys(window)) { walk(window[key], ...) }` and window/_parent/_top/frames/_globalObject/_globalProxy are circular Proxy objects. `walk` (index.js:161) has a depth cap of 5 but NO visited-set / cycle detection, so it recurses unboundedly over thousands of proxied window keys and never returns. Because __tests__/run.mjs loads hls_qa.test.mjs, `npm test` never completes -> the repo's canonical test command is broken. **Suggested fix:** Give the walker a visited set (WeakSet) so each object is walked at most once: `const seen = new WeakSet(); function walk(obj,...){ if (obj && typeof obj==='object'){ if(seen.has(obj)) return; seen.add(obj);} ... }`. Additionally cap Strategy H's window-key scan (window has hundreds of keys; only the handful of genuine player globals matter) — e.g. skip built-in global keys or cap the scan. Alternatively, scope the new test so it stubs only the window globals under test (pageContext, kvsplayer, __goon_plugin_data) instead of a full JSDOM window, avoiding the circular globals entirely.
+364
View File
@@ -0,0 +1,364 @@
/**
* referer_proxy.test.mjs — coverage for the background worker's per-CDN-host
* referer capture + DNR proxy-host learning, added in PR #5.
*
* The background worker is not itself a module, so the test transpiles it into
* one (wrapping + top-level-statement `async function` transpile) under a mocked
* `chrome` + `fetch`/`btoa` environment, then exercises the captured listeners
* and the message handler directly.
*
* Covers the three gaps robot flagged:
* (a) closing a tab must NOT wipe the host-keyed `goon_referers` global store,
* (b) two hosts proxied concurrently must each keep their OWN referer (no race
* that leaves host B's referer live for host A -> 412),
* (c) the new referer-capture / proxy-host-learning / onRemoved paths run.
*/
import { describe, it } from './runner.mjs';
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { dirname, join } from 'node:path';
import { tmpdir } from 'node:os';
const __dirname = dirname(fileURLToPath(import.meta.url));
const backgroundSource = readFileSync(join(__dirname, '../src/background/background.js'), 'utf8');
// Transpile the top-level-statement worker into an ES module. Wrapping in an
// async IIFE makes its top-level `async function` declarations + `await`-using
// nested functions legal inside a module.
function buildModule(code) {
return `(async () => {\n${code}\n})();\n`;
}
// A fresh mock of the chrome APIs the worker touches. `onBeforeRequest` and
// `tabs.onRemoved` capture their listener, and `tabs.get` serves the source
// tab's URL (the captured referer); `declarativeNetRequest.updateSessionRules`
// records the live rules.
function makeChromeEnv() {
const storage = {};
const handlers = { onMessage: [] };
const tabUrls = {};
let sessionRules = {};
const webRequest = {
onBeforeRequest: { addListener: (cb) => { handlers.onBeforeRequest = cb; } },
};
const chrome = {
storage: {
local: {
get: async (keys) => {
const out = {};
const arr = Array.isArray(keys) ? keys : (keys === undefined ? Object.keys(storage) : [keys]);
for (const k of arr) if (k in storage) out[k] = storage[k];
return out;
},
set: async (data) => {
for (const [k, v] of Object.entries(data)) storage[k] = v;
},
remove: async (keys) => {
const arr = Array.isArray(keys) ? keys : [keys];
for (const k of arr) delete storage[k];
},
},
},
webRequest,
tabs: {
get: async (tabId) => tabUrls[tabId] ? { tabId, url: tabUrls[tabId] } : undefined,
onRemoved: { addListener: (cb) => { handlers.onRemoved = cb; } },
},
runtime: { onMessage: { addListener: (cb) => { handlers.onMessage.push(cb); } } },
declarativeNetRequest: {
updateSessionRules: async ({ removeRuleIds = [], addRules = [] } = {}) => {
const rm = new Set(removeRuleIds);
sessionRules = Object.fromEntries(
Object.entries(sessionRules).filter(([id]) => !rm.has(id))
);
for (const r of addRules) sessionRules[r.id] = r;
},
},
};
return { chrome, handlers, storage, tabUrls,
setTabUrl: (tabId, url) => { tabUrls[tabId] = url; },
getSessionRules: () => sessionRules };
}
// Load the worker into a fresh environment. `data:` URLs are cached by Node, so
// the module body (listener registration) would only run once; instead we write
// a uniquely-named temp file per load so each call registers into its own env.
let loadCounter = 0;
async function loadBackground(env) {
global.chrome = env.chrome;
const file = join(tmpdir(), `goon_bg_${process.pid}_${loadCounter++}.mjs`);
writeFileSync(file, buildModule(backgroundSource));
const module = await import(pathToFileURL(file).href);
await module.default;
return module;
}
// Drive a referer capture the way the worker now does it: the worker reads the
// source tab's URL (via chrome.tabs.get) as the provider referer, so we set the
// tab's URL and fire an onBeforeRequest for a video URL on that tab.
async function capture(env, videoUrl, referer) {
env.setTabUrl(11, referer);
await env.handlers.onBeforeRequest({ tabId: 11, url: videoUrl });
}
describe('PR#5: per-host referer capture + proxy race', () => {
// (a) Blocking fix: a host-keyed `goon_referers` entry must outlive the close
// of ANY tab — including a different tab.
it('closing a tab does not wipe goon_referers captured for another host', async () => {
const env = makeChromeEnv();
global.chrome = env.chrome;
await loadBackground(env);
// A live source tab captures the provider referer for its CDN host.
await capture(env, 'https://cdnA.example.com/hls/live/seg-1.ts', 'https://pageA.example.com/watch');
const rA = env.storage['goon_referers']?.['example.com'];
if (!rA) throw new Error('referer was not captured for cdnA');
if (rA.referer !== 'https://pageA.example.com/watch') {
throw new Error('captured referer wrong: ' + JSON.stringify(rA));
}
// A DIFFERENT tab closes. The per-tab cleanup must NOT remove goon_referers.
await env.handlers.onRemoved(22);
const after = env.storage['goon_referers'];
if (!after || !after['example.com']) {
throw new Error('closing tab 22 wiped goon_referers — host A referer lost');
}
if (after['example.com'].referer !== 'https://pageA.example.com/watch') {
throw new Error('closing tab 22 corrupted the host A referer: ' + JSON.stringify(after));
}
});
it('closing the capturing tab itself preserves the referer for later proxy use', async () => {
const env = makeChromeEnv();
global.chrome = env.chrome;
await loadBackground(env);
await capture(env, 'https://cdnB.example.com/video/x/master.m3u8', 'https://pageB.example.com/v');
// The very tab that captured the referer now closes.
await env.handlers.onRemoved(11);
const after = env.storage['goon_referers']?.['example.com'];
if (!after) throw new Error('goon_referers for the capturing host was wiped on its own tab close');
if (after.referer !== 'https://pageB.example.com/v') {
throw new Error('capturing-tab close corrupted referer: ' + JSON.stringify(after));
}
});
// (b) Should-fix + coverage: two hosts proxied concurrently must each keep
// their own referer. With a shared rule id + non-atomic install this regressed;
// per-host rule ids + the per-host lock keep them distinct.
it('concurrent proxy fetches for two distinct registrable domains keep distinct referer rules', async () => {
const env = makeChromeEnv();
let fetchCalls = 0;
global.chrome = env.chrome;
global.fetch = async (url) => {
fetchCalls++;
return {
ok: true,
status: 200,
headers: { get: () => 'application/vnd.apple.mpegurl' },
arrayBuffer: async () => new Uint8Array([0]).buffer,
};
};
await loadBackground(env);
// Two GOON_PROXY_FETCH messages for different CDN hosts, fired together.
const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1];
let respA, respB;
handler({ type: 'GOON_PROXY_FETCH', url: 'https://cdnA.example.com/hls/live/seg-1.ts', referer: 'https://pageA.example.com/watch' }, {}, (r) => { respA = r; });
handler({ type: 'GOON_PROXY_FETCH', url: 'https://cdnB.other.com/hls/live/seg-1.ts', referer: 'https://pageB.other.com/watch' }, {}, (r) => { respB = r; });
await new Promise(r => setTimeout(r, 30)); // let async sends + installs flush
if (!respA || !respA.ok) throw new Error('proxy A failed: ' + JSON.stringify(respA));
if (!respB || !respB.ok) throw new Error('proxy B failed: ' + JSON.stringify(respB));
if (fetchCalls < 2) throw new Error('expected both proxy fetches to run, got ' + fetchCalls);
// Each host must have its OWN installed DNR rule with its OWN referer —
// host A must NOT be served host B's referer (the 412 regression).
const rules = env.getSessionRules();
const ruleA = Object.values(rules).find(r => r.condition?.requestDomains?.includes('example.com'));
const ruleB = Object.values(rules).find(r => r.condition?.requestDomains?.includes('other.com'));
if (!ruleA) throw new Error('no DNR rule installed for cdna; rules=' + JSON.stringify(rules));
if (!ruleB) throw new Error('no DNR rule installed for cdnb; rules=' + JSON.stringify(rules));
if (ruleA.id === ruleB.id) {
throw new Error('cdnA and cdnB shared a DNR rule id -> concurrent race possible');
}
const refA = ruleA.action.requestHeaders?.find(h => h.header.toLowerCase() === 'referer')?.value;
const refB = ruleB.action.requestHeaders?.find(h => h.header.toLowerCase() === 'referer')?.value;
if (refA !== 'https://pageA.example.com/watch') {
throw new Error('cdnA served wrong referer: ' + refA);
}
if (refB !== 'https://pageB.other.com/watch') {
throw new Error('cdnB served wrong referer: ' + refB);
}
if (refA === refB) throw new Error('both hosts got the same referer — cross-host bleed');
});
it('repeated proxy for the same host is idempotent (one rule, one install)', async () => {
const env = makeChromeEnv();
let installs = 0;
env.chrome.declarativeNetRequest.updateSessionRules = async ({ addRules = [] } = {}) => {
for (const r of addRules) installs++;
};
global.chrome = env.chrome;
global.fetch = async () => ({
ok: true, status: 200,
headers: { get: () => 'application/vnd.apple.mpegurl' },
arrayBuffer: async () => new Uint8Array([0]).buffer,
});
await loadBackground(env);
const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1];
const run = async (n) => {
handler(
{ type: 'GOON_PROXY_FETCH', url: 'https://cdnA.example.com/hls/live/seg-1.ts', referer: 'https://pageA.example.com/watch' },
{},
() => {}
);
await new Promise(r => setTimeout(r, 5));
};
await run(1);
const installsAfterFirst = installs;
await run(2);
await run(3);
// Same host + same referer should not re-add the rule on repeat fetches.
if (installs !== installsAfterFirst) {
throw new Error(`expected idempotent install, but re-added rule ${installs - installsAfterFirst} extra time(s)`);
}
});
// (c) Domain generalization — the core 412 fix. A provider referer captured for
// ONE edge subdomain must cover a SIBLING subdomain: the DNR rule is scoped to
// the registrable domain and the store is domain-keyed, so a proxy for ev-h
// needs no per-subdomain capture.
it('a proxy for a sibling subdomain reuses the captured referer via the registrable domain', async () => {
const env = makeChromeEnv();
global.chrome = env.chrome;
global.fetch = async () => ({
ok: true,
status: 200,
headers: { get: () => 'application/vnd.apple.mpegurl' },
arrayBuffer: async () => new Uint8Array([0]).buffer,
});
await loadBackground(env);
// Capture the provider referer for hv-h.example.net (one edge subdomain).
await capture(env, 'https://hv-h.example.net/hls/live/seg-1.ts', 'https://provider.example.com/watch');
// Proxy a SIBLING subdomain ev-h.example.net with NO referer in the message:
// the SW must resolve it from the domain-keyed store.
const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1];
let resp;
handler({ type: 'GOON_PROXY_FETCH', url: 'https://ev-h.example.net/hls/live/seg-1.ts' }, {}, (r) => { resp = r; });
await new Promise(r => setTimeout(r, 20));
if (!resp || !resp.ok) throw new Error('sibling proxy failed: ' + JSON.stringify(resp));
const rules = env.getSessionRules();
const rule = Object.values(rules).find(r => r.condition?.requestDomains?.includes('example.net'));
if (!rule) throw new Error('no domain-scoped DNR rule for example.net; rules=' + JSON.stringify(rules));
const ref = rule.action.requestHeaders?.find(h => h.header.toLowerCase() === 'referer')?.value;
if (ref !== 'https://provider.example.com/watch') {
throw new Error('sibling edge served wrong referer: ' + ref);
}
// The rule is scoped to the registrable domain, NOT the exact subdomain.
if (rule.condition.requestDomains?.includes('ev-h.example.net')) {
throw new Error('rule scoped to exact subdomain instead of registrable domain');
}
});
// (d) With no captured referer and none in the message, install NO rule: the
// self-origin fallback (guaranteed-wrong for hotlink protection) is dropped.
it('no captured referer and no message referer installs no DNR rule', async () => {
const env = makeChromeEnv();
let fetchCalls = 0;
global.chrome = env.chrome;
global.fetch = async () => {
fetchCalls++;
return {
ok: true,
status: 200,
headers: { get: () => 'application/vnd.apple.mpegurl' },
arrayBuffer: async () => new Uint8Array([0]).buffer,
};
};
await loadBackground(env);
const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1];
let resp;
handler({ type: 'GOON_PROXY_FETCH', url: 'https://cdnA.example.com/hls/live/seg-1.ts' }, {}, (r) => { resp = r; });
await new Promise(r => setTimeout(r, 20));
if (!resp || !resp.ok) throw new Error('proxy fetch failed: ' + JSON.stringify(resp));
if (fetchCalls < 1) throw new Error('proxy fetch did not run');
const rules = env.getSessionRules();
if (Object.keys(rules).length !== 0) {
throw new Error('installed a rule with no referer (self-origin fallback not dropped): ' + JSON.stringify(rules));
}
});
// (e) GOON_GET_REFERERS (used by scan-time capture) resolves each manifest host
// via the domain-keyed store: a sibling subdomain gets the provider referer,
// and an uncaptured host is omitted.
it('GOON_GET_REFERERS resolves manifest hosts through the domain-keyed store', async () => {
const env = makeChromeEnv();
global.chrome = env.chrome;
await loadBackground(env);
// Capture for hv-h.example.net only.
await capture(env, 'https://hv-h.example.net/hls/live/seg-1.ts', 'https://provider.example.com/watch');
const handler = env.handlers.onMessage[env.handlers.onMessage.length - 1];
let resp;
handler(
{ type: 'GOON_GET_REFERERS', hosts: ['ev-h.example.net', 'hv-h.example.net', 'cdnA.example.com'] },
{},
(r) => { resp = r; });
await new Promise(r => setTimeout(r, 15));
if (!resp || !resp.ok) throw new Error('GOON_GET_REFERERS failed: ' + JSON.stringify(resp));
const referers = resp.referers || {};
if (referers['ev-h.example.net'] !== 'https://provider.example.com/watch') {
throw new Error('sibling subdomain did not resolve the captured referer: ' + JSON.stringify(referers));
}
if (referers['hv-h.example.net'] !== 'https://provider.example.com/watch') {
throw new Error('captured subdomain did not resolve its own referer: ' + JSON.stringify(referers));
}
if ('cdnA.example.com' in referers) {
throw new Error('uncaptured host should be omitted from referers: ' + JSON.stringify(referers));
}
});
// Regression for the live 412 bug: the worker captures the provider referer
// from the source tab's URL via chrome.tabs.get inside onBeforeRequest. The
// old path read request headers via onBeforeSendHeaders + extraInfoSpec, which
// is rejected by some Chromium builds and fails the whole service worker, so it
// was removed; this asserts the new tab-URL capture is wired up and actually
// stores a referer.
it('the onBeforeRequest capture reads the referer from the source tab URL', async () => {
const env = makeChromeEnv();
global.chrome = env.chrome;
await loadBackground(env);
const cap = env.handlers.onBeforeRequest;
if (!cap) throw new Error('onBeforeRequest listener was not registered');
// A video request for cdnA from a tab whose URL IS the provider referer.
env.setTabUrl(11, 'https://pageA.example.com/watch');
await cap({ tabId: 11, url: 'https://cdnA.example.com/hls/live/seg-1.ts' });
const rA = env.storage['goon_referers']?.['example.com'];
if (!rA) throw new Error('referer was not captured from the source tab URL');
if (rA.referer !== 'https://pageA.example.com/watch') {
throw new Error('captured referer wrong: ' + JSON.stringify(rA));
}
});
});
+2
View File
@@ -7,6 +7,8 @@ import './data_flow.test.mjs';
import './viewer_integration.test.mjs'; import './viewer_integration.test.mjs';
import './config_validation.test.mjs'; import './config_validation.test.mjs';
import './hls_qa.test.mjs'; import './hls_qa.test.mjs';
import './simple_video.test.mjs';
import './referer_proxy.test.mjs';
import { run } from './runner.mjs'; import { run } from './runner.mjs';
const result = await run(); const result = await run();
+131
View File
@@ -0,0 +1,131 @@
// Regression tests: simple <video> / direct-media detection.
//
// The feature branch broke detection of pages that expose a plain <video>
// (e.g. a site that serves a direct MP4 with a TRAILING SLASH and a
// ?rnd= query: .../14644093/14644093.mp4/?rnd=...). Two site-agnostic fixes
// are guarded here:
// 1) The URL predicate (isValidManifestUrl) must let concrete media signals
// (.mp4/.m3u8/.webm/... and /hls/) WIN over the placeholder/`%`/trailing-
// slash rejections, so a real direct-media URL is not mistaken for a
// route template.
// 2) scrapeTab must fall back to an ISOLATED-world scrape when the MAIN-world
// scrape yields nothing — the runtime cause of "detects nothing" on such pages
// is a MAIN-world + allFrames rejection on its ad-iframe-heavy page.
import { describe, it } from './runner.mjs';
import { JSDOM } from 'jsdom';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const indexSource = fs.readFileSync(
path.join(__dirname, '../src/viewer/index.js'),
'utf-8'
);
// Extract a `function <name>(...) {…}` by balanced-brace scan. Robust to the
// file's quirky/mixed indentation (a column-0 `}` heuristic would mis-truncate
// nested or oddly-indented bodies).
function extractFn(src, name) {
const re = new RegExp('function ' + name + '\\s*\\(');
const m = re.exec(src);
if (!m) throw new Error('not found: ' + name);
const start = m.index;
let i = src.indexOf('{', start), depth = 0;
for (; i < src.length; i++) {
const c = src[i];
if (c === '{') depth++;
else if (c === '}') { depth--; if (depth === 0) return src.slice(start, i + 1); }
}
throw new Error('unbalanced: ' + name);
}
// The direct-media URL, shaped exactly like the rejected case (a trailing slash +
// ?rnd= query). This is the concrete case the predicate used to reject.
const DIRECT_MP4 =
'https://example.com/get_file/5/6cae22f8b3ba9d74f10cc2f8d060af738c8d30ddf5/14644000/14644093/14644093.mp4/?rnd=1787811205406';
describe('HLS QA: simple <video> direct-media predicate', () => {
// Extract and evaluate the REAL isValidManifestUrl from index.js in a browser
// window so its `new URL(...)` / string behavior matches production.
const isValidSrc = extractFn(indexSource, 'isValidManifestUrl');
function assertValid(url, expectValid, label) {
const dom = new JSDOM(
'<!DOCTYPE html><html><body></body></html>',
{ url: 'https://example.com/videos/x/', runScripts: 'dangerously' }
);
const got = dom.window.eval('(' + isValidSrc + ')(' + JSON.stringify(url) + ')');
if (got !== expectValid) {
throw new Error(
`${label || 'isValidManifestUrl'}(${url}) = ${got}, expected ${expectValid}`
);
}
}
it('accepts a real direct-MP4 URL WITH a trailing slash + ?rnd=', () => {
// Before the fix, `path.endsWith('/')` rejected this real media source.
assertValid(DIRECT_MP4, true, 'trailing-slash MP4');
});
it('accepts a direct .mp4 with no trailing slash', () => {
assertValid('https://cdn.example.com/vids/abc123.mp4', true, 'plain MP4');
});
it('accepts real .m3u8 / .webm / .mpd / .m4s / /hls/ media signals', () => {
assertValid('https://cdn.example.com/live/master.m3u8', true, 'master.m3u8');
assertValid('https://cdn.example.com/seg/webm/v.webm', true, 'webm');
assertValid('https://cdn.example.com/seg/dash/master.mpd', true, 'mpd');
assertValid('https://cdn.example.com/seg/1-0.m4s', true, 'm4s');
assertValid('https://cdn.example.com/hls/abc/playlist.m3u8', true, '/hls/');
});
it('does not over-reject a real .m3u8 carrying a valid %2B/%2F token', () => {
// %2B/%2F are valid percent-encoded hex pairs — a genuine auth token, not a
// route-template placeholder, so it must survive.
assertValid('https://cdn.example.com/live/master.m3u8?h=a%2Bb%2Fc&e=1234', true, '%2B token');
});
it('still rejects route-template / nav URLs (no over-acceptance)', () => {
// The fix must not trade one regression for the other: the route templates
// a provider's pageContext.langUrls leaks must still be rejected.
assertValid('https://example.com/playlists/', false, '/playlists/');
assertValid('https://example.com/playlists/liked/', false, '/playlists/liked/');
assertValid('https://example.com/playlist/%1%/video/%2%/', false, '/playlist/%1%/');
assertValid('https://example.com/members/%ID%/playlists/', false, '/members/%ID%/playlists/');
assertValid('https://example.com/videos/%DIR%/', false, '/videos/%DIR%/');
});
});
describe('HLS QA: scrapeTab isolated-world fallback', () => {
// Extract the whole scrapeTab function (balanced braces, indentation-agnostic).
const scrapeSrc = extractFn(indexSource, 'scrapeTab');
it('runs an isolated-world scrape attempt (no `world` key) as a fallback', () => {
const calls = scrapeSrc.match(/executeScript\(\s*\{[\s\S]*?\}\s*\)/g) || [];
if (calls.length === 0) {
throw new Error('scrapeTab has no executeScript call');
}
// At least one executeScript must omit `world` so it runs in the always-
// permitted isolated world (recovers simple videos a MAIN-world injection
// rejects on sandboxed/opaque-origin ad iframes).
const hasIsolated = calls.some((c) => !/\bworld\s*:/.test(c));
if (!hasIsolated) {
throw new Error(
'scrapeTab has no isolated-world (no `world:`) executeScript fallback — ' +
'a MAIN-world+allFrames rejection on an ad-iframe-heavy page leaves 0 videos'
);
}
});
it('primary scrape still uses world: \'MAIN\' (reads page globals)', () => {
const calls = scrapeSrc.match(/executeScript\(\s*\{[\s\S]*?\}\s*\)/g) || [];
const hasMain = calls.some((c) => /world\s*:\s*['"]MAIN['"]/.test(c));
if (!hasMain) {
throw new Error(
'scrapeTab primary attempt must use world: "MAIN" to read page globals ' +
'(interceptor data, Hls instances, custom <video> props)'
);
}
});
});
+7 -7
View File
@@ -3,13 +3,13 @@
"name": "VidPlex", "name": "VidPlex",
"version": "0.1.0", "version": "0.1.0",
"description": "Find videos on any page and view them in a grid.", "description": "Find videos on any page and view them in a grid.",
"permissions": [ "permissions": [
"storage", "storage",
"tabs", "tabs",
"scripting", "scripting",
"webRequest", "webRequest",
"declarativeNetRequest" "declarativeNetRequest"
], ],
"host_permissions": [ "host_permissions": [
"<all_urls>" "<all_urls>"
], ],
+2
View File
@@ -23,5 +23,7 @@
"typescript": "^5.7.0", "typescript": "^5.7.0",
"vite": "^6.0.0", "vite": "^6.0.0",
"vitest": "^3.0.0" "vitest": "^3.0.0"
},
"dependencies": {
} }
} }
+405 -246
View File
@@ -23,13 +23,12 @@ function isHlsManifest(url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
u.includes('/playlist.m3u') || u.includes('/manifest'); u.includes('/playlist.m3u') || u.includes('/manifest');
} }
// === STRATEGY 8: Network-level fallback — log ALL phncdn.com and video-related requests === // === STRATEGY 8: Network-level fallback — log video requests across any CDN ===
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Pornhub's es6player generates HLS manifests in-memory with zero .m3u8 network requests. // Some players generate HLS manifests in-memory and make zero .m3u8 network
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// However, the player still fetches video segments from phncdn.com. By logging all phncdn // requests, only fetching video segments from a CDN. By logging every video-like
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// requests we can: (a) confirm HLS activity via segment patterns, (b) reconstruct manifest // request (any host) we can: (a) confirm HLS activity via segment patterns,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URLs from segment paths, (c) detect CDN response headers that reveal playlist endpoints. // (b) reconstruct manifest URLs from segment paths, (c) detect CDN response
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// headers that reveal playlist endpoints. No site-specific host list.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const PHNCDN_HOSTS = ['phncdn.com', 'pornhubpremium.com', 'cdn.pornhub.com'];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const VIDEO_PATH_PATTERNS = ['/hls/', '/dash/', '/video/', '/segment', '/media/', '/stream']; const VIDEO_PATH_PATTERNS = ['/hls/', '/dash/', '/video/', '/segment', '/media/', '/stream'];
function isVideoRequest(url) { function isVideoRequest(url) {
@@ -47,7 +46,7 @@ async function persistVideoRequest(tabId, url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { try {
const stored = await chrome.storage.local.get(key); const stored = await chrome.storage.local.get(key);
const arr = stored[key] || []; const arr = stored[key] || [];
// Avoid duplicates — phncdn segments come in rapid succession // Avoid duplicates — video segments come in rapid succession
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!arr.some(r => r === url)) { if (!arr.some(r => r === url)) {
arr.push(url); arr.push(url);
// Keep last 200 requests per tab (enough to detect segment patterns) // Keep last 200 requests per tab (enough to detect segment patterns)
@@ -59,26 +58,28 @@ async function persistVideoRequest(tabId, url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
} }
// --- phncdn token / URL helpers ------------------------------------------ // --- Token / URL helpers ------------------------------------------
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// //
// The phncdn auth token (h= / hash= / hdnea=) is base64 and its %-encoded // A media CDN's auth token (h= / hash= / hdnea=) is base64 and its %-encoded
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// escapes are significant: a `+` decoded to a space corrupts it, which is what // escapes are significant: a `+` decoded to a space corrupts it, which is what
// produces the 410 Gone the live player avoids by sending `%2B`. So every // produces the 410 Gone the live player avoids by sending `%2B`. So every
// helper below keeps the query string RAW — it is never passed through // helper below keeps the query string RAW — it is never passed through
// URLSearchParams (which decodes + -> space). // URLSearchParams (which decodes + -> space).
// Extract the phncdn videoId from a /hls/videos/ URL; '' if it isn't one. // Group key for a video's URL: the path directory holding its variant/segment
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Path shape: /hls/videos/{year}/{subdir}/{videoId}/{variant}/... // (the last non-file path segment of a CDN URL). Used to match the freshest
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
function extractVideoId(url) { // live URL by video, host-independent. '' when the URL is malformed.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!url || !url.includes('/hls/videos/')) return ''; function extractGroupKey(url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!url) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { try {
const path = new URL(url).pathname.split('/'); const path = new URL(url).pathname.split('/');
const hlsIdx = path.findIndex(p => p === 'hls'); // Drop the final (file/segment or manifest) segment; the remaining
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (hlsIdx === -1 || hlsIdx + 4 >= path.length) return ''; // directory joins into a key that is stable across a video's variants.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return path[hlsIdx + 4]; path.pop();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch { return path.join('/');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return ''; return '';
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
// Raw query (everything after '?') with no decoding. '' when absent. // Raw query (everything after '?') with no decoding. '' when absent.
@@ -99,31 +100,24 @@ function tokenExpirySeconds(rawQuery) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return secs; return secs;
} }
// Build a manifest URL for a variant directory from a raw phncdn URL, // Build the parent manifest URL for a segment URL, keeping the FULL path to
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// preserving the FULL variant path and the raw query (token encoding intact). // the variant directory and the RAW query (token encoding intact). Swaps the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// /hls/videos/{year}/{subid}/{videoId}/{variant}/seg-1.ts -> // segment filename for a manifest filename in the same directory:
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// /hls/videos/{year}/{subid}/{videoId}/{variant}/master.m3u8 // {host}/path/to/variant/seg-1.ts -> {host}/path/to/variant/master.m3u8
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// The variant directory (e.g. 720P_4000K_338337051.mp4) is MANDATORY — a master // The variant directory is MANDATORY — a master playlist lives inside it;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// playlist lives inside it; dropping it (the old bug) yields a 410. Returns '' // dropping it yields a 404/410. Returns '' for non-segment URLs.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// when the URL isn't a phncdn HLS URL with a complete variant path. function buildManifestForVariant(url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
function buildManifestForVariant(rawUrl) { if (!url || !/\/seg-\d+/i.test(url)) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!rawUrl || !rawUrl.includes('/hls/videos/')) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { try {
const path = rawUrl.split('?')[0]; const [pathPart, queryPart] = url.split('?');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const parts = path.split('/'); const idx = pathPart.lastIndexOf('/');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const hlsIdx = parts.findIndex(p => p === 'hls'); if (idx === -1) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Need hls, videos, year, subdir, videoId, variant (6 dirs). const dir = pathPart.slice(0, idx); // variant directory
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (hlsIdx === -1 || hlsIdx + 5 >= parts.length) return ''; const rawQuery = queryPart ? '?' + queryPart : '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const prefix = parts.slice(0, hlsIdx + 2).join('/'); // .../hls/videos return `${dir}/master.m3u8${rawQuery}`;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const year = parts[hlsIdx + 2]; } catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const subid = parts[hlsIdx + 3];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const videoId = parts[hlsIdx + 4];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const variant = parts[hlsIdx + 5];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const rawQuery = extractRawQuery(rawUrl);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return `${prefix}/${year}/${subid}/${videoId}/${variant}/master.m3u8${rawQuery ? '?' + rawQuery : ''}`;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return ''; return '';
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
// Reconstruct manifest URLs from segment URLs, keeping the raw query intact. // Reconstruct manifest URLs from segment URLs, keeping the raw query intact.
@@ -133,124 +127,145 @@ function reconstructManifestsFromSegments(segments) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const seen = new Set(); const seen = new Set();
const manifests = []; const manifests = [];
for (const url of segments) { for (const url of segments) {
// Only handle HLS segment URLs: /hls/videos/{..}/{variant}/seg-N.ts // Only handle video/segment URLs (e.g. .../seg-N.ts).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!url.includes('/hls/videos/') || !/\/seg-\d+/.test(url)) continue;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const manifestUrl = buildManifestForVariant(url); const manifestUrl = buildManifestForVariant(url);
if (manifestUrl && !seen.has(manifestUrl)) { if (manifestUrl && !seen.has(manifestUrl)) {
seen.add(manifestUrl); seen.add(manifestUrl);
manifests.push(manifestUrl); manifests.push(manifestUrl);
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return manifests; return manifests;
} }
// Collect phncdn URLs across ALL tabs without a tabs.query match pattern (which // Collect captured video URLs across ALL tabs without a tabs.query match pattern
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// throws if the pattern is malformed). We scan every persisted vr_requests_ / // (which throws if the pattern is malformed). We scan every persisted vr_requests_ /
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// wr_manifests_ key so the rescan works even if the live source tab is closed. // wr_manifests_ key so the rescan works even if the live source tab is closed.
async function collectAllPhncdnUrls() { async function collectAllVideoUrls() {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const all = await chrome.storage.local.get(); const all = await chrome.storage.local.get();
const out = []; const out = [];
for (const key of Object.keys(all)) { for (const key of Object.keys(all)) {
if ((key.startsWith('vr_requests_') || key.startsWith('wr_manifests_')) && Array.isArray(all[key])) { if ((key.startsWith('vr_requests_') || key.startsWith('wr_manifests_')) && Array.isArray(all[key])) {
out.push(...all[key]); out.push(...all[key]);
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return out; return out;
} }
// For a videoId, return the freshest live phncdn URL (highest token expiry) // For a group key, return the freshest live captured URL (highest token
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// across all persisted tabs, preferring captured .m3u8 manifest URLs. null if // expiry) across all persisted tabs, preferring captured .m3u8 manifest URLs.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// nothing usable is captured yet. Rescans use this to replace a stored URL that // null if nothing usable is captured yet. Rescans use this to replace a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// now 410s with the newest token the live tab captured. // stored URL that now 410s with the newest token the live tab captured.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// //
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// No tabs.query match pattern is used: '*pornhub.com*' is an INVALID pattern and // No tabs.query match pattern is used: match patterns are brittle (a bad
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// made this throw ("Invalid url pattern"), which silently dropped every rescan. // pattern throws "Invalid url pattern" and silently drops every rescan).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// We scan persisted capture data instead, which is always available and // We scan persisted capture data instead, which is always available and
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// sidesteps the match-Pattern API entirely. // sidesteps the match-pattern API entirely.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function freshLiveForVideo(videoId, preferManifest) { async function freshLiveForGroup(groupKey, preferManifest) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let all; let all;
try { try {
all = await collectAllPhncdnUrls(); all = await collectAllVideoUrls();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch (e) { } catch (e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] collectAllPhncdnUrls failed:', e && e.message ? e.message : e); console.warn('[goon-bg] collectAllVideoUrls failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
all = []; all = [];
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let bestManifest = null, bestManifestExp = 0; let bestManifest = null, bestManifestExp = 0;
let bestVariant = null, bestVariantExp = 0; let bestVariant = null, bestVariantExp = 0;
let bestTokenSource = null, bestTokenExp = 0; let bestTokenSource = null, bestTokenExp = 0;
for (const url of all) { for (const url of all) {
if (extractVideoId(url) !== videoId) continue; if (extractGroupKey(url) !== groupKey) continue;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const exp = tokenExpirySeconds(extractRawQuery(url)); const exp = tokenExpirySeconds(extractRawQuery(url));
if (url.includes('.m3u8')) { if (url.includes('.m3u8')) {
if (exp > bestManifestExp) { bestManifestExp = exp; bestManifest = url; } if (exp > bestManifestExp) { bestManifestExp = exp; bestManifest = url; }
} else if (/\/seg-\d+/.test(url)) { } else if (/\/seg-\d+/i.test(url)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (exp > bestVariantExp) { bestVariantExp = exp; bestVariant = url; } if (exp > bestVariantExp) { bestVariantExp = exp; bestVariant = url; }
} else { } else {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (exp > bestTokenExp) { bestTokenExp = exp; bestTokenSource = url; } if (exp > bestTokenExp) { bestTokenExp = exp; bestTokenSource = url; }
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Prefer a captured manifest; else reconstruct the variant manifest; else // Prefer a captured manifest; else the best segment (reconstructable to a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// hand back a token source so the proxy can at least re-token a good path. // manifest); else a token source so the proxy can re-token a good path.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (bestManifest) return bestManifest; if (bestManifest) return bestManifest;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!preferManifest && bestVariant) return bestVariant; if (!preferManifest && bestVariant) return bestVariant;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!preferManifest && bestTokenSource) return bestTokenSource; if (!preferManifest && bestTokenSource) return bestTokenSource;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (bestVariant) return bestVariant; if (bestVariant) return bestVariant;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return bestManifest || bestTokenSource || null; return bestManifest || bestTokenSource || null;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Cache freshest tokens per videoId so we don't query storage on every proxy // Cache freshest tokens per group key so we don't query storage on every proxy
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// request. Keyed by videoId; values resolve once and are reused until a rescan // request. Keyed by group key; values resolve once and are reused until a rescan
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (GOON_RESOLVE_MANIFEST) or a 410 forces an invalidation. // (GOON_RESOLVE_MANIFEST) or a 410 forces an invalidation.
const tokenCache = new Map(); const tokenCache = new Map();
// Given a phncdn /hls/videos/ URL that arrives WITHOUT a query (hls.js drops the // Given a CDN URL that arrives WITHOUT a query (hls.js drops the relative-URL
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// relative-URL token when building sub-playslists/segments), inject the freshest // token when building sub-playslists/segments), inject the freshest live token
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// live token for that videoId. Returns the URL with a query appended, or the // for that group. Returns the URL with a query appended, or the original URL
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// original URL unchanged when no token is available yet. // unchanged when no token is available yet.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function injectFreshToken(url) { async function injectFreshToken(url) {
if (!url || !url.includes('/hls/videos/')) return url; if (!url) return url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Already carries a token — leave it alone. // Already carries a token — leave it alone.
if (url.includes('?')) return url; if (url.includes('?')) return url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const videoId = extractVideoId(url); try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!videoId) return url; const groupKey = extractGroupKey(url);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (tokenCache.has(videoId)) { if (!groupKey) return url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const token = tokenCache.get(videoId); if (tokenCache.has(groupKey)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const token = tokenCache.get(groupKey);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return token ? url + '?' + token : url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const fresh = await freshLiveForGroup(groupKey, false);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const token = fresh ? extractRawQuery(fresh) : '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
tokenCache.set(groupKey, token);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] injectFreshToken for', groupKey, (token ? token.substring(0, 40) + '…' : '(none captured)'));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return token ? url + '?' + token : url; return token ? url + '?' + token : url;
} } catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const fresh = await freshLiveForVideo(videoId, false); return url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const token = fresh ? extractRawQuery(fresh) : ''; }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
tokenCache.set(videoId, token);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] injectFreshToken for', videoId, videoId + '-> ' + (token ? token.substring(0, 40) + '…' : '(none captured)'));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return token ? url + '?' + token : url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
// Clear the per-video token cache. Called on rescan so the next proxy fetches // Clear the per-video token cache. Called on rescan so the next proxy fetches
// re-query storage for a freshly-rotated token. // re-query storage for a freshly-rotated token.
function invalidateTokenCache() { tokenCache.clear(); } function invalidateTokenCache() { tokenCache.clear(); }
// CDN registrable domains whose referer we have already captured this SW
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// lifetime, so we do not re-query the tab for every segment of a stream.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Lost if the SW is killed; persistReferer's storage dedupe covers a fresh SW.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const refererCapturedDomains = new Set();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
chrome.webRequest.onBeforeRequest.addListener( chrome.webRequest.onBeforeRequest.addListener(
async (details) => { async (details) => {
if (details.tabId < 0) return; if (details.tabId < 0) return;
const u = details.url.toLowerCase(); // Log video-like requests from any host (CDN-agnostic).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const isPhncdn = PHNCDN_HOSTS.some(host => details.url.includes(host)); if (isVideoRequest(details.url)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const isVideo = isVideoRequest(details.url); console.log('[goon-bg] webRequest: video request in tab', details.tabId, ':', details.url.substring(0, 180));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Log ALL phncdn.com requests regardless of path
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (isPhncdn) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] webRequest: phncdn request in tab', details.tabId, ':', details.url.substring(0, 180));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await persistVideoRequest(details.tabId, details.url); await persistVideoRequest(details.tabId, details.url);
} // Capture the provider referer for this CDN host from the source tab's
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URL: the browser sends the referring page's URL as the Referer for a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// cross-origin video request, and that IS the tab's URL. extraInfoSpec
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (reading the actual request headers) is rejected by some Chromium
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// builds and fails the whole service worker, so we derive the referer
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// from the tab instead. The `tabs` + host_permissions already allow it.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const host = hostOf(details.url);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const domain = host ? registrableDomain(host) : '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (domain && !refererCapturedDomains.has(domain)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const tab = await chrome.tabs.get(details.tabId);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const ref = tab && tab.url;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (ref) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await persistReferer(host, ref);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererCapturedDomains.add(domain);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] captured referer for', domain, '->', ref);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Tab may have closed mid-stream; a later request from a live tab
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// re-captures. Best-effort.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Log video-like requests from any host // Also capture HLS manifest URLs (original logic)
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (isVideo && !isPhncdn) { if (!isHlsManifest(details.url)) return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] webRequest: video request in tab', details.tabId, ':', details.url.substring(0, 150));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Also capture HLS manifest URLs (original logic)
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!isHlsManifest(details.url)) return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] webRequest: HLS manifest in tab', details.tabId, ':', details.url.substring(0, 120)); console.log('[goon-bg] webRequest: HLS manifest in tab', details.tabId, ':', details.url.substring(0, 120));
@@ -269,76 +284,193 @@ chrome.webRequest.onBeforeRequest.addListener(
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
{ urls: ['<all_urls>'] } { urls: ['<all_urls>'] }
); );
// Clean up stale entries when tabs are closed // Clean up stale PER-TAB entries when tabs are closed. NOTE: 'goon_referers' is
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// a domain-keyed GLOBAL store — it must outlive its capture tab so a proxy fetch
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// for that host still finds its referer after the source tab is closed. It is
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// therefore NOT removed here (only the per-tab vr_requests_/wr_manifests_ keys
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// are). Stale referers are pruned opportunistically in persistReferer.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
chrome.tabs.onRemoved.addListener(async (tabId) => { chrome.tabs.onRemoved.addListener(async (tabId) => {
const keys = ['wr_manifests_' + tabId, 'vr_requests_' + tabId]; const keys = ['wr_manifests_' + tabId, 'vr_requests_' + tabId];
try { try {
await chrome.storage.local.remove(keys); await chrome.storage.local.remove(keys);
} catch { /* ignore */ } } catch { /* ignore */ }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
});
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// === Force a pornhub Referer on every *.phncdn.com request ===
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// The phncdn video CDN (hv-h.phncdn.com, pix-fl.phncdn.com, ev-h.phncdn.com,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// ... and the auth-token hosts) rejects any request whose Referer is not a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// pornhub-origin with HTTP 412 (see round-1 curl test: `Referer:
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// https://www.pornhub.com/` -> 200, no referer -> 412). Origin is NOT the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// gate (curl with a pornhub Referer and no Origin still 200s), so we only
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// rewrite Referer and never touch Origin.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// A normal fetch() CANNOT set the `Referer` header — it is a "forbidden
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// header" in the Fetch API, so a service-worker fetch() gets
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// `Referer: chrome-extension://<id>`, which the CDN rejects (412). The
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// declarativeNetRequest API rewrites request headers at the network layer,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// below the Fetch API's forbidden-header restriction, and — per Chrome docs —
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// these rules DO apply to fetch() calls made inside the service worker. So we
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// force a pornhub referer on phncdn.com; the GOON_PROXY_FETCH handler in the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// SW then fetches the manifest + segments successfully.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const REFERER_RULE_ID = 9001;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const REFERER_RULE = {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
id: REFERER_RULE_ID,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
priority: 1,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
action: {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
type: 'modifyHeaders',
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// `set` is allowed for any request header (only `append` is restricted to
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// a fixed allowlist). `referer` is the header the CDN checks.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
requestHeaders: [
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
{ header: 'referer', operation: 'set', value: 'https://www.pornhub.com/' },
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
},
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
condition: {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// phncdn.com (host + subdomains) ONLY. Do NOT include pornhub.com: the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// real page player's auth calls (api.pornhub.com) and its own segment
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// fetches must keep their browser-supplied Origin/Referer or playback
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// freezes. The proxy only ever fetches phncdn URLs.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
requestDomains: ['phncdn.com'],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
},
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
};
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Cached promise so concurrent proxy fetches share one rule install; a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// transient failure is cleared so a later fetch retries.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let refererRuleReady = null;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function ensureRefererRule() {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (refererRuleReady) return refererRuleReady;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererRuleReady = (async () => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const api = chrome.declarativeNetRequest;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!api || typeof api.updateSessionRules !== 'function') {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
throw new Error('declarativeNetRequest.updateSessionRules unavailable');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Idempotent: remove any stale copy of our rule, then add the fresh one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await api.updateSessionRules({
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
removeRuleIds: [REFERER_RULE_ID],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
addRules: [REFERER_RULE],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}); });
console.log('[goon-bg] DNR referer rule installed for *.phncdn.com');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
})(); // === Capture the provider-origin Referer per CDN host (CDN-agnostic) ===
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// If it failed, reset so the next call retries instead of caching the // Some media CDNs reject requests whose `Referer` is not the provider page's
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// rejected promise forever. // origin (HTTP 412). A fetch() CANNOT set `Referer` — it is a "forbidden
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererRuleReady.catch(() => { refererRuleReady = null; }); // header" in the Fetch API, so a service-worker fetch() would send
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return refererRuleReady; // `Referer: chrome-extension://<id>`, which the CDN rejects. The
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// declarativeNetRequest API rewrites request headers at the network layer, below
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the Fetch API's restriction, and its rules apply to fetch() calls made inside
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the SW.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// We cannot hardcode a target referer: the proxy is invoked from the viewer
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (a different origin than the source page), and a DNR rule cannot read a live
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// page's origin. So we derive the provider referer from the source tab's own URL.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// For a cross-origin video request the browser sends the referring page's URL as
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the Referer, and that page IS the tab currently loading the video — so the tab
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URL is the referer we need. We read it via chrome.webRequest.onBeforeRequest
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (no header access — it cannot read the request headers) plus chrome.tabs.get,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// which returns the source tab's URL, and persist that per CDN host. The
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// GOON_PROXY_FETCH handler then installs a DNR rule that re-inserts that
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// captured referer for just that host, so the proxy's fetch() is accepted.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// This requires no knowledge of any particular site. Deriving the referer from
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the tab URL (rather than reading the real request header via
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// onBeforeSendHeaders + extraHeaders) is intentional: extraInfoSpec header
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// parsing is rejected by some Chromium builds and fails the whole service worker,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// and the tabs + host permissions already permit chrome.tabs.get.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Each CDN host gets its OWN declarativeNetRequest rule id so that concurrent
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// proxy fetches for different hosts can't clobber each other's rule (a single
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// shared id + a remove-then-add lets two hosts interleave and leave the wrong
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// host's referer live). The id is derived from the host via a monotonic counter
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// keyed by host, so it is stable for a given host across installs.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let nextRefererRuleId = 9001;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const refererRuleIds = new Map();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
function refererRuleIdFor(host) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const existing = refererRuleIds.get(host);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (existing !== undefined) return existing;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const id = nextRefererRuleId++;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererRuleIds.set(host, id);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return id;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// host of a URL (CDN host, lowercased); '' when the URL is malformed.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
function hostOf(url) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!url) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { return new URL(url).host.toLowerCase(); } catch { return ''; }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Registrable domain of a CDN host — the label a single provider's referer
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// applies to. A referer-protecting CDN serves manifests and segments from many
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// edge subdomains that share ONE
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// provider referer, so we key the referer store, the DNR rule, and proxy-host
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// matching on this domain rather than the exact subhost.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Heuristic: the last two DNS labels. Correct for the overwhelmingly common
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// single-label public suffixes (.com, .net, .org, .io, ...). Known caveat:
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// multi-label public suffixes such as .co.uk / .com.au collapse to the public
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// suffix itself (foo.co.uk -> co.uk); that is an exotic edge case and no worse
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// than the exact-host behavior it replaces. A single-level host (no dot, or a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// bare TLD) is returned unchanged.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
function registrableDomain(host) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!host) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const parts = host.split('.');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (parts.length < 2) return host;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return parts.slice(-2).join('.');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// How long a captured referer is trusted before it is pruned on the next
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// persist. Kept long enough for the scan-now / watch-later flow (a user scans a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// page, then opens the viewer much later) without tying cleanup to tab close (a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// still-open source tab's referer must survive other tabs closing).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const REFERER_TTL_MS = 7 * 24 * 60 * 60 * 1000;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Persist the captured referer per CDN registrable domain so the proxy can
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// re-use it even after the capture tab closes. Keyed by the host's registrable
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// domain so a single provider referer covers every edge subdomain (a CDN serves
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// manifests/segments from many subhosts that share one referer), and so a proxy
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// for any sibling subhost can look it up without knowing the source tab. Each
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// entry is stamped with a capture time; entries older than the TTL are pruned
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// opportunistically here (never on tab close).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function persistReferer(host, referer) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const domain = registrableDomain(host);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!domain || !referer) return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const stored = await chrome.storage.local.get('goon_referers');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const map = stored['goon_referers'] || {};
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const now = Date.now();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Opportunistic per-domain prune: drop entries older than the TTL.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
for (const [h, entry] of Object.entries(map)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (h !== domain && (!entry || !entry.at || now - entry.at > REFERER_TTL_MS)) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
delete map[h];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const current = map[domain];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (current && current.referer === referer) return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
map[domain] = { referer, at: now };
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await chrome.storage.local.set({ goon_referers: map });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch (e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] persistReferer failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Look up the referer captured for a host's registrable domain. '' when none
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// has been captured (or the stored entry is malformed).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function getReferer(host) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!host) return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const stored = await chrome.storage.local.get('goon_referers');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const entry = (stored['goon_referers'] || {})[registrableDomain(host)];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return entry && entry.referer ? entry.referer : '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Install (idempotently) a DNR rule that sets the captured referer for a given
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// CDN host. The value comes from capture (or a fallback origin), never a
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// hardcoded site. Scoped to the requested host so only proxy fetches for that
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// host get the rewritten header.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Each host gets its own rule id (refererRuleIdFor) so that concurrent proxy
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// fetches for DIFFERENT hosts never interleave: host A's rule and host B's rule
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// are distinct DNR ids and one install can't remove the other. Within a single
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// host, a per-host async lock (refererRuleLocks) serializes the idempotency
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// check + updateSessionRules so the install-and-record pair is atomic — no two
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// installs for the same host can interleave their check/update.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const refererRuleInstalled = new Map();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const refererRuleLocks = new Map();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function ensureRefererRule(host, referer) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const domain = registrableDomain(host);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!domain || !referer) return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const api = chrome.declarativeNetRequest;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!api || typeof api.updateSessionRules !== 'function') {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] declarativeNetRequest unavailable; skipping referer rule');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
// Install eagerly on SW start so even the first proxy fetch is covered. // Serialize per host so the idempotency check + updateSessionRules is atomic.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
ensureRefererRule(); const run = (async () => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (refererRuleInstalled.has(domain) && refererRuleInstalled.get(domain) === referer) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const ruleId = refererRuleIdFor(domain);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Remove only THIS host's own rule id, then add it back scoped to the host.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await api.updateSessionRules({
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
removeRuleIds: [ruleId],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
addRules: [{
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
id: ruleId,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
priority: 1,
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
action: {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
type: 'modifyHeaders',
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// `set` is allowed for any request header (only `append` is restricted
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// to a fixed allowlist). `referer` is the header the CDN checks.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
requestHeaders: [
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
{ header: 'referer', operation: 'set', value: referer },
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
},
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
condition: {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Host + subdomains ONLY, so only proxy fetches for this host get the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// rewritten referer; the source page's own requests keep theirs.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
requestDomains: [domain],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
},
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}],
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
});
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererRuleInstalled.set(domain, referer);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.log('[goon-bg] DNR referer rule installed for', domain, '(' + ruleId + ')');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch (e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] ensureRefererRule failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
})().finally(() => { refererRuleLocks.set(domain, null); });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
refererRuleLocks.set(domain, run);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return run;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// === Message handlers for popup/data consumers === // === Message handlers for popup/data consumers ===
@@ -362,11 +494,11 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true; return true;
} }
case 'GOON_GET_PHNCDN_REQUESTS': { case 'GOON_GET_VIDEO_REQUESTS': {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Strategy 8: Return all phncdn.com requests for a tab, including reconstructed manifests // Strategy 8: Return all video requests for a tab, including reconstructed manifests
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
(async () => { (async () => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const tabId = message.tabId; const tabId = message.tabId;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const [segKey] = ['vr_requests_' + tabId]; const [segKey] = ['vr_requests_' + tabId];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { try {
const stored = await chrome.storage.local.get(segKey); const stored = await chrome.storage.local.get(segKey);
const segments = stored[segKey] || []; const segments = stored[segKey] || [];
@@ -384,76 +516,103 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true; return true;
} }
case 'GOON_RESOLVE_MANIFEST': { case 'GOON_RESOLVE_MANIFEST': {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Resolve the freshest live phncdn manifest for the video in a stored // Resolve the freshest live manifest for the video encoded in a stored
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URL. The viewer calls this at startup and on a fatal network error: // URL. The viewer calls this at startup and on a fatal network error:
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the live tab keeps rotating short-lived tokens, so the freshest // the live tab keeps rotating short-lived tokens, so the freshest
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// captured manifest (highest expiry) replaces one that has 410'd. // captured manifest (highest expiry) replaces one that has 410'd.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Clears the token cache first so subsequent proxy fetches re-query for // Clears the token cache first so subsequent proxy fetches re-query for
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// the freshly-rotated token rather than the cached (now-dead) one. // the freshly-rotated token rather than the cached (now-dead) one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function resolveFreshManifest(message, sendResponse) { async function resolveFreshManifest(message, sendResponse) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
invalidateTokenCache(); invalidateTokenCache();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const url = message.url || ''; const url = message.url || '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const videoId = extractVideoId(url); const groupKey = extractGroupKey(url);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!videoId) { sendResponse({ ok: false, error: 'no videoId' }); return; } if (!groupKey) { sendResponse({ ok: false, error: 'no group key' }); return; }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try { try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const fresh = await freshLiveForVideo(videoId, true); const fresh = await freshLiveForGroup(groupKey, true);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: true, fresh, videoId, changed: !!(fresh && fresh !== url) }); sendResponse({ ok: true, fresh, groupKey, changed: !!(fresh && fresh !== url) });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch (e) { } catch (e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] resolveFreshManifest failed:', e && e.message ? e.message : e); console.warn('[goon-bg] resolveFreshManifest failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: false, error: e && e.message ? e.message : String(e), videoId }); sendResponse({ ok: false, error: e && e.message ? e.message : String(e), groupKey });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
resolveFreshManifest(message, sendResponse);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
case 'GOON_PROXY_FETCH': {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Proxy a fetch request through the service worker (bypasses CORS).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Returns base64-encoded response data. Used by the viewer when
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// extension-origin requests to video CDNs are blocked by CORS.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// The phncdn CDN 412s requests without a pornhub Referer. A fetch()
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// can't set Referer itself (forbidden header — Chrome replaces it with
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// chrome-extension://...), so we install a declarativeNetRequest rule
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// that forces `Referer: https://www.pornhub.com/` at the network layer
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// for all *.phncdn.com requests (DNR applies to SW fetch() per Chrome
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// docs). Await it first so the very next fetch is covered.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
(async () => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
await ensureRefererRule();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// hls.js resolves sub-playslists/segments relative to the master
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// and can DROP the token query, handing us a token-less phncdn
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URL that 410s. injectFreshToken supplies the freshest live token
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// for that videoId; the final `+` -> `%2B` pass keeps the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (possibly injected or surviving) base64 token intact for the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// CDN's query parsing.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const resolved = await injectFreshToken(message.url || '');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const proxyUrl = resolved.replace(/\+/g, '%2B');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const resp = await fetch(proxyUrl);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!resp.ok) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] proxyFetch', resp.status, message.url.substring(0, 120));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: false, status: resp.status, error: 'HTTP ' + resp.status });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const buffer = await resp.arrayBuffer();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const bytes = new Uint8Array(buffer);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Convert to base64 (btoa can only handle strings up to ~65k chars)
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let binary = '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const CHUNK_SIZE = 32768;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const chunk = bytes.subarray(i, i + CHUNK_SIZE);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
binary += String.fromCharCode.apply(null, Array.from(chunk));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const base64 = btoa(binary);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: true, data: base64, mimeType: resp.headers.get('content-type') || '' });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch(e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] proxyFetch failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: false, error: e && e.message ? e.message : String(e) });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} }
})(); }
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true; resolveFreshManifest(message, sendResponse);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} return true;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
case 'GOON_PROXY_FETCH': {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Proxy a fetch request through the service worker (bypasses CORS).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Returns base64-encoded response data. Used by the viewer when
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// extension-origin requests to video CDNs are blocked by CORS.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
//
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// A fetch() can't set the Referer itself (forbidden header — Chrome
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// replaces it with chrome-extension://...), so a declarativeNetRequest
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// rule installs the captured provider-origin referer at the network
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// layer (DNR applies to SW fetch() per Chrome docs). The rule is
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// installed on demand for the requested host only.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
(async () => {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
try {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const proxyHost = hostOf(message.url);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (proxyHost) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Prefer the provider's real referer captured from the live
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// tab (domain-aware), or the entry's scan-time referer the viewer sent.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// With neither, install no rule: a self-origin referer is
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// guaranteed-wrong for hotlink protection, so we never fall
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// back to one (it would just 412).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const captured = await getReferer(proxyHost);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const referer = captured || message.referer || '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (referer) await ensureRefererRule(proxyHost, referer);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// hls.js resolves sub-playslists/segments relative to the master
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// and can DROP the token query, handing us a token-less
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// URL that 410s. injectFreshToken supplies the freshest live
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// token for that video; the final `+` -> `%2B` pass keeps the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (possibly injected or surviving) base64 token intact for the
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// CDN's query parsing.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const resolved = await injectFreshToken(message.url || '');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const proxyUrl = resolved.replace(/\+/g, '%2B');
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const resp = await fetch(proxyUrl);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (!resp.ok) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] proxyFetch', resp.status, message.url.substring(0, 120));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: false, status: resp.status, error: 'HTTP ' + resp.status });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const buffer = await resp.arrayBuffer();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const bytes = new Uint8Array(buffer);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Convert to base64 (btoa can only handle strings up to ~65k chars)
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
let binary = '';
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const CHUNK_SIZE = 32768;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const chunk = bytes.subarray(i, i + CHUNK_SIZE);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
binary += String.fromCharCode.apply(null, Array.from(chunk));
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const base64 = btoa(binary);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: true, data: base64, mimeType: resp.headers.get('content-type') || '' });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
} catch(e) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
console.warn('[goon-bg] proxyFetch failed:', e && e.message ? e.message : e);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: false, error: e && e.message ? e.message : String(e) });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
})();
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
case 'GOON_GET_REFERERS': {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Resolve the provider referer captured for each manifest host
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// (domain-aware: one provider referer covers every edge subdomain).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// Used at scan time to bind the referer to a stored video entry so it
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
// survives the watch-later flow.
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
async function getReferers(message, sendResponse) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const hosts = Array.isArray(message.hosts) ? message.hosts : [];
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const referers = {};
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
for (const host of hosts) {
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
const r = await getReferer(host);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
if (r) referers[host] = r;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
sendResponse({ ok: true, referers });
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
getReferers(message, sendResponse);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
return true;
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
}
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
default: default:
console.warn('[goon-plugin] Unknown message type:', message.type); console.warn('[goon-plugin] Unknown message type:', message.type);
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.

Blocking: 'goon_referers' is a host-keyed GLOBAL store meant to outlive its capture tab (see the docstring just above and the PR body: "persist it per CDN host ... even after this capture tab closes"). Adding it to the per-tab onRemoved cleanup means closing ANY tab wipes referers captured by other still-open source tabs, so the next proxy fetch for those hosts loses its referer and 412s. Remove 'goon_referers' from this array (keep the per-tab vr_requests_ / wr_manifests_ keys). If you want cleanup, key it per host with an expiry instead of on every tab removal.
Review

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).

Concurrent-proxy race: REFERER_RULE_ID is a single shared id and the idempotency check + updateSessionRules({removeRuleIds, addRules}) are not atomic. Two hosts proxied in parallel (common in the multi-panel grid) can interleave so the live rule ends up host B's while refererRuleInstalled records host A as installed -> host A's fetches get host B's referer -> 412. Now that the rule is per-host, use a distinct rule id per host (or add all hosts' rules in one updateSessionRules call) and make the install+record atomic (per-host async lock / queue).
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
Review

[SHOULD-FIX] F2
Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The extraHeaders permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism.
Suggested fix: Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.

[SHOULD-FIX] F2 Documentation/behavior mismatch. The PR body, commit 66c6218 message, and the code comments at :311 (:311 'via chrome.webRequest.onBeforeSendHeaders, which sees the request headers') describe an onBeforeSendHeaders + extraHeaders header-reading capture. The actual code uses onBeforeRequest (cannot read request headers) and derives the referer from the source TAB's URL instead (chrome.tabs.get). The `extraHeaders` permission is not declared in manifest.json (permissions: storage, tabs, scripting, webRequest, declarativeNetRequest). The commit message for :245-248 ('extraInfoSpec ... is rejected by some Chromium builds and fails the whole service worker') explains why, but the PR body / high-level comments were never updated to match, so a reviewer is misled about the mechanism. **Suggested fix:** Reword the PR body and the :311/:308-314 comments to say the referer is derived from the source tab URL (via onBeforeRequest + chrome.tabs.get) rather than 'read from request headers via onBeforeSendHeaders'. The actual mechanism is sound and arguably safer than reading real headers; the mismatch is a documentation issue, not a correctness one.
+5 -5
View File
@@ -25,11 +25,11 @@
/** /**
* Inject interceptors into the MAIN world at document_start. * Inject interceptors into the MAIN world at document_start.
* *
* Uses script.src = chrome.runtime.getURL('interceptors.js') instead of * Uses script.src = chrome.runtime.getURL('interceptors.js') instead of
* script.textContent to bypass strict page CSP. Sites like Pornhub enforce * script.textContent to bypass strict page CSP. Some sites enforce
* 'script-src' that blocks inline execution (textContent). Chrome treats * 'script-src' that blocks inline execution (textContent). Chrome treats
* extension-originated <script src="..."> as content scripts — not as * extension-originated <script src="..."> as content scripts — not as
* page inline code — so the page's CSP does not apply. * page inline code — so the page's CSP does not apply.
* *
* IMPORTANT: Remove the script element ONLY after the load event fires. * IMPORTANT: Remove the script element ONLY after the load event fires.
* Removing it synchronously can prevent the browser from starting the fetch, * Removing it synchronously can prevent the browser from starting the fetch,
+2 -1
View File
@@ -2,7 +2,8 @@
* interceptors.js — Main-world interceptors for HLS manifest detection. * interceptors.js — Main-world interceptors for HLS manifest detection.
* *
* This file is injected into the main world via a <script src="..."> tag * This file is injected into the main world via a <script src="..."> tag
* (NOT inline textContent) to bypass strict CSP on sites like Pornhub. * (NOT inline textContent) to bypass strict CSP on sites that restrict
* script-src.
* *
* When loaded as an external script from the extension origin, Chrome treats * When loaded as an external script from the extension origin, Chrome treats
* it as an extension content script — not as page inline code — so the * it as an extension content script — not as page inline code — so the
+206 -148
View File
@@ -122,7 +122,11 @@ function m3u8DeepProbeFn() {
function probeLog(...args) { function probeLog(...args) {
try { console.log('[goon-probe]', ...args); } catch(e) {} try { console.log('[goon-probe]', ...args); } catch(e) {}
} }
probeLog('Deep probe started on:', window.location.href, 'World:', typeof chrome !== 'undefined' ? 'ISOLATED' : 'MAIN'); // NOTE: the probe is injected with chrome.scripting's `world: 'MAIN'`, so it
// runs in the page's main world and can see page globals. `typeof chrome` is
// NOT a reliable world discriminator (extensions expose `chrome` in the
// isolated world too), so don't infer it that way.
probeLog('Deep probe started on:', window.location.href, '(main world)');
// Helpers available in the MAIN world context // Helpers available in the MAIN world context
function resolveUrl(url) { function resolveUrl(url) {
@@ -134,17 +138,39 @@ function m3u8DeepProbeFn() {
} catch { return null; } } catch { return null; }
} }
function isManifestUrl(url) { function isManifestUrl(url) {
if (!url) return false; if (!url) return false;
const u = url.toLowerCase(); const u = url.toLowerCase();
return u.includes('.m3u8') || u.includes('.hlsvod') || // Concrete HLS/manifest signals win over the template heuristics below: a
(u.includes('/hls/') && u.includes('http')) || // real .m3u8/.hlsvod//hls/ path is fetchable even if it happens to carry a
(u.includes('/playlist') && u.includes('http')) || // trailing '%' query token or a trailing slash — so check these first.
(u.includes('/manifest') && u.includes('http')); if (u.includes('.m3u8') || u.includes('.hlsvod') || u.includes('/hls/')) return true;
} // Dynamic route TEMPLATES (e.g. /playlist/%1%/ or /videos/{id}/) are nav
// links, not fetchable manifests. A placeholder is a `%` that is NOT the
// start of a valid %-encoded hex pair (so real tokens like %2B, %2F are
// preserved) or a {name} brace token — so genuine %-encoded auth tokens are
// never mistaken for template placeholders.
if (/%(?!([0-9A-Fa-f]{2}))/i.test(u) || /\{[a-z0-9_]+\}/i.test(u)) return false;
// Plural collection paths are nav menus, not manifests.
if (u.includes('/playlists') || u.includes('/manifests')) return false;
// A singular manifest-ish path segment with no concrete file may still be a
// real (rare, extensionless) endpoint, so allow it when it carries http.
return (u.includes('/manifest') || u.includes('/playlist')) && u.includes('http');
}
function walk(obj, path, depth) { function walk(obj, path, depth) {
if (depth > 5) return; if (depth > 5) return;
// Cycle guard: the live window objects hold circular references
// (window/_parent/_top/frames/_globalObject/_globalProxy are mutually
// reachable), so a depth cap alone does NOT terminate — descending a cycle
// re-walks the same identities along exponentially many paths. Each object
// tree gets its own visited set so an identity is walked at most once per
// tree (trees are independent, so the set is declared per call).
const visited = new WeakSet();
if (obj && typeof obj === 'object') {
if (visited.has(obj)) return;
visited.add(obj);
}
if (typeof obj === 'string') { if (typeof obj === 'string') {
const match = obj.match(/https?:\/\/[^"'`\s)]+?(?:\.m3u8|\.hlsvod|\/hls\/|\/playlist[^"'`\s]*|\/manifest[^"'`\s]*)[^"'`\s)]*/gi); const match = obj.match(/https?:\/\/[^"'`\s)]+?(?:\.m3u8|\.hlsvod|\/hls\/|\/playlist[^"'`\s]*|\/manifest[^"'`\s]*)[^"'`\s)]*/gi);
if (match) { if (match) {
@@ -319,10 +345,10 @@ function m3u8DeepProbeFn() {
} }
probeLog('Strategy E (interceptor data): found', manifests.length - preECount, 'new manifests'); probeLog('Strategy E (interceptor data): found', manifests.length - preECount, 'new manifests');
// Strategy F: Dynamic playlist fallback — scan video elements for Hls-like // Strategy F: Dynamic playlist fallback — scan video elements for Hls-like
// instances and extract ANY URL, not just .m3u8. Sites like Pornhub use // instances and extract ANY URL, not just .m3u8. Some players use
// HLS.js dynamic playlists where the "manifest" is generated in-memory, // HLS.js dynamic playlists where the "manifest" is generated in-memory,
// so the URL might be a blob:, CDN endpoint, or data URI. // so the URL might be a blob:, CDN endpoint, or data URI.
let strategyFCount = 0; let strategyFCount = 0;
try { try {
for (const v of document.querySelectorAll('video')) { for (const v of document.querySelectorAll('video')) {
@@ -666,8 +692,9 @@ async function probeHlsManifests(tabId) {
log('probeHlsManifests: executing deep probe on tab', tabId); log('probeHlsManifests: executing deep probe on tab', tabId);
const raw = await chrome.scripting.executeScript({ const raw = await chrome.scripting.executeScript({
target: { tabId, allFrames: true }, target: { tabId, allFrames: true },
world: 'MAIN',
func: m3u8DeepProbeFn, func: m3u8DeepProbeFn,
}); });
// When allFrames is true, destroyed / sandboxed frames may return null entries. // When allFrames is true, destroyed / sandboxed frames may return null entries.
// Guard: ensure raw is an array, then filter to objects with a defined .result. // Guard: ensure raw is an array, then filter to objects with a defined .result.
const manifests = Array.isArray(raw) const manifests = Array.isArray(raw)
@@ -697,12 +724,12 @@ function dedupManifests(allManifests) {
}); });
// Sort: prefer interceptor-collected > player globals > config > inline-script // Sort: prefer interceptor-collected > player globals > config > inline-script
const playerRank = { const playerRank = {
'__goon_plugin_data': 0, '__goon_plugin_data': 0,
'content-script-intercept': 0, 'content-script-intercept': 0,
'webRequest': 1, 'webRequest': 1,
'phncdn_reconstructed': 2, 'hls_reconstructed': 2,
'video._goon_hls_manifest': 3, 'video._goon_hls_manifest': 3,
'inline-script': 99, 'inline-script': 99,
}; };
unique.sort((a, b) => { unique.sort((a, b) => {
const aRank = playerRank[a.player] ?? 50; const aRank = playerRank[a.player] ?? 50;
@@ -746,26 +773,26 @@ async function queryWebRequestManifests(tabId) {
} }
/** /**
* Query the service worker for all phncdn.com requests captured by Strategy 8. * Query the service worker for all video requests captured by Strategy 8,
* Returns segments + reconstructed manifest URLs from segment patterns. * including reconstructed manifest URLs derived from segment patterns.
* @param {number} tabId * @param {number} tabId
* @returns {Promise<{segments: Array, reconstructedManifests: Array, totalRequests: number}>} * @returns {Promise<{segments: Array, reconstructedManifests: Array, totalRequests: number}>}
*/ */
async function queryPhncdnRequests(tabId) { async function queryVideoRequests(tabId) {
try { try {
const resp = await chrome.runtime.sendMessage({ const resp = await chrome.runtime.sendMessage({
type: 'GOON_GET_PHNCDN_REQUESTS', type: 'GOON_GET_VIDEO_REQUESTS',
tabId: tabId, tabId: tabId,
}); });
return resp || { segments: [], reconstructedManifests: [], totalRequests: 0 }; return resp || { segments: [], reconstructedManifests: [], totalRequests: 0 };
} catch { } catch {
return { segments: [], reconstructedManifests: [], totalRequests: 0 }; return { segments: [], reconstructedManifests: [], totalRequests: 0 };
} }
} }
/** /**
* Scrape video elements from ALL frames in a tab. * Scrape video elements from ALL frames in a tab.
* On sites like Pornhub, the video player lives inside an iframe — main-frame-only * On many video sites the player lives inside an iframe — main-frame-only
* scraping sees zero <video> elements. Running allFrames catches them. * scraping sees zero <video> elements. Running allFrames catches them.
* *
* Runs in the MAIN world so it can see page globals: * Runs in the MAIN world so it can see page globals:
@@ -778,36 +805,64 @@ async function queryPhncdnRequests(tabId) {
* @param {Array} manifests - HLS manifests found by probing * @param {Array} manifests - HLS manifests found by probing
* @returns {Promise<Object|null>} scrape result or null on error * @returns {Promise<Object|null>} scrape result or null on error
*/ */
async function scrapeTab(tabId, manifests) { /**
try { * Reduce a per-frame executeScript result array to the single largest-video
const raw = await chrome.scripting.executeScript({ * result (or a "no videos" result / null). Shared by the MAIN- and
target: { tabId, allFrames: true }, * isolated-world scrape attempts in scrapeTab.
func: scrapeFn, */
args: [manifests], function reduceScrapeFrames(raw) {
});
// When allFrames is true, destroyed / sandboxed frames may return null entries. // When allFrames is true, destroyed / sandboxed frames may return null entries.
// Guard: ensure raw is an array, then filter to objects with a defined .result. // Guard: ensure raw is an array, then filter to objects with a defined .result.
const frameResults = Array.isArray(raw) const frameResults = Array.isArray(raw)
? raw.filter(r => r != null && typeof r === 'object' && 'result' in r) ? raw.filter(r => r != null && typeof r === 'object' && 'result' in r)
.map(r => r.result) .map(r => r.result)
: []; : [];
const valid = frameResults.filter(r => r && r.videos && r.videos.length > 0); const valid = frameResults.filter(r => r && r.videos && r.videos.length > 0);
if (valid.length === 0) { if (valid.length === 0) {
const firstWithResult = frameResults.find(r => r != null); const firstWithResult = frameResults.find(r => r != null);
return firstWithResult || null; return firstWithResult || null;
} }
valid.sort((a, b) => { valid.sort((a, b) => {
const aArea = a.videos[0]?.area || 0; const aArea = a.videos[0]?.area || 0;
const bArea = b.videos[0]?.area || 0; const bArea = b.videos[0]?.area || 0;
return bArea - aArea; return bArea - aArea;
}); });
return valid[0]; return valid[0];
} catch(e) {
warn('scrapeTab: tab', tabId, 'error:', e && e.message ? e.message : e);
return null;
}
} }
async function scrapeTab(tabId, manifests) {
// Primary: MAIN world — scrapeFn can read page globals the main frame
// exposes (interceptor annotations, Hls instances, custom <video> props).
try {
const mainRaw = await chrome.scripting.executeScript({
target: { tabId, allFrames: true },
world: 'MAIN',
func: scrapeFn,
args: [manifests],
});
let result = reduceScrapeFrames(mainRaw);
// Isolated-world fallback: some pages embed sandboxed / opaque-origin ad
// iframes that make the MAIN-world injection reject, so the primary run can
// return no videos even when a <video> is visible. scrapeFn only needs the
// shared DOM plus the passed-in manifest list, so an isolated-world run (no
// `world` key — always permitted) still sees the same <video> elements.
if (!result || !(result.videos && result.videos.length > 0)) {
const isoRaw = await chrome.scripting.executeScript({
target: { tabId, allFrames: true },
func: scrapeFn,
args: [manifests],
});
const iso = reduceScrapeFrames(isoRaw);
if (iso && iso.videos && iso.videos.length > 0) result = iso;
else if (!result) result = iso;
}
return result;
} catch(e) {
warn('scrapeTab: tab', tabId, 'error:', e && e.message ? e.message : e);
return null;
}
}
/** /**
* Filter intercepted URLs to prefer actual HLS manifests over segment files. * Filter intercepted URLs to prefer actual HLS manifests over segment files.
* Interceptors capture both .m3u8 manifests and .ts segment URLs (any URL * Interceptors capture both .m3u8 manifests and .ts segment URLs (any URL
@@ -829,101 +884,76 @@ function filterManifests(manifests) {
(u.includes('/playlist') && !u.endsWith('.ts')); (u.includes('/playlist') && !u.endsWith('.ts'));
}); });
// Reject fake reconstructed manifest paths. // Reject clearly-fake manifest paths (see isValidManifestUrl).
// Real manifests are under /hls/videos/{year}/{subdir}/{videoId}/... const valid = actualManifests.filter(m => isValidManifestUrl(m.url));
// Fakes look like: phncdn.com/videos/{id}/playlist.m3u8 or .../master.m3u8 (no /hls/)
const valid = actualManifests.filter(m => {
const u = m.url.toLowerCase();
try {
const urlObj = new URL(u);
// phncdn URLs must have /hls/ in the path — fake reconstructed paths lack it
if (u.includes('phncdn') || u.includes('pornhubcdn')) {
if (!urlObj.pathname.includes('/hls/') && !u.includes('?h=')) {
return false; // Fake: phncdn URL without /hls/ or auth query param
}
}
} catch {}
return true;
});
// If we have actual (non-fake) manifests, use them directly
if (valid.length > 0) { if (valid.length > 0) {
return valid.map(m => ({ url: m.url })); return valid.map(m => ({ url: m.url }));
} }
// No .m3u8 found — interceptors only captured .ts segments. // No manifest found — interceptors only captured segment files. Reconstruct a
// Reconstruct the parent manifest URL from segment paths. // manifest URL from each segment's directory, carrying over its auth query
// Segment: https://ev-h.phncdn.com/hls/videos/202008/01/338337051/720P_4000K_338337051.mp4/seg-1-v1-a1.ts?h=tmrD...&e=1786932752&f=1 // string intact (CDNs reject a path-only URL with 403). CDN-agnostic: the
// Manifest: https://ev-h.phncdn.com/hls/videos/202008/01/338337051/720P_4000K_338337051.mp4/master.m3u8?h=tmrD...&e=1786932752&f=1 // segment filename is swapped for a manifest filename in the same directory,
// // e.g. .../variant/seg-1.ts?h=... -> .../variant/master.m3u8?h=...
// Auth tokens (h, e, f or validfrom, validto, hash) are extracted from segment query params
// and reapplied to the reconstructed manifest URL. This is critical because CDN domains
// require auth — a path-only URL will be rejected with 403.
const seen = new Set(); const seen = new Set();
const reconstructed = []; const reconstructed = [];
// A segment filename: numbered segment, or an .ts/.m4s/.aac media extension.
const SEG_RE = /(seg-\d+|\.(?:ts|m4s|aac)(?:$|\?))/i;
for (const m of manifests) { for (const m of manifests) {
const u = m.url.toLowerCase(); if (!SEG_RE.test(m.url)) continue;
if (!u.includes('/hls/videos/') || !/\/seg-\d+/.test(u)) continue;
try { try {
const urlObj = new URL(m.url); const urlObj = new URL(m.url);
// Skip non-phncdn hosts — we only reconstruct for known video CDNs
const host = urlObj.host.toLowerCase();
if (!host.includes('phncdn')) continue;
const pathParts = urlObj.pathname.split('/'); const pathParts = urlObj.pathname.split('/');
// Find the /hls/ marker and extract: hls/videos/{year}/{subdir}/{videoId}/{variant}/... // Need a directory to attach the manifest to (skip flat single-file paths).
const hlsIdx = pathParts.findIndex(p => p === 'hls'); if (pathParts.length < 3) continue;
if (hlsIdx === -1 || hlsIdx + 5 >= pathParts.length) continue; pathParts.pop(); // drop the segment filename, keep its directory
// Reconstruct with BOTH "hls" prefix AND variant directory included: const dir = pathParts.join('/');
// segment: /hls/videos/202008/01/338337051/720P_4000K_338337051.mp4/seg-1.ts // Carry the raw query (auth token) over unchanged — never re-encode it.
// manifest: /hls/videos/202008/01/338337051/720P_4000K_338337051.mp4/master.m3u8 const q = m.url.indexOf('?');
const year = pathParts[hlsIdx + 2]; // e.g. 2020 const rawQuery = q === -1 ? '' : m.url.slice(q);
const subDir = pathParts[hlsIdx + 3]; // e.g. 01 const manifestUrl = `${urlObj.protocol}//${urlObj.host}/${dir}/master.m3u8${rawQuery}`;
const videoId = pathParts[hlsIdx + 4]; // e.g. 338337051
const variant = pathParts[hlsIdx + 5]; // e.g. 720P_4000K_338337051.mp4
// Extract auth params from segment query string — different CDN variants use different keys:
// ev-h/validfrom/validto/hash vs hv-h/h/e/f
const authParts = [];
for (const [key, val] of urlObj.searchParams) {
if (['h', 'e', 'f', 'validfrom', 'validto', 'hash'].includes(key)) {
authParts.push(`${key}=${val}`);
}
}
const authStr = authParts.length > 0 ? '?' + authParts.join('&') : '';
// Build manifest URL: include hls prefix AND variant directory
const manifestUrl = `${urlObj.protocol}//${urlObj.host}/${pathParts[hlsIdx]}/${pathParts[hlsIdx + 1]}/${year}/${subDir}/${videoId}/${variant}/master.m3u8${authStr}`;
if (!seen.has(manifestUrl)) { if (!seen.has(manifestUrl)) {
seen.add(manifestUrl); seen.add(manifestUrl);
reconstructed.push({ url: manifestUrl }); reconstructed.push({ url: manifestUrl });
} }
} catch { /* skip malformed URLs */ } } catch { /* skip malformed URLs */ }
} }
return reconstructed; return reconstructed;
} }
/** /**
* Validate that a manifest URL uses a real phncdn.com path structure. * Validate that a manifest URL is well-formed and generic (CDN-agnostic).
* Rejects fake paths like /videos/{id}/playlist.m3u8 — real manifests use * Rejects malformed paths; a manifest-like URL must live in a directory (have a
* /hls/videos/{year}/{subdir}/{videoId}/master.m3u8 or similar /hls/ paths. * path separator), which filters out obviously-broken single-segment paths.
*/ */
function isValidManifestUrl(url) { function isValidManifestUrl(url) {
if (!url || typeof url !== 'string') return false; if (!url || typeof url !== 'string') return false;
// Real phncdn HLS manifest: must contain /hls/videos/ with sufficient path depth let urlObj;
const hlsIdx = url.indexOf('/hls/videos/'); try {
if (hlsIdx === -1) { urlObj = new URL(url);
// Not phncdn — could be a different CDN, allow it } catch {
if (!url.includes('phncdn') && !url.includes('pornhubcdn')) return true;
// phncdn without /hls/ prefix is invalid for manifests
return false; return false;
} }
// Check there are at least {year}/{subdir}/{videoId} after /hls/videos/ const path = urlObj.pathname.toLowerCase();
const after = url.substring(hlsIdx + '/hls/videos/'.length); // 1) Concrete media/manifest signals win outright: a real file extension or
if (after.indexOf('/') === -1) return false; // need at least one more segment // /hls/ path is fetchable, even with a trailing slash
// or a token-bearing query — so check these before the
// template heuristics and never let a coincidental '%'/'/' sink a source.
if (path.includes('.m3u8') || path.includes('.hlsvod') || path.includes('/hls/') ||
path.includes('/manifest') || path.includes('.mp4') || path.includes('.webm') ||
path.includes('.ogv') || path.includes('.mpd') || path.includes('.m4s')) return true;
// 2) Dynamic route templates / nav links: a `%` that isn't a valid %-encoded
// hex pair (real %2B etc. are kept) or a {name} brace token.
if (/%(?!([0-9A-Fa-f]{2}))/i.test(url) || /\{[a-z0-9_]+\}/i.test(url)) return false;
// 3) Plural collection paths and trailing-slash directory listings are nav
// menus, not manifest files (e.g. /playlists/, /playlists/liked/).
if (path.includes('/playlists') || path.includes('/manifests') || path.endsWith('/')) return false;
// 4) A singular /playlist endpoint may be a real (extensionless) manifest;
// allow it when it sits in a directory.
if (path.includes('/playlist')) return path.includes('/');
// 5) Otherwise treat as a direct media source — allow it.
return true; return true;
} }
/** /**
* Build a normalized video entry from a scraped video object. * Build a normalized video entry from a scraped video object.
@@ -941,8 +971,8 @@ function makeVideoEntry(video, tabTitle, posterFromContent) {
mediaType: video.mediaType || 'video', mediaType: video.mediaType || 'video',
}; };
if (video.hlsManifest) { if (video.hlsManifest) {
// Reject fake phncdn manifest paths before storing // Reject fake/invalid manifest paths before storing
if (!isValidManifestUrl(video.hlsManifest)) { if (!isValidManifestUrl(video.hlsManifest)) {
console.warn('[goon] Rejecting fake manifest URL:', video.hlsManifest.substring(0, 120)); console.warn('[goon] Rejecting fake manifest URL:', video.hlsManifest.substring(0, 120));
} else { } else {
entry.hlsManifest = video.hlsManifest; entry.hlsManifest = video.hlsManifest;
@@ -1004,28 +1034,28 @@ async function scanVideos(tabsToScan) {
} }
await logToPage(`step1.5 (webRequest): found ${totalWebRequestManifests} manifests via network interception`); await logToPage(`step1.5 (webRequest): found ${totalWebRequestManifests} manifests via network interception`);
// Step 1.6: Strategy 8 — Query phncdn.com requests and reconstruct manifest URLs. // Step 1.6: Strategy 8 — Query captured video requests and reconstruct manifest URLs.
// When the player uses in-memory dynamic HLS (zero .m3u8 network requests), we can // When the player uses in-memory dynamic HLS (zero .m3u8 network requests), we can
// still detect it by recognizing segment URL patterns from phncdn.com and deriving // still detect it by recognizing segment URL patterns and deriving the parent
// the parent manifest URL: /hls/videos/{id}/master.m3u8 // manifest URL from the segment's directory.
const phncdnPromises = tabsToScan.map(t => queryPhncdnRequests(t.id)); const videoRequestPromises = tabsToScan.map(t => queryVideoRequests(t.id));
const phncdnResults = await Promise.all(phncdnPromises); const videoRequestResults = await Promise.all(videoRequestPromises);
let totalPhncdnManifests = 0; let totalReconstructedManifests = 0;
for (let i = 0; i < tabsToScan.length; i++) { for (let i = 0; i < tabsToScan.length; i++) {
const reconstructed = phncdnResults[i]?.reconstructedManifests || []; const reconstructed = videoRequestResults[i]?.reconstructedManifests || [];
if (reconstructed.length > 0) { if (reconstructed.length > 0) {
log('scanVideos phncdn: tab', tabsToScan[i].id, 'found', reconstructed.length, 'reconstructed manifests from segment URLs'); log('scanVideos strategy8: tab', tabsToScan[i].id, 'found', reconstructed.length, 'reconstructed manifests from segment URLs');
} }
totalPhncdnManifests += reconstructed.length; totalReconstructedManifests += reconstructed.length;
const existing = tabManifestMap.get(tabsToScan[i].id) || []; const existing = tabManifestMap.get(tabsToScan[i].id) || [];
for (const url of reconstructed) { for (const url of reconstructed) {
if (!existing.some(m => m.url === url)) { if (!existing.some(m => m.url === url)) {
existing.push({ url, player: 'phncdn_reconstructed' }); existing.push({ url, player: 'hls_reconstructed' });
}
} }
}
tabManifestMap.set(tabsToScan[i].id, existing); tabManifestMap.set(tabsToScan[i].id, existing);
} }
await logToPage(`step1.6 (phncdn): found ${totalPhncdnManifests} reconstructed manifests from segment URLs`); await logToPage(`step1.6 (strategy8): found ${totalReconstructedManifests} reconstructed manifests from segment URLs`);
// Step 2: Scrape video elements (all frames) AND query content scripts (in parallel) // Step 2: Scrape video elements (all frames) AND query content scripts (in parallel)
const scrapePromises = tabsToScan.map(t => const scrapePromises = tabsToScan.map(t =>
@@ -1128,6 +1158,7 @@ async function scanVideos(tabsToScan) {
} }
const items = [...seen.values()]; const items = [...seen.values()];
await attachReferers(items);
const skipped = scrapeResults.filter(r => r === null).length; const skipped = scrapeResults.filter(r => r === null).length;
log('scanVideos complete: scanned=', tabResults.length, 'skipped=', skipped, 'unique items=', items.length); log('scanVideos complete: scanned=', tabResults.length, 'skipped=', skipped, 'unique items=', items.length);
@@ -1138,6 +1169,31 @@ async function scanVideos(tabsToScan) {
return { items, scanned: tabResults.length, skipped }; return { items, scanned: tabResults.length, skipped };
} }
/**
* Bind the provider referer the SW captured (domain-aware) to each item so it
* survives the watch-later flow and covers the sibling edge subdomains a segment
* may resolve to. Best-effort: playback still falls back to the SW's live store.
*/
async function attachReferers(items) {
const hostOf = (url) => { try { return new URL(url).host.toLowerCase(); } catch { return ''; } };
const hosts = [];
for (const it of items) {
const host = hostOf(it.hlsManifest || it.src || '');
if (host) hosts.push(host);
}
if (hosts.length === 0) return;
let referers = {};
try {
const resp = await chrome.runtime.sendMessage({ type: 'GOON_GET_REFERERS', hosts });
if (resp && resp.referers) referers = resp.referers;
} catch { /* referer is best-effort; the SW still resolves it at play time */ }
if (!Object.keys(referers).length) return;
for (const it of items) {
const host = hostOf(it.hlsManifest || it.src || '');
if (host && referers[host]) it.referer = referers[host];
}
}
/** Store video entries in chrome.storage.local. */ /** Store video entries in chrome.storage.local. */
async function storeVideos(items) { async function storeVideos(items) {
const stored = items.map(v => ({ const stored = items.map(v => ({
@@ -1146,6 +1202,7 @@ async function storeVideos(items) {
tab: v.tab, tab: v.tab,
mediaType: v.mediaType || 'video', mediaType: v.mediaType || 'video',
hlsManifest: v.hlsManifest || undefined, hlsManifest: v.hlsManifest || undefined,
referer: v.referer || undefined,
})); }));
await chrome.storage.local.set({ await chrome.storage.local.set({
[STORAGE_KEY]: stored, [STORAGE_KEY]: stored,
@@ -1186,6 +1243,7 @@ findBtn.addEventListener('click', async () => {
try { try {
await chrome.scripting.executeScript({ await chrome.scripting.executeScript({
target: { tabId: activeTab.id }, target: { tabId: activeTab.id },
world: 'MAIN',
func: () => { func: () => {
const diag = []; const diag = [];
// Check interceptor data // Check interceptor data
+5
View File
@@ -33,6 +33,11 @@
"type": "string", "type": "string",
"enum": ["direct", "hls"], "enum": ["direct", "hls"],
"description": "Media delivery format. Defaults to 'direct' when omitted. Set to 'hls' for HTTP Live Streaming." "description": "Media delivery format. Defaults to 'direct' when omitted. Set to 'hls' for HTTP Live Streaming."
},
"referer": {
"type": ["string", "null"],
"format": "uri",
"description": "Provider Referer captured at scan time for the manifest's CDN; the proxy sends it so a referer-protecting CDN accepts sibling edge subdomains. Optional."
} }
}, },
"additionalProperties": false, "additionalProperties": false,
+159 -100
View File
@@ -19,10 +19,10 @@
* overlays and an aggregate status bar. * overlays and an aggregate status bar.
*/ */
// HLS is vendored locally to avoid CSP blocks on sites like Pornhub that // HLS is vendored locally to avoid CSP blocks on sites that restrict
// restrict script-src to 'self'. The hls.min.js bundle is built by Vite // script-src to 'self'. The hls.js bundle is copied (not minified) so it
// and exposed via chrome.runtime.getURL() so it loads from the extension // loads from the extension origin via chrome.runtime.getURL(), bypassing
// origin, bypassing page CSP entirely. // page CSP entirely.
/** /**
* Grid-optimized hls.js configuration. * Grid-optimized hls.js configuration.
@@ -54,14 +54,15 @@ const panelsEl = document.getElementById('panels');
/** Track all Hls instances so we can destroy them on unload. */ /** Track all Hls instances so we can destroy them on unload. */
/** /**
* Custom HLS loader that proxies phncdn.com URLs through the service worker * Custom HLS loader that proxies URLs whose host blocks extension-origin
* (bypassing CORS/Referer restrictions) and fetches everything else natively. * requests (via a learned proxy-host set) through the service worker
* * (bypassing CORS/Referer restrictions) and fetches everything else natively.
* hls.js creates one loader instance per request via `new HlsLoader(hls)`, then *
* calls `loader.load(context, config, callbacks)` — callbacks are the 3rd arg * hls.js creates one loader instance per request via `new HlsLoader(hls)`, then
* in v9 (some wrapper paths put them 2nd, so we scan each argument). The stats * calls `loader.load(context, config, callbacks)` — callbacks are the 3rd arg
* object passed to the callbacks is pre-allocated by us and mutated by hls.js * in v9 (some wrapper paths put them 2nd, so we scan each argument). The stats
* object passed to the callbacks is pre-allocated by us and mutated by hls.js
* in place, so it must have the correct sub-shape (see createLoadedStats). * in place, so it must have the correct sub-shape (see createLoadedStats).
*/ */
function HlsLoader(config) { function HlsLoader(config) {
@@ -134,8 +135,8 @@ const panelsEl = document.getElementById('panels');
const stats = this.stats; const stats = this.stats;
stats.loading.start = trequest; stats.loading.start = trequest;
const isManifest = /\.m3u8($|\?)/i.test(url || ''); const isManifest = /\.m3u8($|\?)/i.test(url || '');
const needProxy = (url || '').includes('phncdn') || (url || '').includes('pornhubcdn'); const needProxy = needsProxy(url);
// hls.js loader callback contract (verified against the bundled loader): // hls.js loader callback contract (verified against the bundled loader):
// onSuccess(response, stats, context, networkDetails) // onSuccess(response, stats, context, networkDetails)
@@ -176,8 +177,8 @@ const panelsEl = document.getElementById('panels');
if (needProxy) { if (needProxy) {
// Route through the service worker to bypass the CDN's CORS/Referer // Route through the service worker to bypass the CDN's CORS/Referer
// restrictions. The SW adds the required Referer and returns base64. // restrictions. The SW adds the required Referer and returns base64.
console.log('[goon-viewer] Proxying via SW:', (url || '').substring(0, 120)); console.log('[goon-viewer] Proxying via SW:', (url || '').substring(0, 120));
chrome.runtime.sendMessage({ type: 'GOON_PROXY_FETCH', url }, (resp) => { chrome.runtime.sendMessage({ type: 'GOON_PROXY_FETCH', url, referer: refererForUrl(url) }, (resp) => {
if (self._aborted) return; if (self._aborted) return;
const lastErr = chrome.runtime.lastError; const lastErr = chrome.runtime.lastError;
if (lastErr || !resp || !resp.ok) { if (lastErr || !resp || !resp.ok) {
@@ -233,52 +234,93 @@ const panelsEl = document.getElementById('panels');
HlsLoader.prototype.destroy = function () { this.abort(); }; HlsLoader.prototype.destroy = function () { this.abort(); };
HlsLoader.canSupportLoader = function () { return true; }; HlsLoader.canSupportLoader = function () { return true; };
// === CORS diagnostics — check if phncdn.com allows extension-origin requests === // === Service-worker proxy helper (CDN-agnostic) ===
(async function checkCors() { // Proxies a video URL through the service worker (no CORS restrictions). The
// SW forces the correct provider-origin Referer for the host (captured by the
// background worker), so a referer-protecting CDN accepts the request.
// Returns an ArrayBuffer of the response content.
async function proxyFetch(url, referer) {
try { try {
const resp = await fetch('https://phncdn.com/images/favicon.ico', { method: 'HEAD', mode: 'cors' }); const resp = await chrome.runtime.sendMessage({ type: 'GOON_PROXY_FETCH', url, referer });
console.log('[goon-viewer] CORS check phncdn.com:', resp.ok ? 'ALLOWED (status ' + resp.status + ')' : 'BLOCKED');
} catch(e) {
console.warn('[goon-viewer] CORS check phncdn.com: BLOCKED —', e && e.message ? e.message : e);
}
})();
// === HLS segment proxy helper ===
// Proxies a video URL through the service worker (no CORS restrictions).
// Returns an ArrayBuffer of the response content.
async function proxyFetch(url) {
try {
const resp = await chrome.runtime.sendMessage({ type: 'GOON_PROXY_FETCH', url });
if (resp && resp.data && resp.ok) { if (resp && resp.data && resp.ok) {
// Decode base64 → ArrayBuffer // Decode base64 → ArrayBuffer
const binary = atob(resp.data); const binary = atob(resp.data);
const bytes = new Uint8Array(binary.length); const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) { for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i); bytes[i] = binary.charCodeAt(i);
} }
return bytes.buffer; return bytes.buffer;
} }
return null; return null;
} catch(e) { } catch(e) {
console.warn('[goon-viewer] proxyFetch failed for:', url.substring(0, 120), e && e.message ? e.message : e); console.warn('[goon-viewer] proxyFetch failed for:', url.substring(0, 120), e && e.message ? e.message : e);
return null; return null;
} }
} }
// === Check if any manifest URLs require proxying (phncdn.com / cdn) === // CDN hosts that block extension-origin requests are learned by a CORS probe
function urlsNeedProxy(urls) { // at viewer startup (hosts that fail the probe are recorded), then stored in
return urls.some(u => u && typeof u === 'string' && (u.includes('phncdn') || u.includes('pornhubcdn'))); // chrome.storage.local. The proxy is used only for those hosts, so we never
} // hardcode a particular CDN.
const PROXY_HOSTS_KEY = 'goon_proxy_hosts';
const proxyHosts = new Set();
// Provider referers bound to a stored entry at scan time, keyed by the entry's
// manifest registrable domain. A single provider referer covers every edge
// subdomain, so a segment on a sibling subhost resolves to the same referer.
const refererByDomain = new Map();
(async () => {
try {
const stored = await chrome.storage.local.get(PROXY_HOSTS_KEY);
const hosts = stored[PROXY_HOSTS_KEY];
if (Array.isArray(hosts)) for (const h of hosts) proxyHosts.add(registrableDomainOf(String(h).toLowerCase()));
} catch { /* ignore */ }
})();
/** Check whether fetching a URL from the extension page works (CORS test). */ // Registrable domain of a CDN host — the label a single provider referer
async function corsCheck(url) { // applies to (a referer-protecting CDN serves from many edge subdomains that
// share one referer). Last-two-labels heuristic; mirrors the SW's copy.
function registrableDomainOf(host) {
if (!host) return '';
const parts = host.split('.');
if (parts.length < 2) return host;
return parts.slice(-2).join('.');
}
// The provider referer to send for a proxied request: the referer bound to the
// entry at scan time (domain-keyed), or '' when none was captured. No self-origin
// fallback — a self-origin referer is guaranteed-wrong for hotlink protection.
function refererForUrl(url) {
const ref = refererByDomain.get(registrableDomainOf(hostOfUrl(url)));
return ref || '';
}
// Whether a given video URL should be proxied: its host is a known
// referer-protected CDN host.
function needsProxy(url) {
if (!url || typeof url !== 'string') return false;
const host = hostOfUrl(url);
return host ? proxyHosts.has(registrableDomainOf(host)) : false;
}
// host of a URL (lowercased); '' when malformed.
function hostOfUrl(url) {
try { return new URL(url).host.toLowerCase(); } catch { return ''; }
}
// Whether any of the given URLs needs proxying.
function urlsNeedProxy(urls) {
return urls.some(u => needsProxy(u));
}
/** Check whether fetching a URL from the extension page works (CORS test). */
async function corsCheck(url) {
try { try {
const resp = await fetch(url, { method: 'HEAD', mode: 'cors' }); const resp = await fetch(url, { method: 'HEAD', mode: 'cors' });
return resp.ok; return resp.ok;
} catch { } catch {
return false; return false;
} }
} }
/** Track all Hls instances so we can destroy them on unload. */ /** Track all Hls instances so we can destroy them on unload. */
const hlsInstances = new Map(); const hlsInstances = new Map();
@@ -750,10 +792,10 @@ function wireHlsEvents(hls, video, panel) {
renderPanelState(video, panel); renderPanelState(video, panel);
hls.destroy(); hls.destroy();
hlsInstances.delete(video); hlsInstances.delete(video);
// A fatal network error on a phncdn stream is almost always an expired // A fatal network error on a token-protected stream is almost always an
// auth token. The live source tab keeps rotating tokens, so pull a fresh // expired auth token. The live source tab keeps rotating tokens, so pull a
// manifest from it and retry — capped at a few attempts per panel so a // fresh manifest from it and retry — capped at a few attempts per panel so a
// tab with no live source doesn't loop forever. // tab with no live source doesn't loop forever.
if ((video.dataset.hlsRescans || '0') | 0 < 3) { if ((video.dataset.hlsRescans || '0') | 0 < 3) {
video.dataset.hlsRescans = ((video.dataset.hlsRescans || '0') | 0) + 1; video.dataset.hlsRescans = ((video.dataset.hlsRescans || '0') | 0) + 1;
console.log('[goon-viewer] fatal NETWORK_ERROR → auto-rescan attempt #' + video.dataset.hlsRescans); console.log('[goon-viewer] fatal NETWORK_ERROR → auto-rescan attempt #' + video.dataset.hlsRescans);
@@ -930,12 +972,12 @@ function playHls(video, manifestUrl, panel) {
return; return;
} }
// Check whether this manifest URL needs proxying (phncdn.com without CORS headers // Check whether this manifest URL needs proxying (a referer-protected host
// blocks extension-origin requests from the viewer page). // without CORS headers blocks extension-origin requests from the viewer page).
const needProxy = manifestUrl.includes('phncdn') || manifestUrl.includes('pornhubcdn'); const needProxy = needsProxy(manifestUrl);
if (needProxy) { if (needProxy) {
console.log('[goon-viewer] Manifest needs proxying via service worker'); console.log('[goon-viewer] Manifest needs proxying via service worker');
} }
destroyHls(video); destroyHls(video);
@@ -977,9 +1019,9 @@ function playHls(video, manifestUrl, panel) {
// Log the manifest URL being loaded for debugging (helps diagnose playback failures). // Log the manifest URL being loaded for debugging (helps diagnose playback failures).
console.log(`[goon-viewer] Loading HLS manifest:`, manifestUrl.substring(0, 200)); console.log(`[goon-viewer] Loading HLS manifest:`, manifestUrl.substring(0, 200));
// Remember the manifest actually loaded so a later auto-rescan can retry from // Remember the manifest actually loaded so a later auto-rescan can retry from
// the freshest token on the same video (extractVideoId keys off this). // the freshest token on the same video (the service worker keys off the path).
video.dataset.hlsManifest = manifestUrl; video.dataset.hlsManifest = manifestUrl;
hls.loadSource(manifestUrl); hls.loadSource(manifestUrl);
hls.attachMedia(video); hls.attachMedia(video);
@@ -1017,32 +1059,32 @@ function loadHlsJs() {
}); });
} }
/* ======================================================================== */ /* ======================================================================== */
/* Fresh-token resolution — replace expired/revoked phncdn URLs */ /* Fresh-token resolution — replace expired/revoked token URLs */
/* ======================================================================== */ /* ======================================================================== */
/** /**
* Ask the service worker for the freshest live phncdn manifest for the video * Ask the service worker for the freshest live manifest for the video encoded
* encoded in `storedUrl`. The live source tab keeps rotating short-lived * in `storedUrl`. The live source tab keeps rotating short-lived auth tokens
* phncdn tokens (a stored one may already be 410 Gone), so the freshest * (a stored one may already be 410 Gone), so the freshest captured manifest —
* captured manifest — identified by matching the videoId in the path — is the * identified by matching the video's group key in the path — is the one that
* one that will actually validate. Resolves to the new URL, or null when the * will actually validate. Resolves to the new URL, or null when the tab
* tab captured nothing (or the caller can't reach the SW). * captured nothing (or the caller can't reach the SW).
*/ */
async function resolveFreshManifest(storedUrl) { async function resolveFreshManifest(storedUrl) {
if (!storedUrl || !storedUrl.includes('phncdn')) return null; if (!storedUrl || !needsProxy(storedUrl)) return null;
try { try {
const resp = await chrome.runtime.sendMessage({ type: 'GOON_RESOLVE_MANIFEST', url: storedUrl }); const resp = await chrome.runtime.sendMessage({ type: 'GOON_RESOLVE_MANIFEST', url: storedUrl });
if (resp && resp.ok && resp.changed && resp.fresh) { if (resp && resp.ok && resp.changed && resp.fresh) {
console.log('[goon-viewer] resolved fresh manifest for', (resp.videoId || '?'), console.log('[goon-viewer] resolved fresh manifest for', (resp.groupKey || '?'),
'→', (resp.fresh || '').substring(0, 100)); '→', (resp.fresh || '').substring(0, 100));
return resp.fresh; return resp.fresh;
} }
} catch (e) { } catch (e) {
console.warn('[goon-viewer] resolveFreshManifest failed:', e && e.message ? e.message : e); console.warn('[goon-viewer] resolveFreshManifest failed:', e && e.message ? e.message : e);
}
return null;
} }
return null;
}
/** /**
* Re-attempt playback on `video` after its stored manifest died (fatal * Re-attempt playback on `video` after its stored manifest died (fatal
@@ -1154,16 +1196,33 @@ async function init() {
}); });
} }
// Test CORS on all video URLs // Probe CORS on each video URL. Hosts that block extension-origin
const urlsToTest = videoUrls.map(v => v.src || v.hlsManifest || 'none').filter(u => u !== 'none' && typeof u === 'string'); // requests are learned as referer-protected and persisted so the viewer
for (const url of [...new Set(urlsToTest)]) { // routes them through the service-worker proxy.
const corsOk = await corsCheck(url); // Bind each entry's scan-time referer to its manifest's registrable
console.log('[goon-viewer] CORS', corsOk ? 'OK' : 'BLOCKED:', url.substring(0, 120)); // domain, so the proxy sends it for every edge subdomain the provider
} // serves from (segments often resolve to a sibling subhost).
for (const entry of videoUrls) {
const ref = entry && entry.referer;
if (!ref) continue;
const dom = registrableDomainOf(hostOfUrl(entry.src || entry.hlsManifest || ''));
if (dom) refererByDomain.set(dom, ref);
}
const urlsToTest = videoUrls.map(v => v.src || v.hlsManifest || 'none').filter(u => u !== 'none' && typeof u === 'string');
const blockedHosts = new Set();
for (const url of [...new Set(urlsToTest)]) {
const corsOk = await corsCheck(url);
if (!corsOk) blockedHosts.add(registrableDomainOf(hostOfUrl(url)));
console.log('[goon-viewer] CORS', corsOk ? 'OK' : 'BLOCKED:', url.substring(0, 120));
}
if (blockedHosts.size > 0) {
for (const h of blockedHosts) if (h) proxyHosts.add(h);
try { await chrome.storage.local.set({ [PROXY_HOSTS_KEY]: [...proxyHosts] }); } catch { /* ignore */ }
}
// Check if any URLs point to phncdn.com (proxy will be needed if CORS blocked) // Report whether any URL needs proxying (referer-protected host).
const needsProxy = urlsToTest.some(u => u.includes('phncdn') || u.includes('pornhubcdn')); const anyNeedsProxy = urlsNeedProxy(urlsToTest);
console.log('[goon-viewer] Proxy required:', needsProxy); console.log('[goon-viewer] Proxy required:', anyNeedsProxy);
} else { } else {
console.warn('[goon-viewer] No video entries in storage for ID:', id, '— data may have been cleared or never saved'); console.warn('[goon-viewer] No video entries in storage for ID:', id, '— data may have been cleared or never saved');
} }
@@ -1194,15 +1253,15 @@ async function init() {
} }
return; return;
} }
// Before playback, swap each stored phncdn manifest for the freshest live // Before playback, swap each referer-protected manifest for the freshest
// one the source tab has captured (a stored token may already be 410 // live one the source tab has captured (a stored token may already be 410
// Gone). Non-phncdn manifests are left untouched. // Gone). Non-protected manifests are left untouched.
for (const { video, manifest, panel } of hlsEntries) { for (const { video, manifest } of hlsEntries) {
if (manifest.includes('phncdn')) { if (needsProxy(manifest)) {
const fresh = await resolveFreshManifest(manifest); const fresh = await resolveFreshManifest(manifest);
if (fresh) video.dataset.hlsManifest = fresh; if (fresh) video.dataset.hlsManifest = fresh;
} }
} }
for (const { video, panel } of hlsEntries) { for (const { video, panel } of hlsEntries) {
playHls(video, video.dataset.hlsManifest, panel); playHls(video, video.dataset.hlsManifest, panel);
} }