diff options
Diffstat (limited to 'src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja')
-rw-r--r-- | src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja | 173 |
1 files changed, 172 insertions, 1 deletions
diff --git a/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja b/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja index 653b7df..593673b 100644 --- a/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja +++ b/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja @@ -45,6 +45,177 @@ code in a proprietary program, I am not going to enforce this in court. #} (function(){ - console.log('TODO: make Haketilo able to actually display a popup') document.currentScript.remove(); + + /* + * To slightly decrease the chance of accidental popup breakage we snapshot + * methods that other code might redefine. + */ + function get_setter(obj, name) { + return Object.getOwnPropertyDescriptor(obj, name).set; + } + + const ElementPrototype = [0, 0, 0] + .reduce(n => Object.getPrototypeOf(n), document.documentElement); + + const prepend_fun = ElementPrototype.prepend; + const setattr_fun = ElementPrototype.setAttribute; + const remove_fun = ElementPrototype.remove; + const setinner_fun = get_setter(ElementPrototype, "innerHTML"); + const open_fun = window.open; + + const shortcut = "HKT"; + const nonce = atob("{{nonce_b64}}"); + const popup_style = "{{popup_style}}"; + const popup_html = atob("{{popup_page_b64}}"); + const popup_container = document.createElement("div"); + const popup_frame = document.createElement("iframe"); + + function make_style(styles_obj) { + return Object.entries(styles_obj) + .map(([key, val]) => `${key}: ${val} !important`) + .join(';'); + } + + const frame_style = make_style({ + "position": "absolute", + "left": "50%", + "top": "50%", + "transform": "translate(-50%, -50%)", + "display": "block", + "visibility": "visible", + "min-width": "initial", + "width": "600px", + "max-width": "calc(100vw - 20px)", + "min-height": "initial", + "height": "700px", + "max-height": "calc(100vh - 20px)", + "background-color": "#fff", + "opacity": "100%", + "margin": 0, + "padding": 0, + "border": "none", + "border-radius": "5px" + }); + + const container_style = make_style({ + "position": "fixed", + "left": "0", + "top": "0", + "transform": "initial", + "z-index": 2147483647, + "display": "block", + "visibility": "visible", + "min-width": "100vw", + "max-width": "100vw", + "min-height": "100vh", + "max-height": "100vh", + "background-color": "#0008", + "opacity": "100%", + "margin": 0, + "padding": 0, + "border": "none", + "border-radius": 0 + }); + + const popup_blob_opts = {type: "text/html;charset=UTF-8"}; + const popup_blob = new Blob([popup_html], popup_blob_opts); + const popup_url = URL.createObjectURL(popup_blob); + + function show_popup_dialog() { + setattr_fun.call(popup_frame, "srcdoc", popup_html); + setattr_fun.call(popup_frame, "nonce", nonce); + setattr_fun.call(popup_frame, "style", frame_style); + + setattr_fun.call(popup_container, "style", container_style); + setinner_fun.call(popup_container, ""); + prepend_fun.call(popup_container, popup_frame); + + prepend_fun.call(document.body, popup_container); + } + + let popup_newtab_wanted = false; + + function show_popup_newtab() { + /* + * We cannot open popup directly here because browsers block window + * creation attempts from "keypress" event handlers. Instead, we set a + * flag to have "click" event handler open the popup. + */ + popup_newtab_wanted = true; + console.info(`You typed "${shortcut}". Please click anywhere on the page to show Haketilo page information.`); + } + + function show_popup() { + if (popup_style === "T") { + show_popup_newtab(); + } else { + /* popup_syle === "D" */ + show_popup_dialog(); + } + } + + function hide_popup_dialog() { + remove_fun.call(popup_container); + } + + let letters_matched = 0; + + function matches_previous(letter) { + return letters_matched > 0 && letter === shortcut[letters_matched - 1]; + } + + function match_letter(letter) { + if (letter !== shortcut[letters_matched] && !matches_previous(letter)) + letters_matched = 0; + + if (letter === shortcut[letters_matched]) { + if (++letters_matched === shortcut.length) { + letters_matched = 0; + return true; + } + } + + return false; + } + + function consume_keypress(event) { + if (!event.isTrusted) + return; + + if (match_letter(event.key)) + show_popup(); + } + + function cancel_event(event) { + event.stopImmediatePropagation(); + event.stopPropagation(); + event.preventDefault(); + } + + function consume_click(event) { + if (!event.isTrusted) + return; + + if (popup_style === "T") { + if (popup_newtab_wanted) { + popup_newtab_wanted = false; + cancel_event(event); + window.open( + popup_url, + "_blank", + "popup,width=600px,height=700px" + ); + } + } else { + /* popup_syle === "D" */ + if (event.target === popup_container) { + hide_popup_dialog(); + cancel_event(event); + } + } + } + + document.addEventListener("keypress", consume_keypress, {capture: true}); + document.addEventListener("click", consume_click, {capture: true}); })(); |