Not able to access chrome.runtime in iframe - javascript

Trying to access chrome.runtime.sendMessage in an iframe, but it is showing that
chrome.runtime is undefined
chrome.runtime.sendMessage("kbfjlfcddgkokfgifbohnjfpcnkknpbf", { getVersion: true },
function (response) {
console.log(response);
}
);

Based from this thread, if you're inserting JavaScript into a page with a <script> tag, it executes in the page's context.
Sometimes it is desirable: that's the only way to access page-level JavaScript objects.
But in your case it means that the code does not have access to Chrome APIs, as it is "the same" as the page's code.
You need to look into communicating between page-level and context scripts, or between page-level and background (spoiler, in most cases needs a context script proxy anyway).
Also from this page, by adding a breakpoint, or debugger statement, it causes that value to be undefined. Try to refresh the page then open after page is loaded and see if the chrome.runtime works correctly.

Related

Why does browser.runtime.getBackgroundPage() resolve to null in a page action script?

In the following code
browser.runtime.getBackgroundPage().then(bgp=>{
document.querySelector("button").addEventListener("click", e=>{
alert(bgp);
});
});
bgp turns out to be null. I searched around and suggestions are most of the time for Chrome extensions, suggesting adding a "background" permission, which is not valid for Firefox. I also tried adding a background page explicitly, although one should be always created for me but it did not work either.
runtime.getBackgroundPage() provides access to the background script, not an HTML document.
This provides a convenient way for other privileged extension scripts
to get direct access to the background script's scope. This enables
them to access variables or call functions defined in that scope.
"Privileged script" here includes scripts running in options pages, or
scripts running in browser action or page action popups, but does not
include content scripts.
For example, the following code logs <unavailable> to the console.
browser.runtime.getBackgroundPage().then(bg => console.log(bg));
The window object can be seen in the debug console.

Inject javascript at the very top of the page \ Anti iframe-buster

I'm developing an extension that, sometimes, will show some websites inside iframes. I've already bypassed the X-FRAME-OPTIONS issue but now I'm stuck with the simple iframe buster code, eg.:
if (top != self) {
document.getElementsByTagName("html")[0].style.display = "none";
top.location.replace(location);
}
I'm trying to inject javascript at the very top of the page to override the window.top object, but at document_start is already too late to inject it, ie alert() is never called before the buster script runs:
chrome.webRequest.onCompleted.addListener(function(details) {
if (isEnabled) {
chrome.tabs.executeScript(details.tabId, {frameId: details.frameId, runAt: "document_start", code: "alert('asas');"});
}
}, {
types: ["sub_frame"],
urls: ["<all_urls>"]
});
Is there any way around this?
Thank you
The problem is probably caused by chrome.webRequest.onCompleted.addListener listener being asynchronous
document_start injects code before any DOM is created, so that is not the cause of your problem. I have verified this while playing around and trying to answer this question.
The problem here is that chrome.webRequest.onCompleted.addListener is asynchronous, which means that when the callback (and therefor your chrome.tabs.executeScript) is executed, the browser has already started constructing the DOM.
You can start by injecting the script to all relevant iframes directly using the "content_scripts" in manifest.json instead of using programmatic injection. I haven't verified this, but you could also try injecting the script from a chrome.webRequest.onHeadersReceived listener with the "blocking" option, which allows you to handle the request synchronously. You are probably already listening to onHeadersReceived in order to remove the X-Frame-Options header anyway.
Edit:
Programmatic injection in a blocking onHeadersReceived listener is not possible. Chrome returns an error about lack of permissions - probably because the URL is not known at this point yet (the headers could cause a redirect).

Why is chrome.runtime undefined when running JS inside a page from a content script?

I am creating a Google Chrome extension (my first one) and I want to send messages from the extension to the current tab.
I am following the documentation:
https://developer.chrome.com/apps/runtime#event-onMessage
The extension loads a small external JS into the tab's HTML, which contains the following code:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request)
}
);
As soon as the JS is loaded I get the following error:
Uncaught TypeError: Cannot read property 'onMessage' of undefined.
Opening console and typing chrome, I can see that the runtime is not a property of chrome.
It looks like I am doing something wrong, but what? Do I need to add something to the manifest.json file?
Chrome Version 39.0.2171.71 m
Thank you.
If you're inserting JavaScript into a page with a <script> tag, it executes in the page's context.
Sometimes it is desirable: that's the only way to access page-level JavaScript objects.
But in your case it means that the code does not have access to Chrome APIs, as it is "the same" as the page's code.
You need to look into communicating between page-level and content scripts, or between page-level and background (spoiler, in most cases needs a context script proxy anyway).
or it could just be a Heisenbug which only appears under certain circumstances. in my case, closing the chrome://extensions tab and refreshing my target caused chrome.runtime to be available again. Why is chrome.runtime undefined in the content script?

