is a classic Cross-Origin Frame Access issue. It happens because your injected iframe's src
points to a different origin (e.g. https://www.nytimes.com
), and the browser’s same-origin policy forbids scripts on the parent page from accessing the iframe's DOM.
If you inject an <iframe>
into a page like https://www.nytimes.com
and set its src
to any URL from a different origin, your extension’s content script can’t directly access or modify the iframe's document.
Browsers enforce same-origin policy to prevent malicious scripts from reading or manipulating cross-origin iframe content.
Option 1: Use an iframe with a source from your extension (same origin)
src
to an external website (like https://www.nytimes.com
), create an HTML file inside your extension (e.g. panel.html
) and set the iframe’s src
to that file using the extension URL:js
iframe.src = chrome.runtime.getURL("panel.html");
Option 2: Communicate via postMessage between iframe and content script
If you absolutely need to load an external page inside the iframe (or cross-origin page), you cannot directly access its DOM.
Instead, use window.postMessage to send messages between the parent and iframe if the iframe supports it (requires cooperation from iframe page).
This generally doesn’t work with external sites like nytimes.com
unless you control the iframe content.
Option 3: Build your UI fully inside your extension
Instead of embedding an external site, create your entire UI (your Wordle panel) inside the extension iframe.
Populate it dynamically from your extension scripts, then inject the iframe into the page.
js
// Instead of this:// iframe.src = "https://www.nytimes.com"; // Cross-origin iframe, blocked access // Do this: iframe.src = chrome.runtime.getURL("panel.html"); // Your extension page iframe.addEventListener("load", () => { const panelDoc = iframe.contentDocument || iframe.contentWindow.document; // Now safe to modify DOM });
"panel.html"
is declared in your manifest.json
under web_accessible_resources
:json
"web_accessible_resources": [ { "resources": ["panel.html"], "matches": ["<all_urls>"] } ]
Use contentWindow
only after the iframe is loaded (load
event).
If you want to pass data (like user stats) into the iframe, you can either:
Inject the data via query parameters in the URL and let the iframe script parse them.
Use iframe.contentWindow.postMessage()
to send data after the iframe loads.
Or have the iframe pull data from chrome.storage
or background script.