Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Try & Catch - Trouble Capturing Window.postMessage() Errors

I am having trouble catching errors when using window.postMessage().

I want to be able to capture the error I am getting -

"Unable to post message to http://www.that-domain.com. Recipient has origin http://www.this-domain.com

Simple example code ( should error ):

 try {
    window.postMessage('1','http://www.differentDomain.com');
 } 
 catch (e) {       
     alert('error');
 }

More detailed process flow: I am adding a cross domain iframe to the document with jQuery and then posting to it. This shouldn't error because the target origins should match - they are both set by the proxyDomain variable.

var $iframeProxy = $('<iframe id="myIFrame" src="' + proxyDomain + '"></iframe>').appendTo('body');

window.storageProxy = $iframeProxy[0].contentWindow;

try {
    window.storageProxy.postMessage(message, proxyDomain);
}
catch (e) {       
    alert('error');
}
like image 521
Kevin M Avatar asked Jun 13 '13 06:06

Kevin M


People also ask

What is try in JavaScript?

The try statement allows you to define a block of code to be tested for errors while it is being executed. The catch statement allows you to define a block of code to be executed, if an error occurs in the try block. The JavaScript statements try and catch come in pairs: try { Block of code to try.

Should I use try-catch in JavaScript?

The try-catch statement should be used any time you want to hide errors from the user, or any time you want to produce custom errors for your users' benefit. If you haven't figured it out yet, when you execute a try-catch statement, the browser's usual error handling mechanism will be disabled.

How many try blocks JavaScript?

Note: catch {} statement executes only after the execution of the try {} statement. Also, one try block can contain one or more catch blocks.

What is try-catch and finally in JavaScript?

The try statement defines the code block to run (to try). The catch statement defines a code block to handle any error. The finally statement defines a code block to run regardless of the result. The throw statement defines a custom error. Both catch and finally are optional, but you must use one of them.


3 Answers

No way to catch the error, but usually the error happens if the target origin is different, the case is test vs production scenario.

So you can check what is the parent domain and change the target origin accordingly:

function getTargetOrigin()
{
    try {
        var url = (window.location != window.parent.location) ? document.referrer : document.location.href;
        if (url.indexOf('www.myproduction-website.com')!=-1)
            return document.location.protocol + '//www.myproduction-website.com'
        else //set the alternative target
            return document.location.protocol + '//' + url.split('/')[2];
    }
    catch(e) //falback to production
    {
        return document.location.protocol + '//www.myproduction-website.com'
    }
}

and use it inside the postMessage function:

function postMessage(message){
    window.top.postMessage( message,getTargetOrigin());
}

With this solution you can use the same code in multiple server configuration without hard coding the targetOrigin url.

This solution fails if you navigate inside the frame and you recheck the document.referrer, in that case it will contain not the parent url but the previous frame url. In that case consider to use the '*' as targetOrigin url, it's the only working solution to send messages to other domains.

Hope it helps!

like image 155
Matteo Conta Avatar answered Sep 19 '22 11:09

Matteo Conta


Looks like it is outlined in the HTML5 spec that if the domain origins do not match then no error is thrown and it should abort silently.

http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#web-messaging

10.4.3 Posting messages - Part #9

"...if the targetOrigin argument is an absolute URL, and the Document of the Window object on which the method was invoked does not have the same origin as targetOrigin, then abort these steps silently."

like image 25
Kevin M Avatar answered Sep 18 '22 11:09

Kevin M


I would add to matteo-conta's solution improved security, to prevent fake domains like www.myproduction-website.com.fakedomain.com from receiving messages:

function getTargetOrigin() {
    const domainDev = 'mydev-website.com';
    const domainProd = 'myproduction-website.com';
    try {
        var url = (window.location !== window.parent.location) ? document.referrer : document.location.href;
        const domain = url.split('/')[2];
        if (domain.endsWith(domainDev) || domain.endsWith(domainProd))
            return document.location.protocol + domain;
        else return '';
    } catch(e) {
        return ''
    }
}
const targetOrigin = getTargetOrigin();
try{
    if (targetOrigin) window.parent.postMessage(${msg}, targetOrigin);
} catch(e) {
    window.parent.postMessage(e, '*')
}

I also added posting the error message to any targetOrigin so that you would be able to capture it on your website's end using:

window.addEventListener("message", onResponse, false);
onResponse(res) {
    const data = _.get(res, 'data');
    if (res.origin === 'iframe-url-goes-here') {
        console.log('message from iframe', data)
    }
}
like image 43
Alex Pavtoulov Avatar answered Sep 16 '22 11:09

Alex Pavtoulov