AaronWatters / jp_proxy_widget

Generic Jupyter/IPython widget implementation that will support many types of javascript libraries and interactions.
BSD 2-Clause "Simplified" License
61 stars 13 forks source link

Open Widget in a Separate Browser Tab Connected by Broadcast Channel API #20

Closed psychemedia closed 3 years ago

psychemedia commented 3 years ago

Hi

I just came across the browser Broadcast Channel API for the first time, and this generalisation of it — https://github.com/pubkey/broadcast-channel — (demo).

The package seems to allow transfer of JSON serialisable messages, which makes me wonder - could jp_server_proxy in principle be used to connect to control, and receive messages from an application running in another tab using the Broadcast Channel API? One advantage of this might be for folk running two screens: one could display the widget/application tab, and the other a Jupyter notebook "controller"?

AaronWatters commented 3 years ago

Thank you for the idea. I want to look into this further.

It would be nice to have a widget-like interface where the widget author had full control of the window and the Javascript environment and everything. Broadcast channels connecting to a separate window/tab might provide that kind of environment if some details work out.

Great pointer! thanks again!

psychemedia commented 3 years ago

I was wondering whether a really simple jp-proxy-widget in the notebook could subscribe to a channel that talks to a jupyter-server-proxy app using the same channel. On first widget invocation in a Jupyter session, it could start the jupyter-server-proxy app running?

I haven't made time to try to explore a simple proof of concept yet, but the pattern above was what came to mind as the first thing to try as I walked the dog last night!

AaronWatters commented 3 years ago

I'm beginning to suspect that this may not work, at least in Jupyter lab, because the Jupyter lab server does not serve "normal" files -- I don't know how to open a "normal" html page in a new window served by the Jupyter lab server.

This might be a show stopper because of the "same origin" restriction on broadcast channels.

I may be missing something or there may be some way around this that circumvents contacting the server directly using HTTP entirely (using data urls?).

It might be simpler/possible to do it using the "classic" notebook interface/protocol because there is a /files/... url that "just sends the file without messing with it", I think (but I might be wrong about that).

[This is another thing that bugs me about Jupyter lab -- they broke sending standard files using http unmolested I think, but I'd be delighted if I was wrong.]

psychemedia commented 3 years ago

I was thinking of separately (manually) running a an http page via jupyter-server-proxy, which puts it on /proxy/port. As far as same origin goes, I get that doesn't work over different subdomains, but does it also break across eg example.com/path and example.com/path/deeper ?

AaronWatters commented 3 years ago

I don't think it's possible in either Jupyterlab or classic Notebook to launch a new window in the same origin to create a broadcast channel.

If I try the /files/ protocol I get:

xxx.html:10 Uncaught DOMException: Failed to construct 'BroadcastChannel': Can't create BroadcastChannel in an opaque origin
    at http://localhost:8888/files/test/html/xxx.html:10:20

And I'm not going to try data urls because I found this:

Note: Data URLs are treated as unique opaque origins by modern browsers, rather than inheriting the origin of the settings object responsible for the navigation.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

Here is the file I used to test with xxx.html:

<html>
    <h1>Hello world!</h1>

    <div id="here">Add things here.</div>
    <script>
        // alert doesn't work in Jupyterlab
        //alert("test alert!")
        var target = document.getElementById("here");
        target.innerHTML = "changed";
        const bc = new BroadcastChannel('test_channel');
        bc.onmessage = function (ev) { console.log(ev); }
        bc.postMessage('This is a test message.');
    </script>
</html>

Too bad :(.

psychemedia commented 3 years ago

Hmm... "opaque origin"... new to me... Boo :-(

AaronWatters commented 3 years ago

I don't know about jupyter-server-proxy -- maybe it doesn't monkey with things like files does.

psychemedia commented 3 years ago

I just tried to add your fragment into a simple-ish jupyter-server-proxy app I have and the page gives a 500 server error with a could not start broadcast in time message .

psychemedia commented 3 years ago

H,,, scrub that.. broadcast was the name of the package I created. Maybe I broke something else. Bah...

AaronWatters commented 3 years ago

The way to do this is to use WebRTC

https://github.com/mdn/samples-server/tree/master/s/webrtc-simple-datachannel

psychemedia commented 3 years ago

A comment in the demo suggests that requires things to be on the same page? Or was that because it was just a really simple demo?!

AaronWatters commented 3 years ago

Webrtc doesn't have to be on the same page, but it turns out you need out of band 2 way communication using some sort of external mechanism which defeats the purpose since what we are trying to implement is 2 way communication in the first place. So webrtc won't help as a communication mechanism. Websockets might work.