Add grok.com; fix fetch hook on React/Next.js sites (Reddit, Grok, etc.)

Two fixes:

1. grok.com missing from manifest
   Grok moved from grok.x.ai to grok.com. The extension wasn't activating
   on grok.com at all (no content script injected). Added to host_permissions
   and content_scripts in both manifests.

2. Fetch hook bypassed on React/Next.js sites
   content.js was injected async (after awaiting the document-scanner),
   so by the time it replaced window.fetch, React/Next.js had already stored
   a reference to the original. All fetch calls from framework code bypassed
   the hook entirely.

   Fix: load early-hook.js as a "world": "MAIN" content script at
   document_start. This runs synchronously before any page JavaScript,
   captures the real fetch in window.__ssOriginalFetch, and replaces
   window.fetch with a lightweight wrapper. When content.js eventually
   loads, it sets window.__ssInterceptFetch (the real substitution logic)
   and window.__ssReady = true, activating the wrapper. React's stored
   fetch reference now routes through the interceptor.

   Falls back to direct window.fetch replacement if early-hook.js somehow
   didn't run (e.g. older browser without world: MAIN support).

https://claude.ai/code/session_01CwcZK8nqL8pyBH9AxDs9qo
This commit is contained in:
Claude
2026-04-01 16:07:01 +00:00
parent 9af92a24aa
commit ed92b07703
3 changed files with 78 additions and 4 deletions
+32 -1
View File
@@ -25,6 +25,7 @@
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
@@ -53,6 +54,36 @@
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
"https://www.perplexity.ai/*",
"https://copilot.microsoft.com/*",
"https://chat.deepseek.com/*",
"https://huggingface.co/chat/*",
"https://poe.com/*",
"https://github.com/*",
"https://gitlab.com/*",
"https://www.reddit.com/*",
"https://old.reddit.com/*",
"https://stackoverflow.com/*",
"https://pastebin.com/*",
"http://localhost/*",
"http://127.0.0.1/*"
],
"js": ["src/content/early-hook.js"],
"run_at": "document_start",
"world": "MAIN",
"all_frames": true
},
{
"matches": [
"https://claude.ai/*",
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
@@ -114,7 +145,7 @@
],
"web_accessible_resources": [
{
"resources": ["src/content/content.js", "src/content/early-hook.js", "src/lib/document-scanner.js"],
"resources": ["src/content/content.js", "src/lib/document-scanner.js"],
"matches": ["<all_urls>"]
}
]
+32 -1
View File
@@ -15,6 +15,7 @@
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
@@ -43,6 +44,36 @@
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
"https://www.perplexity.ai/*",
"https://copilot.microsoft.com/*",
"https://chat.deepseek.com/*",
"https://huggingface.co/chat/*",
"https://poe.com/*",
"https://github.com/*",
"https://gitlab.com/*",
"https://www.reddit.com/*",
"https://old.reddit.com/*",
"https://stackoverflow.com/*",
"https://pastebin.com/*",
"http://localhost/*",
"http://127.0.0.1/*"
],
"js": ["src/content/early-hook.js"],
"run_at": "document_start",
"world": "MAIN",
"all_frames": true
},
{
"matches": [
"https://claude.ai/*",
"https://chatgpt.com/*",
"https://chat.openai.com/*",
"https://copilot.microsoft.com/*",
"https://grok.com/*",
"https://grok.x.ai/*",
"https://x.com/i/grok*",
"https://gemini.google.com/*",
@@ -102,7 +133,7 @@
],
"web_accessible_resources": [
{
"resources": ["src/content/content.js", "src/content/early-hook.js", "src/lib/document-scanner.js"],
"resources": ["src/content/content.js", "src/lib/document-scanner.js"],
"matches": ["<all_urls>"]
}
]
+14 -2
View File
@@ -789,7 +789,9 @@
// Fetch Interception — scans ALL POST requests with a body.
// Service-agnostic: doesn't depend on URL patterns.
// ============================================================
const originalFetch = window.fetch;
// Use the real fetch captured by early-hook.js (before React stored its reference),
// or fall back to whatever window.fetch is now if early-hook.js didn't run.
const originalFetch = window.__ssOriginalFetch || window.fetch;
// URLs to never touch (static assets, analytics, etc.)
const SKIP_URL_PATTERNS = [
@@ -805,7 +807,7 @@
return SKIP_URL_PATTERNS.some(p => p.test(url));
}
window.fetch = async function (url, options) {
window.__ssInterceptFetch = async function (url, options) {
if (!settings.enabled || !hasSubstitutions()) {
return originalFetch.call(this, url, options);
}
@@ -869,6 +871,16 @@
return originalFetch.call(this, url, options);
};
// Activate the fetch hook. If early-hook.js ran first (world: MAIN, document_start),
// it already replaced window.fetch with a wrapper that forwards to __ssInterceptFetch
// once __ssReady is true — this catches React/Next.js fetch references stored before
// content.js loaded. Otherwise fall back to replacing window.fetch directly.
if (window.__ssOriginalFetch) {
window.__ssReady = true;
} else {
window.fetch = window.__ssInterceptFetch;
}
// ============================================================
// XMLHttpRequest Interception — same aggressive approach
// ============================================================