[Fixed] How to send message from Anvil app to embedded iFrame

What I’m trying to do:

Have an Anvil app with an iframe embedding a non-Anvil page, and use the standard postMessage technique of communicating between them. Trying to avoid reloading the iframe and using query parameters to communicate changes.

Communication from the iframe to the Anvil app is working fine, but communication from the Anvil app to the iframe is generating an error. The same code works fine in a non-Anvil page, so I assume I’m running into some restrictions based on the Anvil/Skulpt model?

What I’ve tried and is working:

Here’s what the iframe content uses to communicate with the parent app (and to prepare to receive messages, although that part isn’t working yet):

<button onclick="sendMessageToParent()">Send Message To Parent</button>

<script>

window.addEventListener("message", self.receiveMessage, false);

function sendMessageToParent()
{
    console.log("sending message to parent");
    window.parent.postMessage({sender: window.location.href, foo: 'bar'}, '*');
}

function receiveMessage(message)
{
    console.log("Recieved in iframe");
    console.log(message);
}

</script>

The message is received in the parent Anvil app just fine. Here’s the code for setting that up:

    def __init__(self, **properties):
        # Set Form properties and Data Bindings.
        self.init_components(**properties)

        self.url = 'put a good URL here'
        self.iframe = None
        self.first_show = True
        anvil.js.window.addEventListener("message", self.receiveMessage, False)        
        
    def form_show(self, **event_args):
        if self.first_show:
            self.iframe = anvil.js.window.document.createElement('iframe')
            self.iframe.style.width = "100%"
            self.iframe.style.height = "100vh"
            self.iframe.setAttribute("src", self.url)
            anvil.js.get_dom_node(self).appendChild(self.iframe)
            self.first_show = False

    def receiveMessage(self, message):
        if 'sender' in message.data:
            self.label_1.text = str(message.data)

All of that’s standard postMessage handling, adapted to Anvil’s Javascript bridge, and as I say works fine.

What I’ve tried and what’s not working:

The communication back from the parent to the iframe fails, though, even though it’s using the same mechanism:

    def toIFrame(self, message):
        self.iframe.contentWindow.postMessage(message, '*')

    def button_1_click(self, **event_args):
        self.toIFrame({'data': 'something'})

The error posted in the browser logs has, among other bits, these messages:

"$mangled": "SecurityError: Failed to read a named property 'sk$object' from 'Window': Blocked a frame with origin \"https://infatuated-melodic-kinkajou.anvil.app\" from accessing a cross-origin frame.",
"$savedKeyHash": "SecurityError: Failed to read a named property 'sk$object' from 'Window': Blocked a frame with origin \"https://infatuated-melodic-kinkajou.anvil.app\" from accessing a cross-origin frame.",
"v": "SecurityError: Failed to read a named property 'sk$object' from 'Window': Blocked a frame with origin \"https://infatuated-melodic-kinkajou.anvil.app\" from accessing a cross-origin frame."

Since the same code worked outside of Anvil, how can I achieve the same effect inside of Anvil?

Clone link:

And here’s the obligatory minimal clone link for folks to play with.

Looks like a bug to me. I’ll dig down and get that fixed next week. Something similar happened in our first implementation when accessing the .parent element of an anvil iframe. Essentially skulpt is trying to access properties that the browser is blocking, when doing a look up on a wrapped JavaScript object and wrapping it back to Python, which is causing those errors.

1 Like

Glad it’s not me going crazy! Well, not any more than usual.

This should now be fixed