Browsing contexts and script execution across them

Aditya Daflapurkar
WhatfixEngineeringBlog
6 min readMar 25, 2022
Photo by Guilherme Vasconcelos on Unsplash

A browsing context is an environment in which the browser displays a document to the user. The most common examples of browsing contexts are windows, tabs, and iframes. The displayed document is associated with a window object and an origin. These two parameters play a major role when we are trying to execute a script inside one browsing context from another browsing context.

Origin of a browsing context

Origin of a browsing context is defined by the scheme, host, and port of its URL. Some examples of origins are https://www.google.com, http://localhost:9000, etc. Two browsing contexts with different origins are isolated from each other based on the Same Origin Policy.

Script execution across browsing contexts

Consider that there are two browsing contexts A and B in the browser in the current session. Functions func_A and func_B are declared at the top level in the documents of A and B respectively. We want to execute func_B from func_A.

<!-- Document inside A -->
<html>
<head>
<script>
// Script inside A
function func_A() {
// Trigger func_B from here
}
func_A();
</script>
</head>
</html>
<!-- Document inside B -->
<html>
<head>
<script>
// Script inside B
function func_B() {
console.log('func_B triggered');
}
</script>
</head>
</html>

This kind of script execution across two different browsing contexts is highly constrained due to security reasons and there are specific methods defined to perform this task. In this blog, we will see different scenarios for this use case and the method applicable for solving each scenario.

Scenario 1: When A has a reference to the window object associated with B

This scenario is commonly seen when both A and B are iframes on the same web page or when A and B share a parent-child relationship(i.e. any one of them has created the other through methods like window.open()). In such cases, A can get a reference to B’s window object. Based on the origins of A and B, there are two sub cases in this scenario:

a) When A and B are having the same origin

If both A and B are iframes having the same origin and A has a reference to B, then the scripts in B will be accessible from A. In this case, func_B can be directly invoked from func_A. The following code snippet shows direct invocation of func_B from func_A for a case where both A and B are iframes with the same origin on the same webpage and iframe B has id id_B:

Direct invocation of func_B from func_A

b) When A and B are having different origins

In this case, the same-origin policy prevents accessing scripts in B from A. The window.postMessage method is defined for such scenarios and it allows communication between the browsing contexts through message passing. The method has the following two signatures:

postMessage(message, targetOrigin)
postMessage(message, targetOrigin, [transfer])

Here, message is the data to be transferred to the target window. targetOrigin is a string parameter indicating the required origin of the target window for the message event to be dispatched. It can be set to “*” if the message is to be dispatched for any target window origin. The transfer parameter is optional and it is a sequence of transferable objects whose ownership is given to the target window.

Triggering postMessage method on target window reference dispatches a message event to the target window. The target window should contain an event listener for “message” event which will execute the targeted function(func_B in our case).

The following code snippet shows the case where both A and B are iframes with different origins on the same webpage and iframe B has id id_B. The origin of iframe A is same as that of the top window. Thus, its scripts can query the DOM inside window.top.

Message passing using window postMessage method

Scenario 2: When A doesn’t have a reference to the window object associated with B

If A and B are two separate tabs or windows and they don’t share a parent-child relationship, then A would not have the reference to B’s window object. In these cases, A and B cannot communicate directly. There has to be a mediator component between A and B, which will receive messages from A and forward them to B. Let’s see the two sub cases for this scenario based on origins of A and B.

a) When A and B are having the same origin

The BroadcastChannel interface is the solution defined for handling this scenario. It is a named channel that can be subscribed to by browsing contexts of a particular origin. It only allows communication between browsing contexts of the same origin. Both the source and target browsing contexts need to create a BroadcastChannel object with the same name. Here, name is a parameter passed to the constructor which uniquely identifies the BroadcastChannel. The source context will dispatch a message to the BroadcastChannel using postMessage method and the target context will add a message listener on its BroadcastChannel instance for handling the message.

Message passing using BroadcastChannel interface

Currently, BroadcastChannel is not fully supported by safari and internet explorer. But there are alternative methods that can be used to handle this case. In these methods, we customise the shared resources between A and B and use them as the mediator for communication. Some examples are as follows:

i) Whenever data is stored into local storage, the “storage” event gets triggered on all other tabs and windows with the same origin. Browsing context A can store some data in local storage from func_A and browsing context B can listen to the storage event to trigger func_B. In the following example, key “action” is set to value “trigger_func_B” to trigger the “storage” event:

Communication between browsing contexts using local storage

ii) Browsing contexts A and B can communicate by message passing through a SharedWorker. A SharedWorker is a script that runs in a background thread, separate from the main execution thread of any browsing context and it can be accessed from multiple browsing contexts of the same origin. In the following example, the SharedWorker script(sharedworker.js) receives message from context A and broadcasts it to all browsing contexts accessing it. Context B handles the message and triggers func_B based on the message data.

Communication between browsing contexts using shared worker

b) When A and B are having different origins

There is a high risk of a cross-site scripting attack if a script from one browsing context is allowed to communicate to an unrelated browsing context from a different origin. Thus, the browser prevents communication between such browsing contexts, and no solution is currently defined specifically to handle this sub case. Any of the mediator components which we saw in the previous sub case (local storage, shared worker, or broadcast channel) are specific to an origin and thus cannot be used for cross-origin communication. Window.postMessage allows for cross-origin communication but only if the source context has a reference to the target context window.

Conclusion

We looked into different scenarios in which a browsing context can trigger script execution in another. We can observe that the method of script execution becomes increasingly restrictive as the relationship between the two browsing contexts weakens and there is no way to communicate between two unrelated browsing contexts with different origins. To summarise:

  1. When two browsing contexts have the same origin and the source context has reference to the target context, scripts from the target can be invoked directly from the source context.
  2. When two browsing contexts have different origins and the source context has reference to the target context, the source has to post a message to the target context’s window and it has to rely on the message handler from the target context to handle the script execution.
  3. When two browsing contexts have the same origins and the source context does not have reference to the target context, the source can communicate to the target only through a mediator component. Unlike window.postMessage, the source doesn’t have the capability to communicate to a specific target. The source can only communicate to the mediator, and the mediator handles the communication to the target. In case the communication is broadcasted to all the targets by the mediator, the target context needs to filter out relevant messages and handle them.
  4. When two browsing contexts have different origins and the source context does not have the reference to target context, the browser doesn’t allow for direct communication between them.

As Whatfix is an overlay that latches on to any application, we deal here with a complex inter-working across browsing contexts from the overlay and the host application. As browser security keeps evolving, we work with the latest technologies and practices to provide a secure, seamless, and efficient solution to the end user.

References

--

--