Call a javascript function on a crossorigin server - javascript

Hello i am trying to run the following script on a different server:
$$.find('button#reboot').on('click', function() {
var popup = new mimosa.popup();
popup.title('Confirmation');
popup.content('<p>Rebooting will cause service interruption.</p><p>Do you wish to continue?</p>');
popup.ok(function() {
mimosa.system.reboot("Reboot Button");
});
popup.cancel();
popup.show();
});
can anybody tell me or give me an example on how that's done?

What you're looking to do is communicate between window objects cross-origin. This can be accomplished using the postMessage api.
See: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
You'd have to set up a post-message listener on your server. When that listener receives the postMessage, have it run the script.
Receiving window:
window.addEventListener("runScript", receiveMessage, false);
function receiveMessage(event)
{
if (event.origin !== "http://example.org:8080") {
return;
}
...
}

Related

Issues with Cross Document Messaging between IFrame & Parent

I have an application running inside an iframe on a "foreign" page (different domain etc.). To allow some basic communication between the iframe & the parent, I load some script of mine on the parent page and use postMessage to do some cross document messaging.
Most of the time this communication works as intended, but sometimes I see some errors reported to my error tracking tool and can't figure out why they happen.
Here's some exemplary code:
PluginOnParent.js
// ...
window.addEventListener('message', function(e) {
// Check message origin etc...
if (e.data.type === 'iFrameRequest') {
e.source.postMessage({
type: 'parentResponse',
responseData: someInterestingData
}, e.origin);
}
// ...
}, false);
// ...
AppInsideIFrame.js
// ...
var timeoutId;
try {
if (window.self === window.top) {
// We're not inside an IFrame, don't do anything...
return;
}
} catch (e) {
// Browsers can block access to window.top due to same origin policy.
// See http://stackoverflow.com/a/326076
// If this happens, we are inside an IFrame...
}
function messageHandler(e) {
if (e.data && (e.data.type === 'parentResponse')) {
window.clearTimeout(timeoutId);
window.removeEventListener('message', messageHandler);
// Do some stuff with the sent data
}
}
timeoutId = window.setTimeout(function() {
errorTracking.report('Communication with parent page failed');
window.removeEventListener('message', messageHandler);
}, 500);
window.addEventListener('message', messageHandler, false);
window.parent.postMessage({ type: 'iFrameRequest' }, '*');
// ...
What happens here, when the timeout hits and the error is reported?
Some more info & thoughts of mine:
I have no control over the parent page myself
It doesn't seem to be a general "configuration" issue (CORS etc.) since the error happens on the same page where it works most of the time
We don't support IE < 10 and other "legacy" browser versions at all, so those are no issue here
My error reporting tool reports a multitude of different browsers amongst which are the latest versions of them (FF 49, Chrome 43 on Android 5, Chrome 53 on Win and Android 6, Mobile Safari 10, ...)
Therefore it doesn't seem like it's an issue related to specific browsers or versions.
The timeout of 500 ms is just some magic number I chose which I thought would be completely safe...
The problem appears to be in your PluginOnParent.js, where you are sending your response. Instead of using "e.origin" (which upon inspection in the developer tools was returning "null") -- try using the literal '*', as it states in the following documentation on postMessage usage (in the description for targetOrigin):
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Also, as a bonus, I just tested this across two different domains and it works as well. I placed Parent.html on one domains web server, and changed the iframe's src to be child.html on a completely different domain, and they communicated together just fine.
Parent.html
<html>
<head>
<script type="text/javascript">
function parentInitialize() {
window.addEventListener('message', function (e) {
// Check message origin etc...
if (e.data.type === 'iFrameRequest') {
var obj = {
type: 'parentResponse',
responseData: 'some response'
};
e.source.postMessage(obj, '*');
}
// ...
})
}
</script>
</head>
<body style="background-color: rgb(72, 222, 218);" onload="javascript: parentInitialize();">
<iframe src="child.html" style="width: 500px; height:350px;"></iframe>
</body>
</html>
Child.html
<html>
<head>
<script type="text/javascript">
function childInitialize() {
// ...
var timeoutId;
try {
if (window.self === window.top) {
// We're not inside an IFrame, don't do anything...
return;
}
} catch (e) {
// Browsers can block access to window.top due to same origin policy.
// See http://stackoverflow.com/a/326076
// If this happens, we are inside an IFrame...
}
function messageHandler(e) {
if (e.data && (e.data.type === 'parentResponse')) {
window.clearTimeout(timeoutId);
window.removeEventListener('message', messageHandler);
// Do some stuff with the sent data
var obj = document.getElementById("status");
obj.value = e.data.responseData;
}
}
timeoutId = window.setTimeout(function () {
var obj = document.getElementById("status");
obj.value = 'Communication with parent page failed';
window.removeEventListener('message', messageHandler);
}, 500);
window.addEventListener('message', messageHandler, false);
window.parent.postMessage({ type: 'iFrameRequest' }, '*');
// ...
}
</script>
</head>
<body style="background-color: rgb(0, 148, 255);" onload="javascript: childInitialize();">
<textarea type="text" style="width:400px; height:250px;" id="status" />
</body>
</html>
Hope that helps!
Most of the time this communication works as intended, but sometimes I
see some errors reported to my error tracking tool and can't figure
out why they happen.
What happens here, when the timeout hits and the error is reported?
I have no control over the parent page myself
Not certain what the function errorTracking.report does when called, though does not appear that an actual error relating to message event occurs?
The timeout of 500 ms is just some magic number I chose which I
thought would be completely safe...
With duration set at 500, setTimeout could be called before a message event fires at window.
timeoutId = window.setTimeout(function() {
errorTracking.report('Communication with parent page failed');
window.removeEventListener('message', messageHandler);
}, 500);
Adjust the duration of setTimeout to a greater duration.
Alternatively substitute onerror handler or window.addEventListener for setTimeout
Notes
When a syntax(?) error occurs in a script, loaded from a
different origin, the details of the syntax error are not reported to
prevent leaking information (see bug 363897). Instead the error
reported is simply "Script error." This behavior can be overriden in
some browsers using the crossorigin attribute on and having
the server send the appropriate CORS HTTP response headers. A
workaround is to isolate "Script error." and handle it knowing that
the error detail is only viewable in the browser console and not
accessible via JavaScript.
For example
// handle errors
onerror = function messageErrorHandlerAtAppInsideIFrame(e) {
console.error("Error at messageErrorIndex", e)
}
to handle any actual errors during communicating between different contexts, or origins.
Use postMessage at load event of iframe to communicate with message handlers at parent window.
http://plnkr.co/edit/M85MDHF1kPPwTE2E0UGt?p=preview

