44

I'm doing a plugin to do some transformations to the interface. I keep getting unsafe javascript attempt to access frame with url.... Domains, protocols and ports must match (typical cross site issue)

But being an extension it should have access to the iframe's content http://code.google.com/chrome/extensions/content_scripts.html ...

Doesn anyone know how to access it's contents so they can be capturable?

fmsf
  • 36,317
  • 49
  • 147
  • 195
  • 3
    What exactly do you want to do? In most cases, you can add `"all_frames":true` to the `"content_scripts"` section of your manifest file, and code the page-specific logic in your content script. – Rob W Jul 04 '12 at 09:06
  • 1
    @RobW adding all_frames:true will make the content_script run once in all the inner frames. This will complicate the logic of it a lot. I just want to access the content of the inner frames through the content_script. (like: content_script --> target_page --> inner_content) – fmsf Jul 04 '12 at 09:26
  • That's not possible: A content script cannot access any of the page's `window` object (including frames). Inject a content script with `"all_frames": false`, in which you set a flag. Then, inject a script with `all_frames": true`, where the existence of this flag is checked. If the flag does not exist, assume that the content script is running in a frame. You can then apply frame-specific logic to it, and use message passing to pass any acquired data to the main content script. – Rob W Jul 04 '12 at 09:34
  • mmm ok thanks :p but you could had added that in an answer to mark it, I was already thinking on doing it like you said but was hoping that direct access existed – fmsf Jul 04 '12 at 09:53
  • I was not sure what you wanted. For deeply nested iframes, the suggested solution would not work. I'll post a concise example. – Rob W Jul 04 '12 at 09:54
  • @robW Thanks heaps, that helped me for what I was trying to do. Thanks heaps. – John Ballinger Jul 04 '12 at 10:41

2 Answers2

54

There's generally no direct way of accessing a different-origin window object. If you want to securely communicate between content scripts in different frames, you have to send a message to the background page which in turn sends the message back to the tab.

Here is an example:

Part of manifest.json:

"background": {"scripts":["bg.js"]},
"content_scripts": [
    {"js": ["main.js"], "matches": ["<all_urls>"]},
    {"js": ["sub.js"], "matches": ["<all_urls>"], "all_frames":true}
]

main.js:

var isTop = true;
chrome.runtime.onMessage.addListener(function(details) {
    alert('Message from frame: ' + details.data);
});

sub.js:

if (!window.isTop) { // true  or  undefined
    // do something...
    var data = 'test';
    // Send message to top frame, for example:
    chrome.runtime.sendMessage({sendBack:true, data:data});
}

Background script 'bg.js':

chrome.runtime.onMessage.addListener(function(message, sender) {
    if (message.sendBack) {
        chrome.tabs.sendMessage(sender.tab.id, message.data);
    }
});

An alternative method is to use chrome.tabs.executeScript in bg.js to trigger a function in the main content script.

Relevant documentation

Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • 3
    Your statement "There's no way to access any of the window objects of a page." doesn't seem to be completely accurate. I just created a basic extension and I am able to access window.frames and get full DOM access to frames on the same origin. Perhaps this has changed since you wrote your answer? It seems perhaps this is still in development and is not fully documented. See: https://code.google.com/p/chromium/issues/detail?id=20773 – kzahel Aug 18 '13 at 17:42
  • 1
    @kzahel At the time of writing, `window.frames[i]` for all `i`s was undefined. This has indeed been fixed for a while. However, the Same origin policy still applies, so the answer is still valid, at least for the asked question (where the OP was encountering problems with cross-domain frame access). – Rob W Aug 18 '13 at 22:34
  • @stt Thanks for [bringing the fault in my answer to my attention](https://stackoverflow.com/review/suggested-edits/6295235). It was rejected by the reviewers, but I have edited my answer anyway. – Rob W Nov 22 '14 at 21:35
  • I need to access a variable defined by the page my content script is running on; how? – theonlygusti Feb 23 '15 at 19:05
  • @theonlygusti See http://stackoverflow.com/questions/9602022/chrome-extension-retrieving-gmails-original-message. – Rob W Feb 23 '15 at 19:45
  • @RobW Thanks, I've figured it out :) – theonlygusti Feb 23 '15 at 19:57
  • On bg.js, I've put "message" instead of "message.data" in order to make it work. – Mr Guliarte Sep 15 '15 at 22:19
  • @RobW, thank you very much, your answer is very useful. – Kostiantyn Ivashchenko May 01 '20 at 20:18
18

I understand that this is an old question but I recently spent half a day in order to solve it. Usually creating of a iframe looks something like that:

var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL('iframe-content-page.html');

This frame will have different origin with a page and you will not be able to obtain its DOM. But if you create iframe just for css isolation you can do this in another way:

var iframe = document.createElement('iframe');
document.getElementById("iframe-parent").appendChild(iframe);
iframe.contentDocument.write(getFrameHtml('html/iframe-content-page.html'));
.......
function getFrameHtml(htmlFileName) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", chrome.extension.getURL(html/htmlFileName), false);
    xmlhttp.send();

    return xmlhttp.responseText;
}
.......
"web_accessible_resources": [   
    "html/htmlFileName.html",
    "styles/*",
    "fonts/*"
]

After that you can use iframe.contentDocument to access to iframe's DOM