Using postMessage between iframes

Reading time ~4 minutes

There were plenty of resources regarding the use of postMessage here and there, however, none focused on reproducing the bugs locally. Just “copy this line in the iframe, this line in the page hosting the iframe and everything will work.”

I had the same issue and wanted to fix it and make sure it was running without long build, deploys etc

Here we will start from scratch, using python to serve pages locally. First we will serve two pages on the same port, then ensure postMessage between these pages works, break it with serving it on different ports and finally fix the iframe communication.

Note that I do not focus on the origin of the event checks below, but developer.mozilla.org does.

Sending data to a parent frame with postMessage

Step 1 : creating and serving two pages

Let’s start with serving a page, containing an iframe locally, at the same address. For this you will need a web browser, and a working python distribution. In what follows, everything will be served on port 4000.

index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>I am the main page</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>

	<body>
		<h1>I am the main page</h1>  
		<iframe id="iframe" src="http://localhost:4000/iframe.html" height="600" width="800">
		</iframe>
	</body>
</html>

iframe.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>Iframe</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>

	<body>
		<h1>I am the iframe</h1>  
		<p>I should be in the site</p>
	</body>
</html>

Side note, I remembered that serving a page locally involved a “python http.server”. The following trick should be known by everybody using terminal, if you want to avoid googling again and again ;)

user@user:~$ history | grep http.se
 1283  python3 -m http.server 4000
 [...]

The command I was looking for was:

python3 -m http.server 4000

Opening localhost in your browser should look like…

iframe sample

Step 2 : postMessage to parent frame

Let’s add some javascript in the pages so that the iframe can send data to the parent frame. Basically, what happens below is that the iframe manages to run a function located in the parent window.

You can check it by clicking the button once the pages are served.

index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>I am the main page</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>

	<body>
		<h1>I am the main page</h1>  
		<iframe id="iframe" src="http://localhost:4000/iframe.html" height="600" width="800">
		</iframe>
	</body>
</html>

iframe.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>Iframe</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>


	<body>
		<h1>I am the iframe</h1>  
		<p>I should be in the site</p>
		<button id="my-btn">Send to parent</button>
	</body>
</html>

<script>
	document.getElementById('my-btn').addEventListener('click', handleButtonClick, false);
	function handleButtonClick(e) {
		window.parent.postMessage('message');
		console.log("Button clicked in the frame");
	}	
</script>

Step 2: emulating cross domain, locally

Triggering the error

Now let’s try to serve the frame and the site on “different domains”. For this, we need to move the pages in different folders, so that it looks like:

.
├── iframe
│   └── index.html
└── index
    └── index.html

We need to run two instances of http.serve, the main page will be served on port 4000 and the iframe on port 4200.

user@user:~/Codes/iframe-communication/index$ python3 -m http.server 4000

and

user@user:~/Codes/iframe-communication/iframe$ python3 -m http.server 4200

While the contents of the pages will be (note that the port to the iframe has to be changed):

index/index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>I am the main page</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>


	<body>
		<h1>I am the main page</h1>  
		<iframe id="iframe" src="http://localhost:4200" height="600" width="800">
		</iframe>
	</body>
</html>

<script>
	window.addEventListener('message', function() {
		alert("Message received");
		console.log("message received");
	}, false);
</script>

iframe/index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>Iframe</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>


	<body>
		<h1>I am the iframe</h1>  
		<p>I should be in the site</p>
		<button id="my-btn">Send to parent</button>
	</body>
</html>

<script>
	document.getElementById('my-btn').addEventListener('click', handleButtonClick, false);
	function handleButtonClick(e) {
		window.parent.postMessage('message');
		console.log("Button clicked in the frame");
	}	
</script>

Clicking on the button, you should see the following error in your console:

(index):24 Failed to execute 'postMessage' on 'DOMWindow': 
The target origin provided ('null') does not match the recipient 
window's origin ('http://localhost:4000').

Fixing the error

The message is clear enough, there is a missing argument : the iframe should now the address of the parent.

Writing the following:

index/index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>I am the main page</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>


	<body>
		<h1>I am the main page</h1>  
		<iframe id="iframe" src="http://localhost:4200" height="600" width="800">
		</iframe>
	</body>
</html>

<script>
	window.addEventListener('message', function() {
		alert("Message received");
		console.log("message received");
	}, false);
</script>

iframe/index.html

<!doctype html>

<html lang="en">
	<head>
		<meta charset="utf-8">

		<title>Iframe</title>
		<meta name="description" content="Iframe communication tutorial">
		<meta name="author" content="TheKernelTrip">

	</head>


	<body>
		<h1>I am the iframe</h1>  
		<p>I should be in the site</p>
		<button id="my-btn">Send to parent</button>
	</body>
</html>

<script>
	document.getElementById('my-btn').addEventListener('click', handleButtonClick, false);
	function handleButtonClick(e) {
		window.parent.postMessage('message', "http://localhost:4000");
		console.log("Button clicked in the frame");
	}	
</script>

Python plot 3d scatter and density

It is often easy to compare, in dimension one, an histogram and the underlying density. This is quite useful when one want to visually ev...… Continue reading

Best casual readings in mathematics

Published on May 03, 2020

LightGBM on the GPU

Published on April 29, 2020