Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Websocket connection from an iframe in context of the parent window

I have a page and an iframe inside of the page. Both hosted on the same domain. I have a full control of the iframe, but don't have a control of the parent page.

Image

So I need to establish a websocket connection from the iframe, but in context of the parent window and keep it alive while I'm navigating out of the iframe (to the other menu items of the parent page).

(see the image) Like establishing connection in A.html, and keep it alive while navigating to B and C.

is that possible?

like image 798
Andrew P. Avatar asked Oct 17 '25 11:10

Andrew P.


1 Answers

Short answer:

Direct script injection to the parent window still might be a more elegant solution.

Long answer:

Websocket is just a kind of connection to the server. Once you create it - it persists in your browser's tab and it's not destroyed if you unload the script which created the websocket.

Two issues with creating websockets within iframes:

  1. you probably don't want to create a new websocket connection every time you load the same iframe content.
  2. once your iframe is unloaded - all websocket's event handlers are lost (like onopen, onclose, onmessage, etc).

You can try to create a websocket factory on the main window. The instance of this factory would be responsible for:

  1. create a websocket using data provided from an iframe and store it in the inner collection property
  2. clone provided websocket event handlers so they are reachable even when their original source is unloaded. It will work fine if the event handlers are simple.

There are some known issues with function cloning - ie, if they use outer closure defined in iframe's script - that closure will be lost. You may want to do a research on cloning libraries.

main.js (loaded in main index.html):

var socketsCollection = new SocketsCollection();

function SocketsCollection() {
    this.collection = {};

    this.add = function(key, obj) {
        if (this.exists(key)) return;

        // clone websocket event handlers
        // PS: this is not the best solution to clone a function. Need a better research here
        eval("var onopen = " + obj.onopen.toString());
        eval("var onclose = " + obj.onclose.toString());
        eval("var onmessage = " + obj.onmessage.toString());

        // create websocket
        var ws = new WebSocket(obj.url);
        ws.onopen = function(e) {
            onopen(e, key, ws);
        };
        ws.onclose = function(e) {
            onclose(e, key, ws);
        }
        ws.onmessage = function(e) {
            onmessage(e, key, ws);
        }

        this.collection[key] = {
            key: key,
            ws: ws
        };

        // test websocket is alive
        var self = this;
        var counter = 1;
        window.setInterval(function () {
            console.log('testing ' + key);
            self.collection[key].ws.send('ping # ' + counter + ' websocket ' + key);
            counter++;
        }, 2000);  
    }

    this.exists = function(key){
        return this.collection[key] !== undefined;
    }
}

iframed.js:

function foo(window, socketKey) {
    if (window.socketsCollection.exists(socketKey)) return;

    var newSocketData = {
        url: "wss://echo.websocket.org/",
        onopen: function(e, key, ws) {
            console.log(key + ' is OPEN', ws.readyState)
            ws.send('Hello socket ' + key);
        },
        onclose: function(e, key, ws) {
            console.log(key + ' is CLOSED', ws.readyState)
        },
        onmessage: function (e, key, ws) {
            console.log(key + ' response: ', e.data);
        }
    };

    window.socketsCollection.add(socketKey, newSocketData);
}

a.html:

<script src="iframed.js"></script>
<script>
    foo.call(window.parent, window.parent, 'A');
</script>

b.html:

<script src="iframed.js"></script>
<script>
    foo.call(window.parent, window.parent, 'B');
</script>
like image 65
Marisev Avatar answered Oct 19 '25 00:10

Marisev