Cross Domain communication from Child (iframe) to Parent not working - javascript

I have a component within AEM (Adobe Experience Manager - a cms) on a page and I want to include this page onto another page (from a different domain) using an iframe. So in the code for the component I am using window.postMessage() and I'm trying to listen to that event in the parent. I have tried communicating the other way, parent to iframe and it worked fine, but I need to communicate the other way. So the component is a search component and when you click on a search result I want to redirect but from the parent window so I'm trying to send the URL to redirect to and then handle the redirection within the parent's JS code.
The code looks like:
(From the parent - html)
<iframe
width="1080"
height="700"
id="theFrame"
src="http://localhost:4502/content/zebra1/global/en_us/hey.html#q=print"
frameborder="0">
</iframe>
(From the parent - js)
function receiveMessage(e)
{
var key = e.message ? "message" : "data";
var data = e[key];
var redirect = JSON.parse(data);
redirectUrl = (redirect.origin ? redirect.origin : '') + (redirect.url ?
redirect.url : '');
if (redirectUrl) {
window.location.href = redirectUrl;
}
}
window.addEventListener("message", receiveMessage, false);
(From the iframe/child - js)
goToSearchResults : function( event ){
var windowOrigin = location.origin;
if( arguments[0].length == 3){
var redirect = {
origin: windowOrigin,
url: arguments[0][1].url || ''
};
if(!$('#supportSearchWrap').data('iframe')) {
location.replace(redirect.url);
} else {
window.postMessage(JSON.stringify(redirect), windowOrigin);
}
}
logger.log( redirect.origin + redirect.url , this.model );
}
It's not working for me. Does anyone see what I'm doing wrong or a better way to do this?

window.postMessage - The window refers to the instance of the window object to which you're posting your message. In your case, it should be the parent of the iframe window.
You can get that reference inside the iframe using window.parent or simply parent.
Also, the targetOrigin property should match the targeted window properties. From MDN docs, it is as below.
targetOrigin
Specifies what the origin of otherWindow must be for the event to be dispatched, either as the literal string "*" (indicating no preference) or as a URI. If at the time the event is scheduled to be dispatched the scheme, hostname, or port of otherWindow's document does not match that provided in targetOrigin, the event will not be dispatched; only if all three match will the event be dispatched.

Related

how to pass data to variables in iframe

I am working on project which connects a local server and global server (servers cant communicate) server communicate to html files via socket io
I am very new to this concept
my scenario is
browser connects to local app
local server sends html page which has iframe in it and iframe's source is global server
global server sends html page
my browser is showing html page in the global server and my local server is also running
how can I pass data from local server to global server?
basically local server sends data to container html file and it passes data to the html file in iframe so that it can pass data to global server
maybe a method is called from container html to iframe html and sends data ? is it possible?
hope I made my point clear
please help
What i understood that you need to pass some kind of variable in iframe so you can append your iframe's "src" property with it in the form of querystring.
You can get this querystring value while reloading of iframe.
e.g.
<div class="my-frame-container" myurl="http://myserver.com" elementid="mycontainerframe"><iframe id="myiFrame" src="" allowtransparency="true" width="100%" height="100%" frameborder="0"></iframe></div>
someEvent(e.g.: click)
window.parent.postMessage(
{
event_id: 'reloadMyFrame',
},
"*"
);
and in the iframe js file you can add a event listener
var eventMethod = window.addEventListener ? "addEventListener" :
"attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" :
"message";
eventer(messageEvent, function (e) {
var eventId = e.data["event_id"];
if(eventId==="reloadMyFrame")
{
var container = $('.my-frame-container');
var frameSrc = container.attr('myurl');
$('#myiFrame').attr("src", url);
$('#myiFrame').reload();
}
}
}
The best way is using event bus javascript
//main doc
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
console.log("main",event);
// ...
}
window.document.getElementById("iframe").contentWindow.postMessage('test', *);
//iframe
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
console.log("iframe",event);
// ...
}
window.parent.postMessage('test', *);
be aware of CORS problematic
https://developer.mozilla.org/docs/Web/API/Window/postMessage
More complet exemple
post message works fine but instead of post message you can call methods inside frame like an instance
$('#myIFrame').on('load',()=>{
let q = document.getElementById("myIFrame").contentWindow
q.method() // a method inside iFrames js
})
and you can call method from inside iFrame to main like
window.top.method();
BUT it will get caught on Uncaught DOMException: Blocked a frame with origin () from accessing a cross-origin frame. if you disable-web-security it will work fine

