accessing iframe element not working in chrome - javascript

I am using iframe, this iframe will have a img tag so i have to set a data from a parent html page to iframe.
I am achieving this by following
My Html is,
<img id='im' src='data:image/jpeg;base64,/9j/4AAQSkZ.....'/>
My script is,
jQuery(".edit").click(function(){
jQuery("#editor").removeClass('disable');
frames = window.frames;
src = jQuery('#im').attr('src');
jQuery(frames[0].document.getElementById('canv')).attr("src",src);
});
The above code correctly pass image data and set the target element in iframe in firefox, but in chrome the target image does not change..What could be the reason for this?

Chrome is a little tricky when it comes to iframes. Add a doctype to the top of your iframe document and it should work.
If that does not work, it is likely an issue with Chrome security settings blocking mixed content. From what I understand, Chrome will not load I frames that are not https.

Related

Firefox WebExtension, isolated HTML overlay

I'm looking for a way to display an isolated overlay on some websites using WebExtensions.
An iframe would seem like the way to go for this as it provides a whole separate scope for css, js and the DOM. And another neat thing is that the target website won't be able to read or change the content.
In Chrome extensions that seems to be possible without any problems, but with WebExtensions in Firefox, even though they share the same syntax, I get security warnings/errors and it doesn't work.
I've tried two different things:
Creating an iframe without an src attribute and inject that into the body of website.
This method failed because I get CSP errors/warnings when I do iframe.contentWindow.document.open().
Relevant content-script code:
let html = `
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>TEST</h1>
</body>
</html>
`
let iframe = document.createElement('iframe')
document.body.appendChild(iframe)
iframe.contentWindow.document.open()
iframe.contentWindow.document.write(html)
iframe.contentWindow.document.close()
The other thing I tried (which would make way more sense as it would disallow the website from accessing the content), was to put my iframe code into a file (overlay.html) in my WebExtension and make the iframe load it by setting it's src to browser.extension.getURL('overlay.html').
Relevant content-script code:
let iframe = document.createElement('iframe')
iframe.src = browser.extension.getURL('overlay.html')
document.body.appendChild(iframe)
In the manifest I defined the overlay.html as web_accessible_resources for this:
"web_accessible_resources": [
"overlay.html"
],
The thing about that is that the iframe simply doesn't load the overlay.html file. But it is definitely available, otherwise location.href = browser.extension.getURL('overlay.html') wouldn't work. It would have been extremely convenient if that would have worked, as I could have stored the whole overlay (html, css, js) as separate files in my extension. As if it would be a standalone website. And using the content-script I could have accessed it to add new data or whatever.
Edit:
At the moment I'm using the srcdoc attribute of my iframe to set the source code it should contain (thanks to wOxxOm for that). So at least I have something that works now. But what I really dislike about this approach is that I can't interact with the iframe content from my content script. Interestingly though, I can interact with the parent page from within the iframe's js code, but again not with the content script. It's also really messy, inconvenient and hard to maintain to put all your html, css, js code into one string instead of multiple files like a normal website.
The Problem
Firstly, we know the problem is a Security Issue, what exactly is the issue? Well when trying to load an extension resource into an iframe by setting the iframe src the browser complains about Security and prevents the iframe from connecting over a different protocol, in this instance 'moz-extension://'.
The Solution
Load the html etc from the extension context and inject as a string.
The Nitty Gritty
To get around this, we can set the src attribute of the iframe to data:text/html;charset=utf8,${markup}.
This directly tells the iframe that the content is html, it uses utf8 encoding and it is followed by the raw markup. We're completely bypassing the need for the iframe to load any resources over the network.
The execution context of a Firefox content script is seperate from the page it has been loaded for. This means that you can make an xhr request without violating CSP.
If you make an xhr request to your markup, you can then get the content of the response as a string, and directly inject it into the iframe src attribute.
Thus the content script:
function loaded (evt) {
if (this.readyState === 4 && this.status === 200) {
var html = this.responseText;
console.log(html);
var iframe = document.createElement('iframe');
iframe.src = 'data:text/html;charset=utf-8,' + html;
document.body.appendChild(iframe);
console.log('iframe.contentWindow =', iframe.contentWindow);
} else {
console.log('problem loading');
}
}
var xhr = new XMLHttpRequest();
xhr.overrideMimeType("text/html");
xhr.open("GET", browser.extension.getURL('test.html'), false);
xhr.addEventListener("readystatechange", loaded);
xhr.send(null);
With a simple HTML file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test</title>
<meta charset="utf8" />
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
So now you have successfully injected a html template into the target iframe.
If you need any images, scripts, css files etc, you'll need to write a bootloader based on the method outlined above, injecting new script tags and so forth directly into the iframe document.
btw, sometimes people use IFRAME url in different protocol, i.e.
https:// (while current page is http:// ).
So, IFRAME url should be relative, like src="//example.com"

