From e04b254b4889ae75f0e4d43111195e2c8e250657 Mon Sep 17 00:00:00 2001 From: titaneric Date: Sat, 20 Mar 2021 16:37:50 +0800 Subject: [PATCH 01/11] Test iframe rendering --- src/theme/book.js | 69 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/src/theme/book.js b/src/theme/book.js index 5e386369f7..7fa0364b6c 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -32,12 +32,12 @@ function playground_text(playground) { method: 'POST', mode: 'cors', }) - .then(response => response.json()) - .then(response => { - // get list of crates available in the rust playground - let playground_crates = response.crates.map(item => item["id"]); - playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); - }); + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); } function handle_crate_list_update(playground_block, playground_crates) { @@ -124,17 +124,46 @@ function playground_text(playground) { result_block.innerText = "Running..."; - fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { - headers: { - 'Content-Type': "application/json", - }, - method: 'POST', - mode: 'cors', - body: JSON.stringify(params) + // fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + // headers: { + // 'Content-Type': "application/json", + // }, + // method: 'POST', + // mode: 'cors', + // body: JSON.stringify(params) + // }) + new Promise((resolve, reject) => { + setTimeout(() => { + resolve("foo"); + }, 200) }) - .then(response => response.json()) - .then(response => result_block.innerText = response.result) - .catch(error => result_block.innerText = "Playground Communication: " + error.message); + // .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, + options = { + objid: 152, + key: 316541321 + }, + //src = "host/widget.js", + uri = encodeURIComponent(JSON.stringify(options)); + doc.someVar = "Hello World!"; + iframe.id = "iframewidget"; + iframe.width = result_block.width; + iframe.height = result_block.height; + + var html = '
'; + doc.open().write(html); + doc.close(); + }) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } // Syntax highlighting Configuration @@ -146,7 +175,7 @@ function playground_text(playground) { let code_nodes = Array .from(document.querySelectorAll('code')) // Don't highlight `inline code` blocks in headers. - .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + .filter(function (node) { return !node.parentElement.classList.contains("header"); }); if (window.ace) { // language-rust class needs to be removed for editable @@ -363,7 +392,7 @@ function playground_text(playground) { set_theme(theme); }); - themePopup.addEventListener('focusout', function(e) { + themePopup.addEventListener('focusout', function (e) { // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { hideThemes(); @@ -371,7 +400,7 @@ function playground_text(playground) { }); // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 - document.addEventListener('click', function(e) { + document.addEventListener('click', function (e) { if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { hideThemes(); } @@ -593,7 +622,7 @@ function playground_text(playground) { }); })(); -(function scrollToTop () { +(function scrollToTop() { var menuTitle = document.querySelector('.menu-title'); menuTitle.addEventListener('click', function () { From bcd57a6b225407f20734e6d789910d364659896a Mon Sep 17 00:00:00 2001 From: titaneric Date: Sun, 21 Mar 2021 00:53:20 +0800 Subject: [PATCH 02/11] Refactor the iframe generator --- src/theme/book.js | 31 ++++++++++++++----------------- src/theme/iframe.html | 18 ++++++++++++++++++ src/theme/wasm-entry.mjs | 3 +++ 3 files changed, 35 insertions(+), 17 deletions(-) create mode 100644 src/theme/iframe.html create mode 100644 src/theme/wasm-entry.mjs diff --git a/src/theme/book.js b/src/theme/book.js index 7fa0364b6c..828875b445 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -144,23 +144,20 @@ function playground_text(playground) { .then(response => { result_block.innerText = ""; var iframe = result_block.appendChild(document.createElement('iframe')), - doc = iframe.contentWindow.document, - options = { - objid: 152, - key: 316541321 - }, - //src = "host/widget.js", - uri = encodeURIComponent(JSON.stringify(options)); - doc.someVar = "Hello World!"; - iframe.id = "iframewidget"; - iframe.width = result_block.width; - iframe.height = result_block.height; - - var html = '
'; - doc.open().write(html); - doc.close(); + 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(); }) .catch(error => result_block.innerText = "Playground Communication: " + error.message); diff --git a/src/theme/iframe.html b/src/theme/iframe.html new file mode 100644 index 0000000000..0ad377685c --- /dev/null +++ b/src/theme/iframe.html @@ -0,0 +1,18 @@ + + + + + + +
+ \ No newline at end of file diff --git a/src/theme/wasm-entry.mjs b/src/theme/wasm-entry.mjs new file mode 100644 index 0000000000..1fc9ec01d1 --- /dev/null +++ b/src/theme/wasm-entry.mjs @@ -0,0 +1,3 @@ +import init from './wasm.js'; + +init(); \ No newline at end of file From 05d553640f3b1dac714cc521b5e00f42a468c3bc Mon Sep 17 00:00:00 2001 From: titaneric Date: Sun, 21 Mar 2021 01:44:07 +0800 Subject: [PATCH 03/11] 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 264c113d3d..a54c08eb5d 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 f417a014df..399bad0d0e 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 a1ee18affd..ef29cc4872 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 04/11] 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 828875b445..af0b54eec7 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 0ad377685c..4cc47cd015 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 1fc9ec01d1..90c4e6eb40 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 From 6816d47d134c243cb2fde7044e7cd3703c9c0984 Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 1 Apr 2021 14:49:53 +0800 Subject: [PATCH 05/11] 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 828875b445..af0b54eec7 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 0ad377685c..4cc47cd015 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 1fc9ec01d1..90c4e6eb40 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 From b206ed0689831ee1ac6c442d3a1035b8adfadb44 Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 1 Apr 2021 18:09:51 +0800 Subject: [PATCH 06/11] Add wasm class to determine type of request --- src/theme/book.js | 91 +++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/src/theme/book.js b/src/theme/book.js index af0b54eec7..13405ac506 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -59,7 +59,14 @@ function playground_text(playground) { win: "Ctrl-Enter", mac: "Ctrl-Enter" }, - exec: _editor => run_rust_code(playground_block) + exec: _editor => { + console.log(code_block.classList.contains("wasm")); + if (code_block.classList.contains("wasm")) { + run_wasm_pack_code(playground_block); + } else { + run_rust_code(playground_block); + } + } }); } } @@ -124,32 +131,51 @@ 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", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => result_block.innerText = response.result) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + function run_wasm_pack_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); } - // fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { - // headers: { - // 'Content-Type': "application/json", - // }, - // method: 'POST', - // mode: 'cors', - // body: JSON.stringify(params) - // }) + + let text = playground_text(code_block); + + var params = { + code: text, + }; + + result_block.innerText = "Running..."; + 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%"; + var iframe = document.createElement('iframe'); iframe.style.height = "100%"; - iframe.border = 0; - iframe.scrolling = "no"; - doc.open().write(html); - doc.close(); - }) + iframe.style.width = "100%"; + iframe.style.padding = 0; + iframe.style.margin = 0; + iframe.style.border = 0; + iframe.src = createObjectURL(html, "text/html"); + result_block.appendChild(iframe); + }); } + // Greatly inspired from WebAssemblyStudio async function prepareSandbox(params) { - var wasmResult = fetch_with_timeout("http://192.168.217.100:9999/wasm-pack", { + var wasmResult = fetch_with_timeout("http://127.0.0.1:9999/wasm-pack", { headers: { 'Content-Type': "application/json", }, @@ -159,10 +185,9 @@ function playground_text(playground) { }) .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 + wasm_bg: base64ToByteArray(wasm_bg) } }) .catch(error => result_block.innerText = "Playground Communication: " + error.message); @@ -185,19 +210,15 @@ function playground_text(playground) { } 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); + var jsBlob = createObjectURL(src, "application/javascript"); 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); + var wasmJSBlob = createObjectURL(wasmJS, "application/javascript"); + var bgWasmBlob = createObjectURL(bgWasm, "application/wasm"); // replace wasm.js src = src.replace(/\bfrom\s+['"](.+?)['"](\s*[;\n])/g, (all, path, sep) => { @@ -209,6 +230,10 @@ function playground_text(playground) { }) return src } + + function createObjectURL(src, mime) { + return URL.createObjectURL(new Blob([src], { type: mime })); + } // Syntax highlighting Configuration hljs.configure({ @@ -313,7 +338,11 @@ function playground_text(playground) { buttons.insertBefore(runCodeButton, buttons.firstChild); runCodeButton.addEventListener('click', function (e) { - run_rust_code(pre_block); + if (code_block.classList.contains("wasm")) { + run_wasm_pack_code(pre_block); + } else { + run_rust_code(pre_block); + } }); if (window.playground_copyable) { From f49d0cc17ebd7649bea257d87a59b1480e4b47ea Mon Sep 17 00:00:00 2001 From: titaneric Date: Tue, 13 Apr 2021 01:08:24 +0800 Subject: [PATCH 07/11] Update error handling --- src/theme/book.js | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/theme/book.js b/src/theme/book.js index 13405ac506..6df0cf4958 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -60,7 +60,6 @@ function playground_text(playground) { mac: "Ctrl-Enter" }, exec: _editor => { - console.log(code_block.classList.contains("wasm")); if (code_block.classList.contains("wasm")) { run_wasm_pack_code(playground_block); } else { @@ -161,18 +160,16 @@ function playground_text(playground) { result_block.innerText = "Running..."; - prepareSandbox(params).then(src => processHTML(src)).then(html => { - result_block.innerText = ""; - var iframe = document.createElement('iframe'); - iframe.style.height = "100%"; - iframe.style.width = "100%"; - iframe.style.padding = 0; - iframe.style.margin = 0; - iframe.style.border = 0; - iframe.src = createObjectURL(html, "text/html"); - result_block.appendChild(iframe); - }); + prepareSandbox(params) + .then(src => processHTML(src)) + .then(html => { + result_block.innerText = ""; + var iframe = createIFrame(html); + result_block.appendChild(iframe); + }) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); } + // Greatly inspired from WebAssemblyStudio async function prepareSandbox(params) { var wasmResult = fetch_with_timeout("http://127.0.0.1:9999/wasm-pack", { @@ -184,13 +181,16 @@ function playground_text(playground) { body: JSON.stringify(params) }) .then(response => response.json()) - .then(({ wasm_js, wasm_bg }) => { + .then(({ wasm_js, wasm_bg, error }) => { + if (error) { + throw new Error(error); + } + return { wasm_js: atob(wasm_js), wasm_bg: base64ToByteArray(wasm_bg) } }) - .catch(error => result_block.innerText = "Playground Communication: " + error.message); var htmlSrc = fetch(new Request("iframe.html")) .then(response => response.text()); @@ -198,8 +198,8 @@ function playground_text(playground) { .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); @@ -208,6 +208,7 @@ function playground_text(playground) { } return new Uint8Array(byteNumbers); } + async function processHTML([htmlSrc, jsSrc, { wasm_js, wasm_bg }]) { var src = rewriteJS(jsSrc, wasm_js, wasm_bg); var jsBlob = createObjectURL(src, "application/javascript"); @@ -230,11 +231,22 @@ function playground_text(playground) { }) return src } - + function createObjectURL(src, mime) { return URL.createObjectURL(new Blob([src], { type: mime })); } + function createIFrame(src) { + var iframe = document.createElement('iframe'); + iframe.style.height = "100%"; + iframe.style.width = "100%"; + iframe.style.padding = 0; + iframe.style.margin = 0; + iframe.style.border = 0; + iframe.src = createObjectURL(src, "text/html"); + return iframe + } + // Syntax highlighting Configuration hljs.configure({ tabReplace: ' ', // 4 spaces From bb7e8657cb21cb002f8874cd7c7940005e92d4fb Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 15 Apr 2021 01:57:03 +0800 Subject: [PATCH 08/11] Adapt wasm-pack new response --- src/theme/book.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/theme/book.js b/src/theme/book.js index 6df0cf4958..8a106ef52c 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -181,9 +181,9 @@ function playground_text(playground) { body: JSON.stringify(params) }) .then(response => response.json()) - .then(({ wasm_js, wasm_bg, error }) => { - if (error) { - throw new Error(error); + .then(({ wasm_js, wasm_bg, success, stderr }) => { + if (!success) { + throw new Error(stderr); } return { @@ -238,11 +238,13 @@ function playground_text(playground) { function createIFrame(src) { var iframe = document.createElement('iframe'); + iframe.scrolling = 'no'; iframe.style.height = "100%"; iframe.style.width = "100%"; iframe.style.padding = 0; iframe.style.margin = 0; iframe.style.border = 0; + iframe.style.overflow = "hidden"; iframe.src = createObjectURL(src, "text/html"); return iframe } From 2c0b3f8c74c8359b786b81fbf3c6e8f54ab61b1c Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 15 Apr 2021 13:11:56 +0800 Subject: [PATCH 09/11] Format Rust code --- src/book/init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/book/init.rs b/src/book/init.rs index a54c08eb5d..5442a7f1e9 100644 --- a/src/book/init.rs +++ b/src/book/init.rs @@ -156,13 +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(()) } From e43b231174b6ce89c4ab28bc1c2898785f6b42b6 Mon Sep 17 00:00:00 2001 From: titaneric Date: Thu, 15 Apr 2021 14:18:04 +0800 Subject: [PATCH 10/11] Update them_dir files in test --- src/theme/mod.rs | 2 ++ tests/init.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/theme/mod.rs b/src/theme/mod.rs index ef29cc4872..de70b98bfb 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -227,6 +227,8 @@ mod tests { "highlight.css", "ayu-highlight.css", "clipboard.min.js", + "iframe.html", + "wasm-entry.mjs", ]; let temp = TempFileBuilder::new().prefix("mdbook-").tempdir().unwrap(); diff --git a/tests/init.rs b/tests/init.rs index 4deb840194..72fce0eee3 100644 --- a/tests/init.rs +++ b/tests/init.rs @@ -123,7 +123,9 @@ fn copy_theme() { "favicon.svg", "highlight.css", "highlight.js", + "iframe.html", "index.hbs", + "wasm-entry.mjs", ]; let theme_dir = temp.path().join("theme"); let mut actual: Vec<_> = walkdir::WalkDir::new(&theme_dir) From 9d96b8bee3e37cb9b7815b7a354b061dcc7f9502 Mon Sep 17 00:00:00 2001 From: titaneric Date: Mon, 20 Sep 2021 18:46:45 +0800 Subject: [PATCH 11/11] Add served playground --- src/theme/book.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme/book.js b/src/theme/book.js index 8a106ef52c..907b41376d 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -172,7 +172,7 @@ function playground_text(playground) { // Greatly inspired from WebAssemblyStudio async function prepareSandbox(params) { - var wasmResult = fetch_with_timeout("http://127.0.0.1:9999/wasm-pack", { + var wasmResult = fetch_with_timeout("https://playground.titaneric.com/wasm-pack", { headers: { 'Content-Type': "application/json", },