[BUG] semantics of (onbeforeunload, Dom_html.handler) incompatible with major browsers
Describe the bug
Adding/removing hooks for the beforeunload event with Dom_html.window##.onbeforeunload := Dom_html.handler … is not working out-of-the-box with both Chromium, Firefox, and Safari. So I had to devise the following hack (unsafe JS code):
let prompt_before_unload () : unit =
Js.Unsafe.js_expr "window.onbeforeunload = function(e) {e.preventDefault(); return false;}" in
let resume_before_unload () : unit =
Js.Unsafe.js_expr "window.onbeforeunload = null" in
(…)
if not sync then prompt_before_unload () else resume_before_unload ()
(…)
— Source code: https://github.com/ocaml-sf/learn-ocaml/blob/a6e4c5e6/src/app/learnocaml_exercise_main.ml#L292-L310
Expected behavior
I open this PR in case it is possible for you to tweak Dom_html.handler, and be able to directly use js_of_ocaml's abstractions (not Js.Unsafe.js_expr) for this use case, and stay compatible with (Chromium, Firefox, Safari…)
FTR → I had sketched a possible explanation of why one such tentative code doesn't work in Firefox here: https://github.com/ocaml-sf/learn-ocaml/issues/467#issuecomment-1059802295
Anyway, if ever you'd think it is already possible to implement the snippet above without Js.Unsafe.js_expr (meaning there's no "bug" over there), I'm taker of any idiomatic snippet 👍
Versions Version of packages used to reproduce the bug:
- ocaml 4.12.1
- js_of_ocaml 3.9.0
(I'll be happy to provide more details on the versions used if need be)
Cc @yurug FYI
I think I understand the issue.
You should be able to avoid the usage of js_expr by using a well constrained Obj.magic
let unsafe_cast (x : ('c, 'a -> 'b) Js.meth_callback) : ('c, 'a) Dom.event_listener = Obj.magic x;;
let prompt_before_unload () : unit =
let cb (e : Dom_html.event Js.t) = Dom.preventDefault e; Js._false in
Dom_html.window##.onbeforeunload := unsafe_cast (Js.Unsafe.callback cb);;
let resume_before_unload () : unit = Dom_html.window##.onbeforeunload := Dom.no_handler
Thanks a lot, @hhugo ! Your approach looks better indeed :)
I'll test this as soon as possible in learn-ocaml.
Just asking in the meantime: do you believe the prompt_before_unload could itself be rewritten directly as an instance of Dom_html.handler ? possibly, modifying/generalizing Dom_html.handler's implementation…
See also https://github.com/ocaml-sf/learn-ocaml/issues/467#issuecomment-1059802295
Anyway, more testing is needed! (and sorry if I can't send more feedback quickly enough…)
Just asking in the meantime: do you believe the prompt_before_unload could itself be rewritten directly as an instance of Dom_html.handler ? possibly, modifying/generalizing Dom_html.handler's implementation…
I'm not sure, you should keep this issue open until one figure out what to do.