Is it possible to send window messages to an embedded website? - javascript

I'm creating a script that runs on every website (using temperMonkey). On a shortcut I inject a form into the page. So if I am in stackoveflow, I pres cmd+esc and I embed a local vuejs page
Step 1: Inject an iframe to (say) stack overflow and send a message to the embedded site**
var iframe = document.createElement("iframe");
iframe.type="text/html"
iframe.src="http://localhost:8080/webhighlights"
iframe.id="localFrame"
var data = {title: document.title, url: window.location,}
iframe.onload= function(){
var t = document.getElementById('localFrame');
t.contentWindow.postMessage({
data
}, 'http://localhost:8080/webhighlights');
}
Step 2: get the message from the site that has embedded me:
mounted: function () {
/* eslint-disable */
window.top.addEventListener('message',receiveMessage, false);
function receiveMessage (event) {
console.log('event data: ', event)
}
}
The message doesn't come through. The error I get in the console is Failed to execute 'postMessage' on 'Window': Location object could not be cloned. at HTMLIFrameElement.iframe.onload

You are sending a message to your iframe's context, so from this iframe you don't want to listen for top's messages, but only for this iframe's window ones.
Change
window.top.addEventListener('message',...
to
window.addEventListener('message',...
If you want to understand better how messaging works, I invite you to read this answer of mine.
Baiscally with an iframe, one port is in the main context, under HTMLIframeElement.contentWindow, and the other one is in the iframe's context, under window. To communicate between both contexts, each context should listen to and talk in their own port.
When this is fixed you will face a new issue that the location object can't be cloned. Since apparently you only want to send the current URL of the main page, then only send this:
var data = {title: document.title, url: window.location.href };

Related

The target origin does not match the recipients window's origin

I am trying to post a message from a MVC website that I am hosting in an iframe in a Angular website but when using window.postMessage I get the above error. This is my code:
In MVC site (called via iframe):
// I need to tell Angular when something has changed here to update their menu
var data = { foo: 'bar' }
window.postMessage(data, 'http://localhost:4200/');
Angluar listening to message post:
renderer.listen('window', 'message', (event) => {
if (event.origin.startsWith('http://localhost')) {
console.log(event.data) // should output: {foo: 'bar'}
}
});
What am I missing here?
With this code, you are trying to post a message from the iframe to the iframe itself. The error about the target origin occurs because the recipient window's origin is actually the embedded app's one. Indeed, if you want to notify the parent window, dont refer to it as window but as window.parent (window being the MVC app's window):
window.parent.postMessage(data, 'http://localhost:4200/');

Get url from iframe when origin is not the same

I want to get the URL from an iframe when the user redirects by clicking links in the iframe. The source of the iframe is not the same as the web application.
For example:
<iframe src="startingUrl" class="embed-responsive-item" id="iframe" sandbox="" allowfullscreen</iframe>
I add a load listener on the iframe to detect when the user redirects to other urls in this iframe:
const iframe = document.getElementById("iframe");
iframe.addEventListener("load", (evt) => {
const location = iframe.contentWindow.location;
console.log(location); // this gives me a Location object where I can see the href property
console.log(location.href); // this gives me a SecurityError: Permission denied to get property "href" on cross-origin object, I also tried to get a copy of the object but that doesn't work either.
});
I know what causes this problem and I also know it is not possible. But I need to find a way to get the current URL of the page. If this is a no go then I want that the user who uses this web application can copy the url of the iframe and put it in an input field.
Now they can do "View frame source" in chrome and This frame: view frame source or info in Firefox. But this is too complicated for the user. Is there a way they can see the URL in the iFrame or a way for the user to get the URL simpler.
The site in the iFrame is not mine.
All help is much appreciated!
Short answer: This is a no go, unless you have the support of the other site in your iframe and they are willing to add the code in #박상수 answer.
Longer answer: You could set up a proxy server to inject the required code to make this work, but then you will run into legal and ethical difficulties, so I am not going to explain how to do that in depth.
Another approach might be to create a browser extension and have your users install that. Again I should point out FaceBook has in the past ran into ethical difficulties taking this approach.
Ultimately their are very good security reasons why the browser stops you doing this and you should probably respect those reasons and not do it.
If you don't see the code below, check the link below.
console.log(iframe.src);
Check out this link
SecurityError: Blocked a frame with origin from accessing a cross-origin frame
let frame = document.getElementById('your-frame-id');
frame.contentWindow.postMessage(/*any variable or object here*/, 'http://your-second-site.com');
window.addEventListener('message', event => {
// IMPORTANT: check the origin of the data!
if (event.origin.startsWith('http://your-first-site.com')) {
// The data was sent from your site.
// Data sent with postMessage is stored in event.data:
console.log(event.data);
} else {
// The data was NOT sent from your site!
// Be careful! Do not use it. This else branch is
// here just for clarity, you usually shouldn't need it.
return;
}
});
You will want to override the error being automatically thrown:
const iframe = document.getElementById('iframe');
iframe.addEventListener('load', evt => {
const loc = iframe.contentWindow.location;
try{
loc.href;
}
catch(e){
if(e.name === 'SecurityError'){
console.log(iframe.src);
}
}
});
<iframe src='https://example.com' class='embed-responsive-item' id='iframe' sandbox='' allowfullscreen></iframe>

window.location.state does not work as expected

I have one situation,where i need to pass some json from one window to new window in the same domain.I have first window lets say it window1 and second window,let say it window2.
I have following code in window1:
var params = [
'height=750',
'width=720',
'scrollbars=yes',
'left=0',
'top=0',
'fullscreen=no', // only works in IE, but here for completeness
'location=no'
].join(',');
var port = location.port;
var url = "http://" + hostName + ':' + port + "/isecalreport" + location.search;
var newWindow = window.open(url,'photocal_report',params);
while(true){
if(newWindow! == undefined) {
newWindow.location.state={payloadFromIseCalWeekly : payloadFromIseCalWeekly,instrumentIdObj : instrumentIdObj};
break;
}
}
Code in window2:
var payloadFromIseCalWeekly = location.state.payloadFromIseCalWeekly ? location.state.payloadFromIseCalWeekly : {};
I want to make use of the json set in window.location.state.
So the problem is ,It works fine in chrome ,mozilla,but fails in IE 11(when debugger is not open.)
When i open debugger in IE11 ,it works fine.
I debugged it and found out that after the instruction which is opening the new window ,the next instruction get run and it doesnot find the new window object.
Its strange as it works when developer console is open.
It would be good if i can get insights about how to resolve the issue.
My aim is to open a new window ,to which i need to pass some data and using that data i want to do an API call.
With few exceptions, you cannot tell one window, tab or frame to talk to another directly. This is to prevent malicious scripts in one of these contexts from hijacking another across pages.
To cope with this, you have two options, you can use postMessage() or simply pass your data via the URL that you open in the new window. There are technically more options if you're on the same domain, but I recommend against going down that rabbit hole.
Sending data via the URL is a one-way affair. You can send query string variables in the URL that the new window can read, but it can't send anything back to the window that created it.
postMessage(), on the other hand, can be used to communicate between multiple contexts and across domains. It is considered secure because it requires that all participants be listening for messages, rather than allowing direct code access.
Your various pages can listen for messages with a simple event listener.
// listen for incoming messages on this page
window.addEventListener('message', function(e) {
// this is the handler function
// do we trust where this was sent from?
if (event.origin !== "http://example.com") {
// if so, print the resulting event object
console.log('message received', e);
}
}, false);
You can then send a message from another page to your window.
// the * is the targetOrigin, read the docs!
newWindow.postMessage("some message data", "*");

Get URL of popup window opened from a Chrome Extension

I've got a Chrome extension that creates a popup window that the user needs to use for a login system. You click on the extension icon and it opens up its application (AngularJS in my case). The user then clicks on a button which calls chrome.windows.create() to open a popup.
I would like the main extension app to monitor the URL of that popup for changes.
I create the popup from the extension this way:
chrome.windows.create(
{
url: 'https://some.external.url.com/whatever',
type: 'panel',
width: 600,
height: 600
},
function (windowReference) {
console.log('My Window:', windowReference);
// start monitoring URL of windowReference somehow
// could be as simple as a setInterval() loop
}
)
The problem is that the windowReference object passed to the callback doesn't have the current URL of the popup. Since the user can interact with the page in the popup (I'm pointing it at out OAuth2 system), the URL will change at times. I want to see that - either actively as changes are made, or by simply querying the current URL periodically.
This is what the windowReference object contains:
{
alwaysOnTop:false,
focused:false,
height:600,
id:1089,
incognito:false,
left:61,
state:"normal",
top:23,
type:"popup",
width:600
}
You can see that there is an ID there, and that, to me, suggest that I might be able to use it to call some other method to get the real URL information I'm after.
Any help would be appreciated.
So the answer turns out to be pretty simple. As Rob W mentioned in a comment, you use the chrome.tabs.query() method to do the search as you would for any other tab.
The missing part for me was that you can use the id from the window reference you get when the popup is created to get the desired results from the tabs query:
chrome.tabs.query(
{ windowId: windowReference.id },
function callback(tabs) {
var popup = tabs[0];
$log.debug("Popup URL:", popup.url);
}
);
You can see that I passed the ID as the value of the windowId parameter in the search query object.

Using easyXDM to communicate between parent document and child iframe loaded from a different domain (amazon)

I'm trying to use easyXDM to communicate between parent document and child iframe (loaded from a different domain - amazon). The iframe src is an oauth signed url and has the following code to communicate with the parent document that loads it:
socket = new easyXDM.Socket({
remote: "http://localhost:56789/hitch.html", /* parent document */
remoteHelper: "http://localhost:56789/easyXDM/name.html",
onMessage: function(message, origin){
alert("Received '" + message + "' from '" + origin + "'");
},
onReady: function() {
socket.postMessage("Yay, it works!");
}
});
the above code is kept in the head portion of the document.
In parent (hitch.html):
var transport = new easyXDM.Socket(/** The configuration */{
local: "/easyXDM/name.html",
swf: "/easyXDM/easyxdm.swf",
onMessage: function(message, origin){
transport.postMessage("This is a message received from " + location);
}
});
When I load the child iframe from amazonS3 inside the parent document, easyXDM is creating another iframe inside the child iframe with src set to "http://localhost:56789/hitch.html?xdm_e=..." . This causes the whole thing to be repeated in a cycle - with parent again trying to load the child iframe and so on.
I'm testing on Firefox 9.0 which has postMessage support. The actual messages are being sent properly and I can see the message boxes. Other than this, it also throws a "url is undefined or empty" error in parent document when initializing easyXDM.socket but it doesn't seem to affect anything else...
Thanks,
I think you've just got the logic backwards. The documentation says quite clearly:
"When using easyXDM you first load the consumer document and then let
easyXDM load the provider."
The "consumer" is the parent document, and easyxdm loads the "provider" which is the child iframe.
ref - https://github.com/oyvindkinsey/easyXDM

Categories