How to run EventSource inside Shared Worker?

I implemented server-sent events to receive messages from the server. I describe how I implemented it on another question
Now I am trying to run it inside a shared worker to prevent the user from opening multiple connections to the servers via multiple browser tabs.
Here is what I have done to run the code inside a Shared Worker
created a file calles worker.js and I put this code inside of it
self.addEventListener('getMessagingQueue', function (event) {
console.log('Listener is Available');
var thisp = this;
var eventSrc = new EventSource('poll.php');
eventSrc.addEventListener('getMessagingQueue', function (event) {
var message = JSON.parse(event.data);
thisp.postMessage(message);
}, false);
}, false);
Then when the HTML page loads I call this code
$(function(){
var worker = new SharedWorker('worker.js');
worker.addEventListener('getMessagingQueue', function (event) {
var message = event.data;
console.group('Message Received');
console.log( message );
console.groupEnd();
}, false);
});
But the code is not returning messages from the server.
How can I correctly run EventSource event inside the shared worker?

Chrome extension: have an extension listen for an event on a page?

I have a chrome extension that im making for my site, currently i have the extension checking the database every minute for updates.
Is it possible to have the extension listen for an event on the actual page?
Something like this
this.trigger('sendUpdate', data) //this happened on the page
this.on(sendUpdate, function(){ //this is what the chrome extension listens for
//do stuff with data
})
you need to add a content_script. content_script have full access to the DOM and you can bind to all events on page
just add this to the menifest file
"content_scripts":[{
"matches":["http://*/*","https://*/*"],
"js":"your injected script.js"
}]
you can get more info https://developer.chrome.com/docs/extensions/mv2/content_scripts/
also from your question it looks like you going to be working with a custom event so your content_scrip js is going to be something similar to this
document.addEventListener('yourEventName', function(e){
//send message to ext
var someInformation = {/*your msg here*/}
chrome.extension.sendMessage(someInformation, function(response) {
//callback
});
}, false);
the background page should listen for a message.
chrome.extension.onMessage.addListener(function(myMessage, sender, sendResponse){
//do something that only the extension has privileges here
return true;
});
then you can trigger the Event from all scripts on the page...
var evt = document.createEvent('Event');
evt.initEvent('yourEventName', true, true);
var some_element = document;
some_element.dispatchEvent(evt);
For anyone reading this in 2023, the sendMessage method has moved to chrome.runtime.sendMessage.
See documentation for manifest v3 (MV3): https://developer.chrome.com/docs/extensions/mv3/messaging/