Access an Iframe variable from parent window

I have a webpage at url https://parent.com and it has an iframe injected into it with source https://iframe.com. Iframe has a global variable defined called iframe_variable. I want to access the iframe_variable from parent document.
I know browsers don't allow cross origin communication and they provide a postMessage API to do it securely.
Constraint: I do not have access to any of parent or iframe code.
On Browser console, I somehow want to access iframe_variable
I have tried the following:
Get reference of iframe first.
var iframe = document.getElementsByTagName('iframe')[0]; // There is only one iframe on document
Create a listener for message event posted from parent window.
var iframeListener = function(e) {
console.log("Got message from parent");
e.source.postMessage(JSON.stringify({'IFRAME_VARIABLE': window.IFRAME_VARIABLE}));
}
Create a listener for parent window to accept 'message' posted from iframe.
parentListener = function(e) {
console.log('Got message from iframe');
var data = JSON.parse(e.data);
window.VARIABLE = data.IFRAME_VARIABLE;
}
Attach parent_listener to message event.
window.addEventListener('message', parentListener, false);
Now if i try to post a message to iframe from parent as follows:
iframe.contentWindow.postMessage('test message', '*')
It doesn't trigger 'iframeListener'. The reason is because it is not registered against the message event in iframe.
I don't think I can even do that from the browser console when I am on parent.com as any attempt to do iframe.contentWindow.addEventListener will result in an error as it will be an attempt to access a different domain.
Is there a workaround that? Is there anything that I am missing in my understanding and research.
P.S: I have not written the origin checks for simplicity. I know I must check for the origin a message is posted from. Not doing that leaves a huge security hole.

Get the height of a same domain iframe when that iframe is inside a different domain iframe?

I have a site which has a media player embedded inside an iframe. The media player and the site are on the same domain, preventing cross-origin issues. Each page, the main page as well as the media player page, have a bit of code which finds the height and width of any parent iframe:
var height = $(parent.window).height();
var width = $(parent.window).width();
No problems so far....until:
A client wants to embed my site inside an iframe on his own site. His site is on a different domain. Now, my iframe is inside another iframe and my code is throwing cross-origin errors.
The following does not throw errors:
var test1 = parent.window; // returns my site
var test2 = window.top; // returns client site
The following does throw cross-origin errors:
var test3 = parent.window.document;
var test4 = $(parent.window);
var test5 = window.top.document;
var test6 = $(window.top);
How do I get the height of the iframe on my domain without the cross-origin errors? I'm hoping for a pure javascript/jQuery solution.
Options which will not work for my solution are:
Using document.domain to white list the site.
Modifying the web.config to white list the site.
Like in Inception, I must go deeper. Please help.
You will need to use Javascript's messager. First, you need to define a function like this:
function myReceiver(event) {
//Do something
}
Then you need an event listener:
window.addEventListener("message", myReceiver);
You will need something like this on both sides. Now, you can send a message like this to the iframe:
innerWindow.contentWindow.postMessage({ message: {ResponseKey: "your response key", info1: "something1", info2: "something2"}}, innerWindow.src)
and this is how you can send a message to the parent:
window.parent.postMessage({ message: {ResponseKey: "your response key", info1: "something1", info2: "something2"}}, myorigin);
The only missing item in the puzzle is myorigin. You will be able to find it out in your iframe using event.origin || event.originalEvent.origin in the message receiver event.
However, the pages using your site in their pages inside an iframe will have to include a Javascript library which will handle the communication you need. I know how painful is this research, I have spent days when I have done it before to find out the answer.
Your code is running from the iframe in the middle of the parent and the child window. So, anytime you call
window.parent
and your site is embedded inside an iframe and the parent is a different domain (Same origin policy), an error will be thrown. I would recommend first checking if the parent is the same origin. You need to wrap this check in a try catch.
NOTE: Most browsers, but not Edge, will not throw an error if the parent is http://localhost:xxx and the iframe is http://localhost:zzz where xxx is a different port number than zzz. So, you also need to manually check the origins match by comparing the protocol, domain, and port.
var isEmbeddedInCrossOriginIframe = false;
try {
var originSelf = (window.self.location.protocol + '//' +
window.self.location.hostname +
(window.self.location.port ? ':' +
window.self.location.port : '')).toLowerCase();
var originParentOrSelf = (window.parent.location.protocol + '//' +
window.parent.location.hostname +
(window.parent.location.port ? ':' +
window.parent.location.port : '')).toLowerCase();
isEmbeddedInCrossOriginIframe = originSelf != originParentOrSelf;
}
catch(err) {
isEmbeddedInCrossOriginIframe = true;
//console.log(err);
}
Your solution will then be:
var height = $(isEmbeddedInCrossOriginIframe ? window : parent.window)
.height();
var width = $(isEmbeddedInCrossOriginIframe ? window : parent.window)
.width();