IE9 "Can't execute code from freed script" when calling hasOwnProperty()

Here is the scenario:
I have a container page that swaps iFrames in and out to show different content. All iFrames come from the same domain. https is enabled.
The container page has an object called Flow, with functions set/getParameter
The first iFrame, s0-welcome, creates an object, data, and calls Flow.setParameter('data', data);
The container then replaces the first iFrame with a second iFrame, s1-transfer.
The s1-transfer calls Flow.getParameter('data') and stores it in a local variable s1data
In the IE9 debug tools console, if I type s1data it shows me all the properties of that object. However, if I call s1data.hasOwnProperty('prop1'), I get a "Can't execute code from a freed script" error. If I call Object.prototype.hasOwnProperty.call(s1data, "prop1"), everything works fine.
It looks to me that there can be 2 possibilities:
1) Container page holds on to the reference from the first iFrame, but when the first iFrame gets disposed, it loses some of the data. This seems unlikely since the only thing I can't access is functions
2) There is a security restriction that does not allow one iFrame to run code related to another iFrame even if both iFrames are from the same domain.
Any thoughts?
Just ran into a similar issue. For me, simply changing s1data.hasOwnProperty('prop1') into ('prop' in s1data) made the error go away.

javascript google chrome extension iframe cannot examine contentWindow

I am writing a Google Chrome extension. Now I need to examine the contents of an iframe but the content script seems unable to access this content even though the debugger can. The iframe contents are a list of messages I have previously sent to that site. If I put the following statement in the content script, it always returns null:
document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow.document;
But if I open the debugger and execute the same command from the command line, it returns "Document" with the appropriate contents. At first I thought it was because the frame wasn't finished loading so I found a snippet like this and tried to use it.
function wait4Iframe2Load() {
// Get a handle to the iframe element
//console.log('Checking for null myFrame');
var myFrame = document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow;
if (myFrame!=null)
{
console.log(myFrame);
// Check if loading is complete
if ( myFrame.document.readyState == 'complete' ) {
// The loading is complete, call the function we want executed once the iframe is loaded
console.log('Loading Complete');
//frameContent=document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow.document.getElementsByTagName('tbody')[0];
return;
}
else alert(" Frame is Not Loaded");
}
else myFrame = document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow;
// If we are here, it is not loaded. Set things up so we check the status again in 100 milliseconds
console.log('Waiting for frame to load...');
window.setTimeout('wait4Iframe2Load()', 100);
}
This simply returns null forever. But while this script is piling up console messages, I can open the debugger and execute the very same command line and it returns a document. Faced with this problem and researching internet answers, it seems it may be some deliberate kind of security issue. Whether it is or isn't, I need to examine the iframe contents and determine what I have written there previously so I can decide what to write there next.
Does anybody have an idea how to solve this problem?
The idea is to inject content script into this iframe and use it to get required information.
As I understand this frame has a specific url known upfront, so you can just inject the script through the manifest (use the all_frames option).
If for some reason you need to dynamically inject it, then there is:
chrome.tabs.executeScript(tabId, {allFrames: true});
It will be injected into both parent and iframe pages. Then inside injected content script you can check whether or not it is running inside the right iframe. For example your dynamically injected content script might look like this (if you inject it though the manifest url checking won't be needed):
if(window.location.href == "http://iframe.example.com/" && window != window.top) {
//we are in the right page that is embedded as iframe, do stuff
} else {
//do nothing
}
Excuse me, have you got solution, Jerome? I am having same problem as yours. But I can not make comment on your post. So please do not mind because this is a question...
Edited:
Finally I got it working. I don't understand while flag all_frames: true in manifest.json is not affected. I need to code as #serg's:
chrome.tabs.executeScript(tabId, {allFrames:true, file:"content_script.js"})
Thank you all, and could you please accept #serg's answer as the right one, Jerome? :-)
I created this library:
https://github.com/attachmentsme/Queuebert
to simplify the communication between browser tabs, the extension's background process, and iframes.
The use-case that I was running into was:
an action is taken in one iframe.
the results should be displayed in an alternate iframe.
Doing this can be a pain, hence the abstraction I built.

Categories