Catch X-Frame-Options Error in javascript

Is there any way to catch an error when loading an iframe from another domain. Here is an example in jsfiddle. http://jsfiddle.net/2Udzu/ . I need to show a message if I receive an error.
Here is what I would like to do, but it doesn't work:
$('iframe')[0].onerror = function(e) {
alert('There was an error loading the iFrame');
}
Anyone have any ideas?
The onerror is applicable only for script errors. Frame content error checking must be done using any other method. Here's one example.
<script>
function chkFrame(fr) {
if (!fr.contentDocument.location) alert('Cross domain');
}
</script>
<iframe src="http://www.google.com/" onload="chkFrame(this)"></iframe>
Due to cross domain restriction, there's no way to detect whether a page is successfully loaded or if the page can't be loaded due to client errors (HTTP 4xx errors) and server errors (HTTP 5xx errors).
If both the parent site and the iframe-url is accessible by you, a way to know that the page is fully loaded (without "sameorigin" issues) is sending a message (postMessage) from the child to the parent like this;
Parent site (containing the iframe)
//Listen for message
window.addEventListener("message", function(event) {
if (event.data === "loading_success") {
//Yay
}
});
//Check whether message has come through or not
iframe_element.onload = function () {
//iframe loaded...
setTimeout(function() {
if (!iframeLoaded) {
//iframe loaded but no message from the site - URL not allowed
alert("Failure!");
}
}, 500);
};
Child site (URL from the iframe)
parent.postMessage("loading_success", "https://the_origin_site.url/");
You could get the_origin_site.url by using a server-side language like PHP if you want the possibility for multiple origins
The accepted answer only works if the domain you're trying to put in an iframe is the same as the one you're requesting from - this solution works for cross-domain where you have access to the scripts on both domains.
I am using following code to detect whether a x-frame-option error occured or another with jquery
$(iframe).load(function (e) {
try
{
// try access to check
console.log(this.contentWindow.document);
// Access possible ...
}
catch (e)
{
// Could not access. Read out error type
console.log(e);
var messageLC = e.message.toLowerCase();
if (messageLC.indexOf("x-frame-options") > -1 || messageLC.indexOf('blocked a frame with origin') > -1 || messageLC.indexOf('accessing a cross-origin') > -1)
{
// show Error Msg with cause of cross-origin access denied
}
else
{
// Shoe Error Msg with other cause
}
}
});

Emit message to add-on from content script onbeforeunload?

I have a content script which times how long a user views a page. To do this, I inject a content script into each page, start a timer and then emit a message back to the add-on when the onbeforeunload event is triggered.
The message never seems to get passed to the background script however.
Given that my main.js looks like this:
var pageMod = require('page-mod'),
self = require("self");
pageMod.PageMod({
include: "http://*",
contentScriptFile: [self.data.url('jquery.min.js'),
self.data.url('content.js')],
onAttach: function(worker) {
worker.port.on('pageView', function(request) {
console.log("Request received");
});
}
});
I can send a message to main.js using the following code no problem.
self.port.emit('pageView', { visitTime: time });
I run into a problem when I try to do it as the user leaves the page however. The message is never received when I do it like this:
$(window).bind('onbeforeunload', function(e) {
self.port.emit('pageView', { visitTime: time });
// This should prevent the user from seeing a dialog.
return undefined;
});
I've tried listening for beforeunload too, that doesn't work either. What could be the problem?
The window object that content scripts access in Firefox browser add-ons is a proxy object and can be a little temperamental. Using window.addEventListener will work.
window.addEventListener('beforeunload', function(e) {
# Do stuff then return undefined so no dialog pops up.
return undefined
});
The onbeforeUnload event is not synchronous, so the browser garbage collects the page before it is finished. Use a synchronous AJAX request:
function Data()
{
var client = new XMLHttpRequest();
client.open("GET", "/request", false); // third paramater indicates sync xhr
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send({ visitTime: time });
client.onreadystatechange = emitter;
}
function emitter()
{
self.port.emit('pageView', { visitTime: time });
}
or return a string as an alternative.

Categories