From 05d553640f3b1dac714cc521b5e00f42a468c3bc Mon Sep 17 00:00:00 2001 From: titaneric Date: Sun, 21 Mar 2021 01:44:07 +0800 Subject: [PATCH 1/2] Copy iframe and wasm-entry --- src/book/init.rs | 6 ++++++ src/renderer/html_handlebars/hbs_renderer.rs | 2 ++ src/theme/mod.rs | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/book/init.rs b/src/book/init.rs index 264c113d..a54c08eb 100644 --- a/src/book/init.rs +++ b/src/book/init.rs @@ -156,7 +156,13 @@ impl BookBuilder { let mut highlight_js = File::create(themedir.join("highlight.js"))?; highlight_js.write_all(theme::HIGHLIGHT_JS)?; + + let mut iframe = File::create(themedir.join("iframe.html"))?; + iframe.write_all(theme::IFRAME)?; + let mut wasm_entry_js = File::create(themedir.join("wasm-entry.mjs"))?; + wasm_entry_js.write_all(theme::WASM_ENTRY_MJS)?; + Ok(()) } diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index f417a014..399bad0d 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -197,6 +197,8 @@ impl HtmlHandlebars { write_file(destination, "CNAME", format!("{}\n", cname).as_bytes())?; } + write_file(destination, "iframe.html", &theme.iframe_html)?; + write_file(destination, "wasm-entry.mjs", &theme.wasm_entry_mjs)?; write_file(destination, "book.js", &theme.js)?; write_file(destination, "css/general.css", &theme.general_css)?; write_file(destination, "css/chrome.css", &theme.chrome_css)?; diff --git a/src/theme/mod.rs b/src/theme/mod.rs index a1ee18af..ef29cc48 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -13,6 +13,8 @@ use std::path::Path; use crate::errors::*; +pub static IFRAME: &[u8] = include_bytes!("iframe.html"); +pub static WASM_ENTRY_MJS: &[u8] = include_bytes!("wasm-entry.mjs"); pub static INDEX: &[u8] = include_bytes!("index.hbs"); pub static HEAD: &[u8] = include_bytes!("head.hbs"); pub static REDIRECT: &[u8] = include_bytes!("redirect.hbs"); @@ -62,6 +64,8 @@ pub struct Theme { pub ayu_highlight_css: Vec, pub highlight_js: Vec, pub clipboard_js: Vec, + pub iframe_html: Vec, + pub wasm_entry_mjs: Vec, } impl Theme { @@ -79,6 +83,8 @@ impl Theme { // Check for individual files, if they exist copy them across { let files = vec![ + (theme_dir.join("iframe.html"), &mut theme.iframe_html), + (theme_dir.join("wasm-entry.mjs"), &mut theme.wasm_entry_mjs), (theme_dir.join("index.hbs"), &mut theme.index), (theme_dir.join("head.hbs"), &mut theme.head), (theme_dir.join("redirect.hbs"), &mut theme.redirect), @@ -161,6 +167,8 @@ impl Default for Theme { ayu_highlight_css: AYU_HIGHLIGHT_CSS.to_owned(), highlight_js: HIGHLIGHT_JS.to_owned(), clipboard_js: CLIPBOARD_JS.to_owned(), + iframe_html: IFRAME.to_owned(), + wasm_entry_mjs: WASM_ENTRY_MJS.to_owned(), } } } @@ -248,6 +256,8 @@ mod tests { ayu_highlight_css: Vec::new(), highlight_js: Vec::new(), clipboard_js: Vec::new(), + iframe_html: Vec::new(), + wasm_entry_mjs: Vec::new(), }; assert_eq!(got, empty); From 3faa6c19955c441a3cf676080a09edb61f9835f2 Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 1 Apr 2021 14:49:53 +0800 Subject: [PATCH 2/2] Finish client side wasm rendering --- src/theme/book.js | 97 +++++++++++++++++++++++++++++----------- src/theme/iframe.html | 28 ++++++------ src/theme/wasm-entry.mjs | 2 +- 3 files changed, 87 insertions(+), 40 deletions(-) diff --git a/src/theme/book.js b/src/theme/book.js index 828875b4..af0b54ee 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -124,6 +124,9 @@ function playground_text(playground) { result_block.innerText = "Running..."; + params = { + code: text + } // fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { // headers: { // 'Content-Type': "application/json", @@ -132,35 +135,79 @@ function playground_text(playground) { // mode: 'cors', // body: JSON.stringify(params) // }) - new Promise((resolve, reject) => { - setTimeout(() => { - resolve("foo"); - }, 200) + prepareSandbox(params).then(src => processHTML(src)).then(html => { + result_block.innerText = ""; + var iframe = result_block.appendChild(document.createElement('iframe')), + doc = iframe.contentWindow.document; + iframe.id = "wasm-rendering"; + iframe.style.width = "100%"; + iframe.style.height = "100%"; + iframe.border = 0; + iframe.scrolling = "no"; + doc.open().write(html); + doc.close(); }) - // .then(response => response.json()) - // .then(response => result_block.innerText = response.result) - // .then(response => result_block.innerHTML = "
") - // .then(response => result_block.innerHTML = "
") - .then(response => { - result_block.innerText = ""; - var iframe = result_block.appendChild(document.createElement('iframe')), - doc = iframe.contentWindow.document; - iframe.id = "wasm-rendering"; - iframe.style.width = "100%"; - iframe.style.height = "100%"; - var xhr = new XMLHttpRequest(); - xhr.open('GET', 'iframe.html', true); - xhr.onreadystatechange = function () { - if (this.readyState !== 4) return; - if (this.status !== 200) return; // or whatever error handling you want - var html = this.responseText; - doc.open().write(html); - doc.close(); - }; - xhr.send(); + } + async function prepareSandbox(params) { + var wasmResult = fetch_with_timeout("http://192.168.217.100:9999/wasm-pack", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(({ wasm_js, wasm_bg }) => { + var wasm_bg_blob = base64ToByteArray(wasm_bg); + return { + wasm_js: atob(wasm_js), + wasm_bg: wasm_bg_blob + } }) .catch(error => result_block.innerText = "Playground Communication: " + error.message); + var htmlSrc = fetch(new Request("iframe.html")) + .then(response => response.text()); + var jsSrc = fetch(new Request("wasm-entry.mjs")) + .then(response => response.text()); + + return Promise.all([htmlSrc, jsSrc, wasmResult]) + .catch(error => console.log(error)); + } + function base64ToByteArray(src) { + var decode = atob(src); + const byteNumbers = new Array(decode.length); + for (let i = 0; i < decode.length; i++) { + byteNumbers[i] = decode.charCodeAt(i); + } + return new Uint8Array(byteNumbers); + } + async function processHTML([htmlSrc, jsSrc, { wasm_js, wasm_bg }]) { + var src = rewriteJS(jsSrc, wasm_js, wasm_bg); + var blob = new Blob([src], { type: "application/javascript" }); + var jsBlob = URL.createObjectURL(blob); + return htmlSrc.replace(/\bsrc\s*=\s*['"](.+?)['"]/g, (all, path) => { + return `src="${jsBlob}"`; + }); + } + + function rewriteJS(src, wasmJS, bgWasm) { + var blob = new Blob([wasmJS], { type: "application/javascript" }); + var wasmJSBlob = URL.createObjectURL(blob); + + var blob = new Blob([bgWasm], { type: "application/wasm" }); + var bgWasmBlob = URL.createObjectURL(blob); + + // replace wasm.js + src = src.replace(/\bfrom\s+['"](.+?)['"](\s*[;\n])/g, (all, path, sep) => { + return `from "${wasmJSBlob}"${sep}`; + }) + // replace `input` of init to object URL + src = src.replace(/\(['"](.+?)['"]\)/g, (all, url) => { + return `("${bgWasmBlob}")`; + }) + return src } // Syntax highlighting Configuration diff --git a/src/theme/iframe.html b/src/theme/iframe.html index 0ad37768..4cc47cd0 100644 --- a/src/theme/iframe.html +++ b/src/theme/iframe.html @@ -1,18 +1,18 @@ - + + + - + - -
- \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/src/theme/wasm-entry.mjs b/src/theme/wasm-entry.mjs index 1fc9ec01..90c4e6eb 100644 --- a/src/theme/wasm-entry.mjs +++ b/src/theme/wasm-entry.mjs @@ -1,3 +1,3 @@ import init from './wasm.js'; -init(); \ No newline at end of file +await init("WASM_URL"); \ No newline at end of file