Chrome extension: writing content into a dynamic iframe created in a sandboxed environment

I can't overcome this issue. Can someone give some advice?
I have this application that uses ExtJS library that I will need to run in Chrome extension. I have successfully created my messaging bridge (postMessage), and sandboxed the whole application in it, and everything works as usual. ExtJS loaded, application is running.
Then I have this piece of logic where I need to preview a piece of HTML snippets in my ExtJS viewport. I created an iframe in the Panel itself and on afterrender I tried to write the snippet in it. This is the code I use:
html: '<iframe src="about:blank" style="width:100%;height:100%;border:none;"></iframe>';
......
//p is the panel found in afterrender
p.body.down('iframe').dom.contentDocument.write(content);
Then the error:
Unsafe JavaScript attempt to access frame with URL about:blank from frame with URL chrome-extension://fcnpmlgapilgclcelfanblpbglmkghbc/core/themes/default/app.html. Domains, protocols and ports must match.
I have tried with postMessage within sandbox to this dynamic iframe but nothing happens. Setting the sandbox attribute in manifest doesn't work either. This is the only way and it works. See my answer below.
Question:
How should one set the manifest to support this kind of use case?
Or is there any better way to preview HTML snippet without using an iframe? Afaik previewing with iframe is the best as it sandboxed the snippet without being messed with parent css.
Note
This piece of code was working fine in manifest v1 but I planned to migrate it to manifest v2. I didn't realize Content Security Policy (CSP) has became that strict.
A screen to describe the problem ;)
Sorry, this is awkward. Apparently postMessage is the only way, and I can't manage to get it work previously because my iframe is not loaded yet. Also, accessing the dom document in the iframe is a big no under CSP, but it's possible to access contentWindow to do a postMessage.
This is what I did to solve this issue. Hope someone would benefit from this:
Create a preview.html in your extension root
Under manifest.json, add it as part of the sandbox attribute
Inside the preview.html, add the following code. Note that my snippet is a full html, so I used document.write instead.
<!DOCTYPE html>
<html>
<head>
<script>
window.addEventListener("message", function(e) {
if (e.data.content) {
document.write(e.data.content);
}
});
</script>
</head>
<body></body>
</html>
Create the iframe in your code as usual, point it to your preview.html, and attach it to the parent div or Ext.Panel.
Use the following code to postMessage after the element has been drawn/created/appended.
var content = '<!DOCTYPE html><html><body>Hello World!</body></html>';
var iframe = p.body.down('iframe').dom; //or the iframe node
iframe.onload = function() {
iframe.contentWindow.postMessage({content: content}, '*');
};
Enjoy ;)
Edit
As pointed by Mike in Chromium forum, this issue can be solved by srcdoc as well. Simply set the iframe's srcdoc and problem is solved.
Just not sure of the status of srcdoc

How to add content script to iframe document programmatically

I'm writing some diagnostic tool and trying to create chrome extension for it. What I have is a page with an iframe. The iframe contains site that is laying on diffrent domain.
What I need is a communication between those two sites. What I figured out is that I can simply add an content script to each of those documents (to iframe document and my top window document) and set a communication between them by the background script and message flow.
The problem is that I don't know what is the address of the document in the iframe right now, users can put multiple diffrent pages into iframe. Because of this I cannot add content script using manifest.json file, because I do not know what to put into matches property.
I tried to use chrome.tabs.executeScript method with allFrames flag set to true. It worked fine when the iframe document was on the same domain as top window document, but when it's on diffrent domain it does not add content script to frame document.
The problem is strange, because if I know the iframe url on design time then I can add those scripts just like that from manifest file...
How can I add content script to iframe document in such situation?
I will also tell you that I have permission to every page in my manifest.json file. It lokks like this: "permissions": ["tabs", "http://*/", "https://*/"]. Additonal obstacle is that I cannot ingerate in iframe document content, it's third-party code.
Any ideas? Thanks for any help.
Following Rob W comment the problem was the timing. I was trying to inject script into iframe before it was loaded. I've used onload event on the iframe to determine the moment when the executeScript method should be called.
Now everything works fine. Thanks Rob.

Source less iframe bug firefox

