Programmatically accessing an iframe that uses a data URI as a source - javascript

I'm creating an iframe programmatically using the "data" URI:
<iframe id="myFrame" src='data:text/html;charset=utf-8,<!DOCTYPE html><html><head></head><body><h1>Hello.</h1></body></html>'></iframe>​
This frame loads fine, but it seems that working with the iframe programmatically hits cross-domain security checks.
var iframeDoc = document.getElementById('myFrame').contentWindow.document;
$(iframeDoc.body).find('h1').text('Changed');
Throws an error in Chrome and Safari:
Unsafe JavaScript attempt to access frame with URL
data:text/html;charset=utf-8,... from frame with URL http://... The
frame requesting access has a protocol of 'http', the frame being
accessed has a protocol of ''. Protocols must match.
Here's a fiddle showing the security error: http://jsfiddle.net/bhGcw/4/
Firefox and Opera do not throw this exception and allow the iframe contents to be changed. Seems like Webkit sees a blank protocol for data URIs, and sees this as a cross-domain violation.
Is there any way around this?

It's a bit late, how about instead of using a data URL, you use the HTML5 attribute srcdoc.
<iframe id="iframe" srcdoc='<html><body><h1>Hello!</h1></body></html>'></iframe>
<script type="text/javascript">
$(function(){
$($("iframe")[0].contentWindow.document).find("h1").text("Modified from the parent window!");
});
</script>
There's an example at http://jsfiddle.net/ff3bF/

It appears that Webkit does a simple string comparison in their domain checking code:
String DOMWindow::crossDomainAccessErrorMessage(DOMWindow* activeWindow)
{
...
SecurityOrigin* activeOrigin = activeWindow->document()->securityOrigin();
SecurityOrigin* targetOrigin = document()->securityOrigin();
if (targetOrigin->protocol() != activeOrigin->protocol())
return message + " The frame requesting access has a protocol of '" + activeOrigin->protocol() + "', the frame being accessed has a protocol of '" + targetOrigin->protocol() + "'. Protocols must match.\n";
...
}
It looks like Chromium is being more strict than the HTML5 spec, at least according the following bug reports:
https://bugs.webkit.org/show_bug.cgi?id=17352
https://code.google.com/p/chromium/issues/detail?id=58999
Chromium devs don't seem to be in favor of relaxing this rule. Bummer.

The answer put forward by #jamie works well for loading HTML into an iframe and allowing subsequent programatic interaction with the content document.
XHTML is not so easy.
The srcdoc attribute appears to be limited to HTML, not XHTML.
A work around is to use a Blob URL which allows the content-type to be specified.
var documentSource = '<?xml version="1.0" encoding="UTF-8"?>\n<html xmlns="http://www.w3.org/1999/xhtml">\n<head>...';
var blob = new Blob([documentSource], { type: "application/xhtml+xml" });
iframe.src = URL.createObjectURL(blob);
This technique works for at least Chrome, Firefox and Safari.

Related

Cookies not working when page accessed via file://

