fonsp / Pluto.jl

🎈 Simple reactive notebooks for Julia
https://plutojl.org/
MIT License
4.95k stars 285 forks source link

iframe with embedded base64 encoded data string does not work #2551

Closed schlichtanders closed 1 year ago

schlichtanders commented 1 year ago

while the standard works and show a nice openstreetmap map

HTML("""
<iframe id="inlineFrameExample"
    title="Inline Frame Example"
    width="300"
    height="200"
    src="https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik">
</iframe>
""")

the embedded version does not work (EDIT: -O, i.e. capitalized, is the correct flag)

begin
using HypertextLiteral
run(`wget -O embed.html "https://www.openstreetmap.org/export/embed.html?bbox=-0.004017949104309083%2C51.47612752641776%2C0.00030577182769775396%2C51.478569861898606&layer=mapnik"`)
@htl """
<iframe id="inlineFrameExample"
    title="Inline Frame Example"
    width="300"
    height="200"
    src="$(LocalResource("embed.html").src)">
</iframe>
"""
end

image


EDIT: The example is of course a dummy example. What I really would like to make visible in Pluto is an automatically created plotly html widget, build in RCall.jl and written to a file.

Here one example file (html zipped): test.zip

While it opens without problems in all my browsers, I cannot make it show up within Pluto. Neither way I tried is working. Including embedding it directly into an iframe.

pankgeorg commented 1 year ago

Does it work if you use srcdoc instead of src?

On a second view, are you sure LocalResource is fetching the file in base64? And even if it is, is the file you saved the correct HTML? It looks like a log from wget

schlichtanders commented 1 year ago

Thank you for inspecting. You are right, it was a typo in this example - it needs to be a capital -O embed.html.

Now the output is like in my other case: white and empty (I updated the original screenshot respectively).

when using srcdoc instead of src, it will print the base64 encoded value inside the iframe, but no real website is shown.

image

pankgeorg commented 1 year ago

what is read("embed.html", String)? can you put that in srcdoc?

schlichtanders commented 1 year ago

the result is also empty

image

schlichtanders commented 1 year ago

@pankgeorg do you have any further idea?

Dynamic iframes are an essential feature when dealing with other HTML resources. I know no workaround

ctrekker commented 1 year ago

It is likely that downloading embed.html and embedding it locally will not work since there could be relative paths requested by the embedded document that don't actually exist on localhost. Taking a look at embed.html, it appears that this is the case:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="utf-8">
    <title>OpenStreetMap Embedded</title>
    <link rel="stylesheet" href="/assets/embed-4fc09af26cf00040a2806b604b9585de7200738031662013140d70d411f5d0e5.css" media="screen" />
    <script src="/assets/embed-cfaa69da71d35951f4fa0dff2f91b727a6ebaead68655ec8ccab1e5878f39c8b.js"></script>
  </head>
  <body>
    <div id="map"></div>
  </body>
</html>

When embedded directly from openstreetmap.org, the relative paths /assets/... included in the document head will point to https://www.openstreetmap.org/assets/.... However, when embedding via a Base64 data url, your host is no longer openstreetmap.org and so the necessary js and css files are missing.

This is not an issue with Pluto but with the embed.html file used. I would suggest making manual modifications to embed.html to correctly point to the assets requested in the document head and see if that works. There's no guarentees it will though since there's no reason for openstreetmap to support locally hosted HTML serving in this way. The only "foolproof" officially supported way to do what you're trying to do is to host your own local instance of openstreetmap.

Hopefully this helps!

schlichtanders commented 1 year ago

@ctrekker thank you for your help

the openstreetmap is only meant as an example. The file which I actually want to display is this one: html file created by RCall ggplotly (zipped)

As mentioned, this opens without problems in every browser, but iframe does not work

schlichtanders commented 1 year ago

the R figure can be created from R using

library(ggplot2)
library(plotly)
data(mtcars, package="datasets")
p = ggplot(mtcars, aes(x=wt, y=mpg)) + geom_point()
ply = ggplotly(p)
htmlwidgets::saveWidget(ply, "r_figure.html")
schlichtanders commented 1 year ago

I guess the issue is that the iframe parsing somehow corrupts the html

there are errors I can see in the browser

Uncaught SyntaxError: missing ) after argument list
Uncaught ReferenceError: crosstalk is not defined
schlichtanders commented 1 year ago

Could this be an issue with julia's Core HTML parser?

ctrekker commented 1 year ago

The main issue with the test.html file you sent is its size. Here's a solution that should be general enough to work: Screenshot from 2023-05-05 12-23-18

The function embedHTML creates an empty iframe, then immediately writes its HTML contents to the value provided by the parameter pagedata. I can't think of a way to do this without JavaScript code, since the HTML contents you sent are too large to fit in a data URL (Chrome places a 2MB size limit on them).

function embedHTML(pagedata; kwargs...)
    @htl """
        <iframe src="about:blank" $(kwargs)></iframe>
        <script>
            const embeddedFrame = currentScript.previousElementSibling;
            const pagedata = atob($(base64encode(pagedata)));
            embeddedFrame.contentWindow.document.open();
            embeddedFrame.contentWindow.document.write(pagedata);
            embeddedFrame.contentWindow.document.close();
        </script>
    """
end
schlichtanders commented 1 year ago

@ctrekker am very very thankful for your help :slightly_smiling_face:

This works for me too. I don't know how you came about the idea that it is about file-size and the workaround, but amazing!! Now I can use R plotly plots in Pluto 🥳

fonsp commented 1 year ago

You don't need to base64-encode, you can do it with HypertextLiteral:

function embedHTML(pagedata; kwargs...)
    @htl """
        <iframe src="about:blank" $(kwargs)></iframe>
        <script>
            const embeddedFrame = currentScript.previousElementSibling;
            const pagedata = $pagedata;
            embeddedFrame.contentWindow.document.open();
            embeddedFrame.contentWindow.document.write(pagedata);
            embeddedFrame.contentWindow.document.close();
        </script>
    """
end

or you can use https://github.com/fonsp/Pluto.jl/pull/1124 !

function embedHTML(pagedata; kwargs...)
    @htl """
        <iframe src="about:blank" $(kwargs)></iframe>
        <script>
            const embeddedFrame = currentScript.previousElementSibling;
            const pagedata = $(PlutoRunner.publish_to_js(pagedata));
            embeddedFrame.contentWindow.document.open();
            embeddedFrame.contentWindow.document.write(pagedata);
            embeddedFrame.contentWindow.document.close();
        </script>
    """
end

See the JavaScript featured notebook for more details :)