From 92a45b1e3bdb95778f2b69db7251b6ee38e08c3f Mon Sep 17 00:00:00 2001 From: Brandon Yi Date: Sun, 28 Jan 2024 23:31:09 -0800 Subject: [PATCH 1/2] Loading blink.js on loadURL Addresses an issue brought up here: https://github.com/JuliaGizmos/Blink.jl/issues/150 This MR addresses the lissue by loading `blink.js` and `webio.bundle.js` whenever content is loaded using a custom URL. This was achieved by passing the URL of the websocket server to `createWindow` in main.js. This allowed the required JS files to be loaded whenever the Electron BrowserWindow does a navigation (i.e. when Julia calls `loadurl` or creates a window with :url defined). --- res/blink.js | 6 ++++++ src/AtomShell/main.js | 31 ++++++++++++++++++++++++++++++- src/AtomShell/window.jl | 38 +++++++++++++++++++++++++++----------- src/content/server.jl | 5 ++++- test/content/api.jl | 11 +++++++++++ 5 files changed, 78 insertions(+), 13 deletions(-) diff --git a/res/blink.js b/res/blink.js index eab4b72..a2a0f63 100644 --- a/res/blink.js +++ b/res/blink.js @@ -5,6 +5,12 @@ // Comms stuff var ws = location.href.replace("http", "ws"); + + // use specified url if it exists (supplied by createWindow opts) + if (document.ws) { + ws = document.ws; + } + if (!/\/\d+$/.test(ws)) { ws += '/' + id; } diff --git a/src/AtomShell/main.js b/src/AtomShell/main.js index 5982b4a..5e613b8 100644 --- a/src/AtomShell/main.js +++ b/src/AtomShell/main.js @@ -77,11 +77,40 @@ app.on("ready", function() { // Window creation var windows = {}; -function createWindow(opts) { +function createWindow(opts, comUrl) { var win = new BrowserWindow(opts); windows[win.id] = win; if (opts.url) { win.loadURL(opts.url); + + windows[win.id].comUrl = comUrl; + + // load blink.js and webio.bundle.js + win.webContents.on('did-finish-load', function() { + win.webContents.executeJavaScript(` + // is blink.js already loaded? + if (typeof Blink != 'object') { + var comUrl = '${comUrl ? comUrl : windows[win.id].comUrl}'; + var port = comUrl.split(":").pop().split("/")[0]; + var id = comUrl.split("/").pop()[0]; + var urlParams = new URLSearchParams(comUrl); + var callback_id = urlParams.get('callback'); + + document.ws = comUrl.replace("http", "ws"); + + var script = document.createElement('script'); + script.defer = true; + script.type = 'text/javascript'; + script.src = 'http://localhost:' + port + '/blink.js'; + document.head.appendChild(script); + + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = 'http://localhost:' + port + '/webio.bundle.js'; + document.head.appendChild(script); + } + `); + }); } win.setMenu(null); diff --git a/src/AtomShell/window.jl b/src/AtomShell/window.jl index 961a1c3..e54155f 100644 --- a/src/AtomShell/window.jl +++ b/src/AtomShell/window.jl @@ -40,12 +40,12 @@ const window_defaults = Dict( :icon => resolve_blink_asset("deps", "julia.png") ) -raw_window(a::Electron, opts) = @js a createWindow($(merge(window_defaults, opts))) +raw_window(a::Electron, opts, comurl="") = @js a createWindow($(merge(window_defaults, opts)), $(comurl)) function Window(a::Shell, opts::AbstractDict = Dict(); async=false, body=nothing) # TODO: Custom urls don't support async b/c don't load Blink.js. (Same as https://github.com/JunoLab/Blink.jl/issues/150) window = haskey(opts, :url) ? - Window(raw_window(a, opts), a, nothing, nothing) : + initwindowUrl(a, Page(), opts) : Window(a, Page(), opts; async=async) isnothing(body) || body!(window, body) return window @@ -57,8 +57,32 @@ function Window(a::Shell, content::Page, opts::AbstractDict = Dict(); async=fals # Create the window. opts = merge(opts, Dict(:url => url)) - w = Window(raw_window(a, opts), a, content, nothing) + w = Window(raw_window(a, opts, url), a, content, nothing) + initializeWindowAsync!(w, callback_cond) + + if !async + wait(w) + end + + isnothing(body) || body!(w, body) + + return w +end + +function initwindowUrl(a, content, opts) + id, callback_cond = Blink.callback!() + comurl = Blink.localurl(content) * "?callback=$id" + + # supply url of server (comurl) since :url in opts is url to load content from + w = Window(raw_window(a, opts, comurl), a, content, nothing) + + initializeWindowAsync!(w, callback_cond) + + return w +end + +function initializeWindowAsync!(w::Window, callback_cond::Threads.Condition) # Note: we have to use a task here because of the use of Condition throughout # the codebase (it might be better to use Channel or Future which are not # edge-triggered). We also need to initialize this after the Window @@ -71,14 +95,6 @@ function Window(a::Shell, content::Page, opts::AbstractDict = Dict(); async=fals exception=(exc, catch_backtrace()), ) end - - if !async - wait(w) - end - - isnothing(body) || body!(w, body) - - return w end function initwindow!(w::Window, callback_cond::Threads.Condition) diff --git a/src/content/server.jl b/src/content/server.jl index 06cc354..b040e6c 100644 --- a/src/content/server.jl +++ b/src/content/server.jl @@ -42,7 +42,10 @@ function ws_handler(ws) p.sock = ws @async @errs get(handlers(p), "init", identity)(p) try - put!(p.cb, true) + # set only on first websocket connection initialization + if !isready(p.cb) + put!(p.cb, true) + end catch e @warn e end diff --git a/test/content/api.jl b/test/content/api.jl index 6be5513..dbce33c 100644 --- a/test/content/api.jl +++ b/test/content/api.jl @@ -19,6 +19,17 @@ w = Window(Blink.Dict(:show => false), async=false); @test (@js w document.getElementById("b").innerHTML) == "bye" end +temp_dir = tempdir() +temp_html = joinpath(temp_dir, "temp.html") +write(temp_html, """Test
Test
""") +w_url = Window(Blink.Dict(:url => "file://" * temp_html, :show => false), async=false); +@testset "blink.js is included in document when using loadurl" begin + sleep(1) # wait for page to load + + @test (@js w_url document.getElementById("a").innerHTML) == "Test" + @test (@js w_url [].filter.call(document.scripts, function (script) return script.src.includes("blink.js") end).length) == 1 +end + # Test `fade` parameter and scripts: # Must create a new window to ensure javascript is reset. From 4afd20f1153422480fcdd385de0926dfcc6c96eb Mon Sep 17 00:00:00 2001 From: Brandon Yi Date: Mon, 29 Jan 2024 00:43:14 -0800 Subject: [PATCH 2/2] Added comments to main.js and window.jl Explaining code in did-finish-load handler and in initwindowUrl --- src/AtomShell/main.js | 5 ++++- src/AtomShell/window.jl | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/AtomShell/main.js b/src/AtomShell/main.js index 5e613b8..2938416 100644 --- a/src/AtomShell/main.js +++ b/src/AtomShell/main.js @@ -77,6 +77,8 @@ app.on("ready", function() { // Window creation var windows = {}; +// opts are Electron BrowserWindow options and comUrl is the url to the local +// server that will be used to communicate with the window. function createWindow(opts, comUrl) { var win = new BrowserWindow(opts); windows[win.id] = win; @@ -85,8 +87,9 @@ function createWindow(opts, comUrl) { windows[win.id].comUrl = comUrl; - // load blink.js and webio.bundle.js + // load blink.js and webio.bundle.js whenever navigation is performed win.webContents.on('did-finish-load', function() { + // load files from the local server instead of provided url for content win.webContents.executeJavaScript(` // is blink.js already loaded? if (typeof Blink != 'object') { diff --git a/src/AtomShell/window.jl b/src/AtomShell/window.jl index e54155f..9f1f79a 100644 --- a/src/AtomShell/window.jl +++ b/src/AtomShell/window.jl @@ -40,12 +40,12 @@ const window_defaults = Dict( :icon => resolve_blink_asset("deps", "julia.png") ) -raw_window(a::Electron, opts, comurl="") = @js a createWindow($(merge(window_defaults, opts)), $(comurl)) +raw_window(a::Electron, opts, comurl) = @js a createWindow($(merge(window_defaults, opts)), $(comurl)) function Window(a::Shell, opts::AbstractDict = Dict(); async=false, body=nothing) # TODO: Custom urls don't support async b/c don't load Blink.js. (Same as https://github.com/JunoLab/Blink.jl/issues/150) window = haskey(opts, :url) ? - initwindowUrl(a, Page(), opts) : + initWindowUrl(a, Page(), opts) : Window(a, Page(), opts; async=async) isnothing(body) || body!(window, body) return window @@ -70,7 +70,12 @@ function Window(a::Shell, content::Page, opts::AbstractDict = Dict(); async=fals return w end -function initwindowUrl(a, content, opts) +""" + initWindowUrl(a::Shell, content::Page, opts::AbstractDict = Dict()) + + Builds window that loads content from specified url in opts. +""" +function initWindowUrl(a::Shell, content::Page, opts::AbstractDict = Dict()) id, callback_cond = Blink.callback!() comurl = Blink.localurl(content) * "?callback=$id"