How to postmessage HTMLIFrameElement?

I'm having issue passing HTMLIFrameElement object from parent site to iframe (located on a different domain) using postMessage method.
This is my code that I already tried:
var frame = document.getElementById('myHTMLIFrameElement');
frame = JSON.parse(JSON.stringify(frame));
event.source.postMessage(frame, "*");
Unfortunately, JSON.parse/JSON.stringify does not seem to be the right way of handling HTMLIFrameElement object. Can you please advise how to pass through HTMLIFrameElement object correctly?
Parsing DOM elements as JSON does not do anything useful as far as I know.
Since postMessage() is part of the window object, you could try using the window object of the frame, which is found under the contentWindow property of the iframe.
So you could try something like:
var frame = document.getElementById('myHTMLIFrameElement');
frame.contentWindow.postMessage( "stuffYouWantToSendToTheIframe", '*' );
And then in the script inside the iframe:
window.addEventListener('message', function( event ) {
// handle message
});

Issue communication with postMessage from parent to child iFrame

I'm having an issue communicating from my parent window to the child iFrame. But in the other side, everything works perfectly.
Here is how I get the chil iFrame object in order to fire the postMessage function:
var iFrame = document.getElementById('Frame').contentWindow;
When I print it int he console, I get the following:
Window {parent: Window, opener: null, top: Window, length: 0, frames: Window…}
When I proceed to the postMessage function as follows:
iFrame.postMessage("message", "http://contoso.com");
It shows me an error when loading the page: iFrame.postMessage is not a function.
When I execute the postMessage in console, I get an undefined
What am I doing wrong ?
try this
var iFrame = document.getElementById('Frame');
iFrame.contentWindow.postMessage("message", "http://contoso.com");
I had this problem too. I found solution from this website https://www.viget.com/articles/using-javascript-postmessage-to-talk-to-iframes
I wasn't able to get this working using a querySelector approach.
What worked for me was the following. I'll refer to the two webpages as the parent that has an iframe on it and the src as the page inside the iframe.
On the src page, I post a message, with the parent url as the origin:
// src
window.parent.postMessage({
type: "connect",
url: window.location.href
}, "http://iframeparent.dev", );
Then in the parent page, I listen for this. It will have a property called source which is the iframe in question. This property can be posted to.
// parent
window.addEventListener("message", (event) => {
// this is the magic connection:
event.source;
}, false);
So you could do something like:
// parent
let sources = [];
window.addEventListener("message", (event) => {
sources.push(event.source);
}, false);
function something() {
sources.forEach(source => {
source.postMessage({some: "data"}, "http://iframesrc.dev")
})
}
Below code also works.
$('#id')[0].contentWindow.postMessage("hello world",targetOrigin);
There is a difference between jQuery selector and document.getElementById.
Document.getElementByID returns HTML DOM object.
jQuery selector returns jQuery object.
For more information please find below link.
document.getElementById vs jQuery $()

Categories

Resources