Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Html outputs write over each other's contents #6598

Open
Strilanc opened this issue Oct 21, 2022 · 6 comments
Open

Html outputs write over each other's contents #6598

Strilanc opened this issue Oct 21, 2022 · 6 comments
Labels

Comments

@Strilanc
Copy link

Strilanc commented Oct 21, 2022

Create a notebook with two cells:

class C1:
    def _repr_html_(self):
        return """
<div id="out">not initialized</div>
<script type="module">
  document.getElementById("out").textContent = "1!";
</script>
        """

C1()

and

class C2:
    def _repr_html_(self):
        return """
<div id="out">not initialized</div>
<script type="module">
  document.getElementById("out").textContent = "2!";
</script>
        """

C2()

then execute both cells.

WHAT SHOULD HAPPEN

First cell output shows "1!"
Second cell output shows "2!"

WHAT ACTUALLY HAPPENS

First cell output shows "2!"
Second cell output shows "not initialized"

WHAT'S PROBABLY GOING WRONG

It appears that the cells are not being sandboxed so that their scripts can only apply to the elements that they declare.

This is something that works, for example, in google collab. But it's completely broken in the local notebook server. Different html outputs just trash each other's contents...

@jupyterlab-probot jupyterlab-probot bot added the status:Needs Triage Applied to issues that need triage label Oct 21, 2022
@RRosio RRosio added bug and removed status:Needs Triage Applied to issues that need triage labels Oct 27, 2022
@gabalafou
Copy link
Contributor

I reproduced this in Notebook 7

@krassowski
Copy link
Member

krassowski commented Oct 27, 2022

User-land workaround for this and the issue you see in #6597 is to sandbox cell outputs by wrapping them in iframe, like:

from urllib.parse import quote
from IPython.display import HTML

def isolate(html_code):
    content = quote(html_code, safe='')
    return """
    <iframe
        width="100%"
        height="100%"
        style="border: none"
        sandbox="allow-scripts allow-modals"
        referrerpolicy="no-referrer"
        src="data:text/html;charset=UTF-8,{content}"
    ></iframe>
    """.format(content=content)

Colab differently to upstream Jupyter frontends uses iframes by default. JupyterLab uses (and Notebook v7 will use) iframe-based isolation for some outputs (e.g. some SVGs).

You can use the workaround like this:

class C1:
    def _repr_html_(self):
        return isolate("""
<div id="out">not initialized</div>
<script type="module">
  document.getElementById("out").textContent = "1!";
</script>
        """)

C1()

Now, changing the default would be a major compatibility breakage for existing users which may relay on the possibility of cells interacting and may break existing notebooks, so if this happens it could only happen in a future major Jupyter Notebook release.

@krassowski
Copy link
Member

krassowski commented Oct 27, 2022

You may also be interested in the Rich Output MIME bundle concept (https://github.com/jasongrout/richoutput-js) which is a result of Jupyter community workshop on widgets and was presented at JupyterLab meeting this week (jupyterlab/frontends-team-compass#152 (comment)). This is at a very early stage, but if it were to be accepted as part of the Jupyter standard, it could potentially offer isolation by default (without breaking any of the existing notebooks).

Edit CC @jasongrout; some ideas to explore: isolation by default (in spec), isolation as user opt-in e.g. with a second MIME type application/vnd.jupyter.es6-rich-output-isolated (but frontends would be allowed to force isolation too).

@Strilanc
Copy link
Author

Strilanc commented Oct 28, 2022

The workaround I went with was to find the object by id and then change the id, so that each cell would get out of the way of the next one. There's a risk that the javascript from one cell's output ends up driving the html widgets in another cell's output if they are initializing simultaneously... so I like the iframe idea better. The downside unfortunately is that these contents are actually coming from a C++ extension, so I would have to do the URL quoting myself. Blergh. Also these outputs happen to be data heavy already (the actual context is that they are 3d model viewers with the 3d model embedded), and another level of quoting will hurt.

@mcrutch
Copy link
Contributor

mcrutch commented Nov 3, 2022

I would argue this is desired because some people like to roll their own equiv to a widget (why, who knows) but you want to be able to update a single output element from any cell in the notebook as execution proceeds. This is also how Notebook v6 and JupyterLab react as well.

@mcrutch
Copy link
Contributor

mcrutch commented Nov 4, 2022

So, I actually found a case where this is really unexpected IMO but not in Notebook, in JupyterLab's MDI interface. Where the notebooks aren't sandboxed from each other. Within the same notebook, I think this behavior is expected but not across notebooks. I will file a ticket there about that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants