Is there a way to allow top navigation within an iframed component?

Hi there,
Components are sandboxed to iframes and the default policies are listed in this file. The allow-top-navigation is not part of them. Without this, it is not possible to have links in components that open a link (for instance in other page of the streamlit app) using the current tab (meaning with href="_top" or href="_parent".
Is it possible to modify this restriction? I found this answer but it is a bit hacky as it uses javascript to directly update the attributes of the sandbox.

2 Likes

Hey @Melaine,

Thanks for sharing this question! I’d recommend posting this in GitHub Issues as a formal enhancement request so our product/engineering teams can take a look!

Hello @Melaine did you achieve any success related to this issue?
Facing the same thing here.
Best!

Hi @MarcosBerghahn,
I used the hacky solution using javascript. This post was useful. In my codebase it all boiled down to calling this python function on scripts for which I have this issue. I know it is hacky and ugly but I was in a rush :slight_smile:

def allow_top_navigation():
    ''' Allow links embedded in iframes to open in the same tab (target='_parent' or '_top')
    Useful for links in AgGrid grids
    '''
    # I know... This is ugly... 
    # Waiting for next versions of Streamlit when there will be more control over iframes
    # or maybe with better tables in order to get rid of AG Grid altogether

    # Refer to http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/
    st.components.v1.html('''<script language="javascript">
        // The document will listen to all 'animationstart' events and some of which have been named 'nodeInserted' by a css trick
       
        var updateAndReloadIframes = function () {
            var reloadRequired = false;
            // Grab all iFrames from agGrid, add the 'allow-top-navigation' property and reload them
            var iframes = parent.document.querySelectorAll("iframe[title='st_aggrid.agGrid']");
            for (var i = 0; i < iframes.length; i++) {
                if (!iframe.sandbox.contains('allow-top-navigation')) {
                    reloadRequired = true;
                    iframes[i].setAttribute("sandbox", "allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads allow-top-navigation-by-user-activation allow-top-navigation");
                }
            }
            if (reloadRequired) {
                setTimeout(function() {
                    for (var i = 0; i < iframes.length; i++) {
                        iframes[i].contentWindow.location.reload();
                    }
                }, 300)
            }
        }

        var event = function(event){
            if (event.animationName == 'nodeInserted') updateAndReloadIframes()
        }
        parent.document.addEventListener('animationstart', event, false);
        parent.document.addEventListener('MSAnimationStart', event, false);
        parent.document.addEventListener('webkitAnimationStart', event, false);

        // Some weird bug appear in prod env. Fix: ping the DOM every 1 second to add the property to the iframes if necessary
        var intervalId = window.setInterval(function(){
            var reloadRequired = false;
            parent.document.querySelectorAll("iframe[title='st_aggrid.agGrid']").forEach((iframe) => {
                if (!iframe.sandbox.contains('allow-top-navigation')) reloadRequired = true;
            })
            if (reloadRequired) updateAndReloadIframes()
        }, 1000);

    </script>
    ''', height=0)

Do not know if this will be useful for you :thinking:

Note: the last part (after the comment // Some weird bug) was added only because of a bug in our prod environment. May not be necessary. It was not necessary in dev environment.