I'm using a src less iframe to develop a javascript widget to protect my css from the parent page.
It's perfectly working in chrome/safari. However in firefox I can see the content is added during few milliseconds and then the iframe becomes empty.
If I inspect html I see empty head and empty body, however if I inspect innerHtml through the console I can see that It has the right content...
I'm sorry I can't give you code as it's hard to pull out the relevant parts : I can tell you I access the iframe with jquery contents() and then find body or find head.
Any idea plz ?
I Managed to make an example : http://jsbin.com/arenat/2/edit#javascript,html,live
Just some code pull out to show the issue : working on chrome no in firefox (10.0.1). Hope it's enough.
When you add the frame to the DOM, it starts loading about:blank asynchronously. Then you modify the DOM in the iframe ... and then the asynchronous load completes and replaces the document you modified.
I suggest either using an onload handler on the iframe to do your manipulation after the about:blank has finished loading or using document.open() and document.close() to force cancellation of the async load.
It's working with a timeout :
http://jsbin.com/arenat/9/edit

Dynamically Resizing an Iframe

I can see that this question has been asked several times, but none of the proposed solutions seem to work for the site I am building, so I am reopening the thread. I am attempting to size an iframe based on the height of it's content. Both the page that contains the iframe and it's source page exist on the same domain.
I have tried the proposed solutions in each of the following threads:
Resize iframe height according to content height in it
Resizing an iframe based on content
I believe that the solutions above are not working because of when the reference to body.clientHeight is made, the browser has not actually determined the height of the document.
Here is the code I am using:
var ifmBlue = document.getElementById("ifmBlue");
ifmBlue.onload = resizeIframe;
function resizeIframe()
{
var ifmBlue = document.getElementById("ifmBluePill");
var ifmDiv = ifmBlue.contentDocument.getElementById("main");
var height = ifmDiv.clientHeight;
ifmBlue.style.height = (ifmBlue.contentDocument.body.scrollHeight || ifmBlue.contentDocument.body.offsetHeight || ifmBlue.contentDocument.body.parentNode.clientHeight || height || 500) + 5 + 'px';
}
If I debug the script using fire debug, the client height of the iframe.contentDocument's main div is 0. Additionally, body.offsetHieght, & body.scrollHeight are 0. However, after the script is finished running, if I inspect the DOM of the HTML iframe element (using fire debug) I can see that the body's clientHeight is 456 and the inner div's clientHeight is 742. This leads me to believe that these values are not yet set when iframe.onload is fired. So, per one of the threads above, I moved the code into the body.onload event handler of the iframe's source page. This solution also did not work.
Any help you can provide is much appreciated.
Thanks,
CJ
DynamicDrive has such a script, which I think does what you're asking for.
There's also a newer version now.
2011 update:
I would strongly recommend using AJAX over something like this, especially considering that a dynamically resizing iframe only works across the same domain.
Even so, it's a bit iffy, so if you absolutely must use AJAX over standard page loading, you really, really should use things like history.pushState (and have standard page loading as a fallback for browsers that don't support it). There's a jQuery plugin which handles this stuff for you, written by a GitHubber, called pjax, which they use only for repo navigation.
you moved the handler? maybe you should move the function to the inner frame as well, so that when you grab height values you reference the body directly rather than frame object... then call a parent.set height function
another trick, call function after settimeout of 10 msecs
i remember I had that problem once but I used IE's getBoundingClientRect() to get height of content, check mozilla developer center for something similar, this is just a hint, i did not research it
on another note, what is ifmBluePill? is it the iframe? or a div inside of it? why do you reference "contentDocument" of a div?
By the way, DynamicDrive improved their script to always resize even if the iframe contents change: http://www.dynamicdrive.com/dynamicindex17/iframessi2.htm
From their page:
This is version II of the original
Iframe SSI script, which like the
original script lets you seamlessly
display external content on your page
via an IFRAME. It does this by
dynamically resizing the IFRAME to be
the height of the page contained
within it, eliminating any possible
IFRAME scrollbars from appearing while
snugly showing the entire external
content. Think of it as SSI (server
side includes) emulated using DHTML!
This script works in both IE5+ and
NS6+, and for other browsers, supports
the option to either completely hide
the iframe in question or display it
using its default height.
Now, this script differs from the
original in that you can load
additional documents* into the IFRAME
even after the page has loaded, and
the IFRAME will dynamically adjust its
height to fit the new document. So use
this script if you need to not only
display external content via the
IFRAME tag, but intend to change this
content after the page has loaded.

Categories

Resources