My code works in firefox and when i visit w3schools using chrome to test my code in their editor it works fine too but when i launch my code in chrome from notepad++ it doesn't work.It seems that body onload is not working because i don't get the alert.My chrome is up to date.Help would be appreciated.
<!DOCTYPE html>
<html>
<head>
<script>
function setCookie(cname,cvalue,exdays){
var d=new Date();
d.setTime(d.getTime()+(exdays*24*60*60*1000));
var expires="expires="+d.toUTCString();
document.cookie=cname +"="+cvalue+"; "+expires;
}
function f(){
var user=prompt("What is your name?","");
if(user!="" && user!=null){
setCookie("username",user,30);}
}
function getC(cname){
var name=cname+"=";
var ca=document.cookie.split(";");
for(var i=0;i<ca.length;i++){
var c=ca[i];
while(c.charAt(0)==" ")c=c.substring(1);
if(c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
function checkcooki(){
var user=getC("username");
if(user!=""){
alert("Welcome back "+user);
}
}
</script>
</head>
<body onLoad="checkcooki()">
<input type="button" onclick="f()" value="klick">
</body>
</html>
For a fact: Using the file:// protocol does NOT guarantee the proper workings with cookies. Since cookies need 3 things:
A name-value pair containing the actual data
An expiry date after which it is no longer valid
The domain and path of the server it should be sent to
The domain tells the browser to which domain the cookie should be sent. If you don't specify it, it becomes the domain of the page that sets the cookie.
On a file:// protocol you don't have a domain.
Now some browsers might have found work-arounds for this, like FireFox and IE. You can test your code on these browsers but they will not use cookies in the same way as on a webserver.
Proper x-browser testing in your case requires the http:// protocol.
I suggest you start a jsfiddle or setup a webserver(IIS, apache).
Proper read on cookies: quircksmode
If you are still persistent to get it working on chrome through the file:// protocol you might have a small chance if you get the path correctly.
path: properly escaped path => encodeURIComponent(document.domain) or "c:\/my%20folder\/index.html" (along these lines but again, very untrustworthy information here)
domain: "/" (no idea what else you can try here)
Your user variable must be a blank string.
Put an alert at the very top of your checkcooki() function to verify that body onload works.

Failed to execute 'postMessage' on 'DOMWindow': https://www.youtube.com !== http://localhost:9000

This is the error message that I get:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided
('https://www.youtube.com') does not match the recipient window's origin
('http://localhost:9000').
I've seen other similar problems where the target origin is http://www.youtube.com and the recipient origin is https://www.youtube.com, but none like mine where the target is https://www.youtube.com and the origin is http://localhost:9000.
I don't get the problem. What is the problem?
How can I fix it?
I believe this is an issue with the target origin being https. I suspect it is because your iFrame url is using http instead of https. Try changing the url of the file you are trying to embed to be https.
For instance:
'//www.youtube.com/embed/' + id + '?showinfo=0&enablejsapi=1&origin=http://localhost:9000';
to be:
'https://www.youtube.com/embed/' + id + '?showinfo=0&enablejsapi=1&origin=http://localhost:9000';
Just add the parameter "origin" with the URL of your site in the paramVars attribute of the player, like this:
this.player = new window['YT'].Player('player', {
videoId: this.mediaid,
width: '100%',
playerVars: {
'autoplay': 1,
'controls': 0,
'autohide': 1,
'wmode': 'opaque',
'origin': 'http://localhost:8100'
},
}
Setting this seems to fix it:
this$1.player = new YouTube.Player(this$1.elementId, {
videoId: videoId,
host: 'https://www.youtube.com',
You can save the JavaScript into local files:
https://www.youtube.com/player_api
https://s.ytimg.com/yts/jsbin/www-widgetapi-vfluxKqfs/www-widgetapi.js
Into the first file, player_api put this code:
if(!window.YT)var YT={loading:0,loaded:0};if(!window.YTConfig)var YTConfig={host:"https://www.youtube.com"};YT.loading||(YT.loading=1,function(){var o=[];YT.ready=function(n){YT.loaded?n():o.push(n)},window.onYTReady=function(){YT.loaded=1;for(var n=0;n<o.length;n++)try{o[n]()}catch(i){}},YT.setConfig=function(o){for(var n in o)o.hasOwnProperty(n)&&(YTConfig[n]=o[n])}}());
Into the second file, find the code: this.a.contentWindow.postMessage(a,b[c]);
and replace it with:
if(this._skiped){
this.a.contentWindow.postMessage(a,b[c]);
}
this._skiped = true;
Of course, you can concatenate into one file - will be more efficient.
This is not a perfect solution, but it's works!
My Source : yt_api-concat
Make sure you are loading from a URL such as:
https://www.youtube.com/embed/HIbAz29L-FA?modestbranding=1&playsinline=0&showinfo=0&enablejsapi=1&origin=https%3A%2F%2Fintercoin.org&widgetid=1
Note the "origin" component, as well as "enablejsapi=1". The origin must match what your domain is, and then it will be whitelisted and work.
In my case this had to do with lazy loading the iframe. Removing the iframe HTML attribute loading="lazy" solved the problem for me.
I got the same error. My mistake was that the enablejsapi=1 parameter was not present in the iframe src.
You also get this message when you do not specify a targetOrigin in calls to window.postMessage().
In this example we post a message to the first iFrame and use * as target, which should allow communication to any targetOrigin.
window.frames[0].postMessage({
message : "Hi there",
command :"hi-there-command",
data : "Some Data"
}, '*')
Try using window.location.href for the url to match the window's origin.
Remove DNS Prefetch will solve this issue.
If you're using WordPress, add this line in your theme's functions.php
remove_action( 'wp_head', 'wp_resource_hints', 2 );
There could be any of the following, but all of them lead into DOM not loaded before its accessed by the javascript.
So here is what you have to ensure before actually calling JS code:
* Make sure the container has loaded before any javascript is called
* Make sure the target URL is loaded in whatever container it has to
I came across the similar issue but on my local when I am trying to have my Javascript run well before onLoad of the main page which causes the error message. I have fixed it by simply waiting for whole page to load and then call the required function.
You could simply do this by adding a timeout function when page has loaded and call your onload event like:
window.onload = new function() {
setTimeout(function() {
// some onload event
}, 10);
}
that will ensure what you are trying will execute well after onLoad is trigger.
In my instance at least this seems to be a harmless "not ready" condition that the API retries until it succeeds.
I get anywhere from two to nine of these (on my worst-case-tester, a 2009 FossilBook with 20 tabs open via cellular hotspot).... but then the video functions properly. Once it's running my postMessage-based calls to seekTo definitely work, haven't tested others.
It looks it's only a Chrome security system to block repeated requests, using CORB.
https://www.chromestatus.com/feature/5629709824032768
In my case, YouTube was blocking Access after the first load of the same webpage which has many video API data request, high payload.
For pages with low payload, the issue does not occur.
In Safari and other non Chronuim based browsers, the issue does not occur.
If I load the webpage in a new browser, the issue does not occur, when I reload the same page, the issue appears.
In some cases (as one commenter mentioned) this might be caused if you are moving the player within DOM, like append or etc..
This helped me (with Vue.js)
Found here vue-youtube
mounted() {
window.YTConfig = {
host: 'https://www.youtube.com/iframe_api'
}
const host = this.nocookie ? 'https://www.youtube-nocookie.com' : 'https://www.youtube.com'
this.player = player(this.$el, {
host,
width: this.width,
height: this.height,
videoId: this.videoId,
playerVars: this.playerVars
})
...
}
UPDATE:
Working like a charm like this:
...
youtube(
video-id="your_video_code_here"
nocookie
)
...
data() {
return {
playerVars: {
origin: window.location.href,
},
};
},
I think the description of the error is misleading and has originally to do with wrong usage of the player object.
I had the same issue when switching to new Videos in a Slider.
When simply using the player.destroy() function described here the problem is gone.
I had this same problem and it turns out it was because I had the Chrome extension "HTTPS Everywhere" running. Disabling the extension solved my problem.
This exact error was related to a content block by Youtube when "playbacked on certain sites or applications". More specifically by WMG (Warner Music Group).
The error message did however suggest that a https iframe import to a http site was the issue, which it wasn't in this case.
You could change your iframe to be like this and add origin to be your current website. It resolves error on my browser.
<iframe class="test-testimonials-youtube-group" type="text/html" width="100%" height="100%"
src="http://www.youtube.com/embed/HiIsKeXN7qg?enablejsapi=1&origin=http://localhost:8000"
frameborder="0">
</div>
ref: https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player
Just wishing to avoid the console error, I solved this using a similar approach to Artur's earlier answer, following these steps:
Downloaded the YouTube Iframe API (from https://www.youtube.com/iframe_api) to a local yt-api.js file.
Removed the code which inserted the www-widgetapi.js script.
Downloaded the www-widgetapi.js script (from https://s.ytimg.com/yts/jsbin/www-widgetapi-vfl7VfO1r/www-widgetapi.js) to a local www-widgetapi.js file.
Replaced the targetOrigin argument in the postMessage call which was causing the error in the console, with a "*" (indicating no preference - see https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
Appended the modified www-widgetapi.js script to the end of the yt-api.js script.
This is not the greatest solution (patched local script to maintain, losing control of where messages are sent) but it solved my issue.
Please see the security warning about removing the targetOrigin URI stated here before using this solution - https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Patched yt-api.js example
Adding origin=${window.location.host} or "*" is not enough.
Add https:// before it and it will work.
Also, make sure that you are using an URL that can be embedded: take the video ID out and concatenate a string that has the YouTube video prefix and the video ID + embed definition.
I think we could customize the sendMessage of the YT.Player
playerOptions.playerVars.origin = window.location.origin or your domain.
this.youtubePlayer = new YT.Player(element,playerOptions);
this.youtubePlayer.sendMessage = function (a) {
a.id = this.id, a.channel = "widget", a = JSON.stringify(a);
var url = new URL(this.h.src), origin = url.searchParams.get("origin");
if (origin && this.h.contentWindow) {
this.h.contentWindow.postMessage(a, origin)
}
}
I used this function to resolve in my project.
Extending #Hokascha's answer above it was also lazy loading for me being automatically added by WordPress. This code will remove all lazy loading on the site's iframes (add to functions.php):
function disable_post_content_iframe_lazy_loading( $default, $tag_name, $context ) {
if ( 'iframe' === $tag_name ) {
return false;
}
return $default;
}
add_filter('wp_lazy_loading_enabled', 'disable_post_content_iframe_lazy_loading', 10, 3);
I got a similar error message in my attempt to embed a Stripe pricing table when:
Adding the embed code via PHP through a custom WordPress short code
Or by appending the code to the page dynamically with JavaScript (Even a using a setTimeout() delay to ensure the DOM was loaded didn't work).
I was able to solve this on my WordPress site by adding the code to the WordPress page itself using plain html code in the block editor.
mine was:
<youtube-player
[videoId]="'paxSz8UblDs'"
[playerVars]="playerVars"
[width]="291"
[height]="194">
</youtube-player>
I just removed the line with playerVars, and it worked without errors on console.
You can try :
document.getElementById('your_id_iframe').contentWindow.postMessage('your_message', 'your_domain_iframe')
I was also facing the same issue then I visit official Youtube Iframe Api where i found this:
The user's browser must support the HTML5 postMessage feature. Most modern browsers support postMessage
and wander to see that official page was also facing this issue. Just Visit official Youtube Iframe Api and see console logs. My Chrome version is 79.0.3945.88.

postMessage in PhoneGap not working - iframe to parent messaging

I've build a PhoneGap app which which makes use of an iframe which is bundled with the app and I'm am trying to pass e message from the iframe to the parent which doesn't seem to be working when I run the app on an actual iPad; however it works fine when I run the app in the browser on the same device.
Here is the code I'm using inside the iframe to send a message, note that I'm using HammerJS to capture some events:
var domain = 'http://' + document.domain;
$('body').hammer().on("swipe", "", function(event) {
var message = event.gesture.direction;
parent.postMessage(message,domain); //send the message and target URI
});
and the code I'm using to get the message:
window.addEventListener('message',function(event) {
alert(event.data);
},false);
And the answer is to use "file://" as the domain name so the code will look like this:
var domain = 'file://';
$('body').hammer().on("swipe", "", function(event) {
var message = event.gesture.direction;
parent.postMessage(message,domain); //send the message and target URI
});
Try with using
var domain = '*';
Normally this should be because of cross domain problem, see more here
You will need to use:
parent.postMessage(message,"*");
Since phonegap/cordova pages are served at "file://" and according to https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
"...posting a message to a page at a file: URL currently requires that the targetOrigin argument be "*". file:// cannot be used as a security restriction; this restriction may be modified in the future."

Ajax request "Access is denied" in IE

I use ajax request in order to check response of websites as follow,
$.ajax ({
url: 'https://www.example.com',
cache: false,
success : function() {
alert(new Date() - start)
},
})
It works on my local pc in all browsers. When I put it on the server, it works in Chrome and Firefox but not in IE8.
I get the error: "Access is denied" jquery.min.js
Why am I getting this error?
For my case the problem is resulted because of compatibility mode. I am in intranet and internet explorer is running with compatibility mode.
I added following tag and this solved all my problems. It forces IE to not use compatibility mode.
<meta http-equiv="X-UA-Compatible" content="IE=Edge" >
--- JAN 2014 ---
IE8 and IE9 use a different method (XDomainRequest) to communicate with cross domains.
You should consider using this if they are using jQuery:
https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
Make sure to use the same protocol as the originating call, i.e. HTTP or HTTPS.
Quoting "epascarello" from an other very similar question :
Making a call to a sub domain is seen as a different domain because of the Same Origin policy. Make sure that you are setting document.domain to avoid access denied with the Same Origin policy.
To get the document.domain in sync you need to set it in two places. Add a script tag that set the domain, and you need to have an iframe on the page that sets the same thing on the other domain.
The page that the Ajax call is made from "www.example.com" and is calling "ajax.example.com":
<script type="text/javascript">
document.domain = "example.com";
</script>
<iframe src="http://ajax.example.com/domainCode.html"></iframe>
The "domainCode.html" would just contain the script tag
<html>
<head>
<script type="text/javascript">
document.domain = "example.com";
</script>
</head>
<body>
</body>
</html>
With that in place you should be able to talk between your sub domains.
Hope that helps !
Note -- Note
do not use "http://www.domain.xxx" for URL in ajax.
only use path(directory) and page name without address.
false state:
var AJAXobj = createAjax();
AJAXobj.onreadystatechange = handlesAJAXcheck;
AJAXobj.open('POST', 'http://www.example.com/dir/getSecurityCode.php', true);
AJAXobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
AJAXobj.send(pack);
true state:
var AJAXobj = createAjax();
AJAXobj.onreadystatechange = handlesAJAXcheck;
AJAXobj.open('POST', 'dir/getSecurityCode.php', true); // <<--- note
AJAXobj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
AJAXobj.send(pack);
I had this problem in IE8. What solved it for me was changing my ajax request to use the same protocol as the original page request. In my case the original page was requested over https and the ajax request was using http. Switching them both to use https fixed the problem.

importNode for web-page document in another domain

I want to get at the 'outerHTML' of a node I've captured using document.evaluate (ie xPath) from a node on another web page that is from a different domain. I.e. I have a Firefox tab running my javascript that is trying to access the content of a second tab. I dont have control over the content of the web page in the second tab.
I used importNode along with the answer to a similar question...
How do I do OuterHTML in firefox?
I am able to do other cross domain manipulation, but cant get importNode to work. I only need this to work in Firefox.
This is where I've got to so far - get error message: "Access to property denied code: 1010" ...
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var recordNodeClone = currentFrame.document.importNode(recordNode, true);
var fosterParentNode = document.createElement('div');
//Error for line below: Access to property denied" code: "1010
fosterParentNode.appendChild( recordNodeClone );
var recordNodeOuterHTML = fosterParentNode.innerHTML;
console.log("fosterParentNode=%o", fosterParentNode);
console.log("fosterParentNode.innerHTML=%o", fosterParentNode.innerHTML);

Categories

Resources