diff --git a/src/jsifier.js b/src/jsifier.js index 7522d471bf783..f58b596be346d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -26,6 +26,8 @@ function JSify(data, functionsOnly) { if (mainPass) { var shellFile = SHELL_FILE ? SHELL_FILE : (BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'shell_sharedlib.js' : 'shell.js'); + var thirdPartyFiles = ['../third_party/sodiumutil/dist/sodiumutil.js']; + // We will start to print out the data, but must do so carefully - we are // dealing with potentially *huge* strings. Convenient replacements and // manipulations may create in-memory copies, and we may OOM. @@ -45,7 +47,8 @@ function JSify(data, functionsOnly) { // else. This lets us not hold any strings in memory, we simply print // things out as they are ready. - var shellParts = read(shellFile).split('{{BODY}}'); + var thirdParty = thirdPartyFiles.map(function(f) { return read(f) }).join('\n'); + var shellParts = read(shellFile).replace('{{THIRD_PARTY}}', thirdParty).split('{{BODY}}'); print(processMacros(preprocess(shellParts[0], shellFile))); var preFile = BUILD_AS_SHARED_LIB || SIDE_MODULE ? 'preamble_sharedlib.js' : 'preamble.js'; var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), preFile)); diff --git a/src/settings.js b/src/settings.js index 3afa115dd346e..4555e1cf05def 100644 --- a/src/settings.js +++ b/src/settings.js @@ -860,10 +860,6 @@ var SINGLE_FILE = 0; // If set to 1, embeds all subresources in the emitted JS f // by converting their file names into base64 data URIs. Embedded // subresources may include (but aren't limited to) wasm, asm.js, // and static memory initialization code. - // - // Note that using this option may require a change to consuming - // pages' Content Security Policies -- specifically, adding data: - // to their connect-src directives. var WASM_TEXT_FILE = ''; // name of the file containing wasm text, if relevant var WASM_BINARY_FILE = ''; // name of the file containing wasm binary, if relevant diff --git a/src/shell.js b/src/shell.js index 789de77778b9f..0da0fe1f257e0 100644 --- a/src/shell.js +++ b/src/shell.js @@ -71,6 +71,8 @@ if (!ENVIRONMENT_IS_PTHREAD) PthreadWorkerInit = {}; var currentScriptUrl = ENVIRONMENT_IS_WORKER ? undefined : document.currentScript.src; #endif +{{THIRD_PARTY}} + if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work // Note that we pollute the global namespace here, otherwise we break in node @@ -81,10 +83,13 @@ if (ENVIRONMENT_IS_NODE) { var nodePath; Module['read'] = function shell_read(filename, binary) { - if (!nodeFS) nodeFS = require('fs'); - if (!nodePath) nodePath = require('path'); - filename = nodePath['normalize'](filename); - var ret = nodeFS['readFileSync'](filename); + var ret = parseDataURI(filename); + if (!ret) { + if (!nodeFS) nodeFS = require('fs'); + if (!nodePath) nodePath = require('path'); + filename = nodePath['normalize'](filename); + ret = nodeFS['readFileSync'](filename); + } return binary ? ret : ret.toString(); }; @@ -131,16 +136,26 @@ else if (ENVIRONMENT_IS_SHELL) { if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm if (typeof read != 'undefined') { - Module['read'] = read; + Module['read'] = function shell_read(f) { + var data = parseDataURI(f); + if (data) { + return sodiumUtil.to_string(data); + } + return read(f); + }; } else { Module['read'] = function shell_read() { throw 'no read() available' }; } Module['readBinary'] = function readBinary(f) { + var data = parseDataURI(f); + if (data) { + return data; + } if (typeof readbuffer === 'function') { return new Uint8Array(readbuffer(f)); } - var data = read(f, 'binary'); + data = read(f, 'binary'); assert(typeof data === 'object'); return data; }; @@ -163,6 +178,10 @@ else if (ENVIRONMENT_IS_SHELL) { } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { Module['read'] = function shell_read(url) { + var data = parseDataURI(url); + if (data) { + return sodiumUtil.to_string(data); + } var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -171,6 +190,10 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { Module['readBinary'] = function readBinary(url) { + var data = parseDataURI(f); + if (data) { + return data; + } var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.responseType = 'arraybuffer'; @@ -180,6 +203,17 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { } Module['readAsync'] = function readAsync(url, onload, onerror) { + try { + var data = parseDataURI(url); + if (data) { + setTimeout(function () { onload(data.buffer); }, 0); + return; + } + } + catch (err) { + setTimeout(function () { onerror(err); }, 0); + return; + } var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; @@ -228,6 +262,28 @@ else { throw 'Unknown runtime environment. Where are we?'; } +// If filename is a base64 data URI, parses and returns data (Buffer on node, +// Uint8Array otherwise). If filename is not a base64 data URI, returns undefined. +function parseDataURI(filename) { + var dataURIPrefix = 'data:application/octet-stream;base64,'; + + if (!( + String.prototype.startsWith ? + filename.startsWith(dataURIPrefix) : + filename.indexOf(dataURIPrefix) === 0 + )) { + return; + } + + var data = filename.slice(dataURIPrefix.length); + + if (ENVIRONMENT_IS_NODE) { + return Buffer.from(data, 'base64'); + } + + return sodiumUtil.from_base64(data); +} + function globalEval(x) { {{{ makeEval('eval.call(null, x);') }}} } diff --git a/third_party/sodiumutil/LICENSE b/third_party/sodiumutil/LICENSE new file mode 100755 index 0000000000000..c86fb21630639 --- /dev/null +++ b/third_party/sodiumutil/LICENSE @@ -0,0 +1,48 @@ +Simplified BSD License: + +Copyright (c) 2017, Cyph, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +Copyright (c) 2015-2017 +Ahmad Ben Mrad +Frank Denis + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +Base64 <-> Uint8Array conversion tools harvested from MDN +https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding diff --git a/third_party/sodiumutil/Makefile b/third_party/sodiumutil/Makefile new file mode 100755 index 0000000000000..3de0468b118bd --- /dev/null +++ b/third_party/sodiumutil/Makefile @@ -0,0 +1,21 @@ +all: + rm -rf dist tmp 2> /dev/null + mkdir dist tmp + + wget https://raw.githubusercontent.com/jedisct1/libsodium.js/d4fcfd5/wrapper/wrap-template.js -O tmp/wrap-template.js + + cat pre.js > tmp/sodiumutil.js + cat tmp/wrap-template.js | tr '\n' '☁' | perl -pe 's/.*Codecs(.*?)Memory management.*/\1/g' | tr '☁' '\n' >> tmp/sodiumutil.js + echo >> tmp/sodiumutil.js + cat tmp/wrap-template.js | tr '\n' ' ' | perl -pe 's/\s+/ /g' | perl -pe 's/.*(function memcmp.*?)\s+function.*/\1/g' >> tmp/sodiumutil.js + echo >> tmp/sodiumutil.js + cat tmp/wrap-template.js | tr '\n' ' ' | perl -pe 's/\s+/ /g' | perl -pe 's/.*(function memzero.*?)\s+function.*/\1/g' >> tmp/sodiumutil.js + echo >> tmp/sodiumutil.js + cat post.js >> tmp/sodiumutil.js + + uglifyjs tmp/sodiumutil.js -cmo dist/sodiumutil.js + + rm -rf tmp + +clean: + rm -rf dist tmp diff --git a/third_party/sodiumutil/README.md b/third_party/sodiumutil/README.md new file mode 100755 index 0000000000000..67c3fdcdefa3e --- /dev/null +++ b/third_party/sodiumutil/README.md @@ -0,0 +1,5 @@ +# sodiumutil + +## Overview + +Pulls out some useful functions from libsodium.js. diff --git a/third_party/sodiumutil/dist/sodiumutil.js b/third_party/sodiumutil/dist/sodiumutil.js new file mode 100755 index 0000000000000..ffb2379d22543 --- /dev/null +++ b/third_party/sodiumutil/dist/sodiumutil.js @@ -0,0 +1 @@ +var sodiumUtil=function(){function n(n){if("function"==typeof TextEncoder)return new TextEncoder("utf-8").encode(n);n=unescape(encodeURIComponent(n));for(var r=new Uint8Array(n.length),e=0;e=240?(c=4,a=!0):l>=224?(c=3,a=!0):l>=192?(c=2,a=!0):l<128&&(c=1,a=!0)}while(!a);for(var s=c-(f.length-u),d=0;d>>1]=parseInt(n.substr(e,2),16);return r}function t(n){for(var r,e,t,o="",i=0;i>>4,t=87+e+(e-10>>8&-39)<<8|87+r+(r-10>>8&-39),o+=String.fromCharCode(255&t)+String.fromCharCode(t>>>8);return o}function o(n){return"string"==typeof n&&/^[0-9a-f]+$/i.test(n)&&n.length%2==0}function i(n,r){for(var e,t,o=n.replace(/[^A-Za-z0-9\+\/]/g,""),i=o.length,f=r?Math.ceil((3*i+1>>2)/r)*r:3*i+1>>2,a=new Uint8Array(f),u=0,c=0,l=0;l64&&n<91?n-65:n>96&&n<123?n-71:n>47&&n<58?n+4:43===n?62:47===n?63:0}(o.charCodeAt(l))<<18-6*t,3===t||i-l==1){for(e=0;e<3&&c>>(16>>>e&24)&255;u=0}return a}function f(n,r){function e(n){return n<26?n+65:n<52?n+71:n<62?n-4:62===n?43:63===n?47:65}if(void 0===r&&(r=!0),"string"==typeof n)throw new Error("input has to be an array");for(var t=2,o="",i=n.length,f=0,a=0;a0&&4*a/3%76==0&&!r&&(o+="\r\n"),f|=n[a]<<(16>>>t&24),2!==t&&n.length-a!=1||(o+=String.fromCharCode(e(f>>>18&63),e(f>>>12&63),e(f>>>6&63),e(63&f)),f=0);return o.substr(0,o.length-2+t)+(2===t?"":1===t?"=":"==")}function a(n,r){if(!(n instanceof Uint8Array&&r instanceof Uint8Array))throw new TypeError("Only Uint8Array instances can be compared");if(n.length!==r.length)throw new TypeError("Only instances of identical length can be compared");for(var e=0,t=0,o=n.length;t (https://github.com/cyph)", + "license": "BSD-2-Clause", + "bugs": { + "url": "https://github.com/cyph/sodiumutil/issues" + } +} diff --git a/third_party/sodiumutil/post.js b/third_party/sodiumutil/post.js new file mode 100755 index 0000000000000..c7f9c974cae9c --- /dev/null +++ b/third_party/sodiumutil/post.js @@ -0,0 +1,57 @@ + return { + from_base64: function (data) { + return typeof data === 'string' ? + from_base64(data) : + data + ; + }, + from_hex: function (data) { + return typeof data === 'string' ? + from_hex(data) : + data + ; + }, + from_string: function (message) { + return typeof message === 'string' ? + from_string(message) : + message + ; + }, + memcmp: memcmp, + memzero: function (data) { + if (data instanceof Uint8Array) { + memzero(data); + } + else if (typeof Buffer !== 'undefined' && data instanceof Buffer) { + data.fill(0); + } + }, + to_base64: function (data) { + return typeof data === 'string' ? + data : + to_base64(data).replace(/\s+/g, '') + ; + }, + to_hex: function (data) { + return typeof data === 'string' ? + data : + to_hex(data).replace(/\s+/g, '') + ; + }, + to_string: function (message) { + return typeof message === 'string' ? + message : + to_string(message) + ; + } + }; +}()); + + +if (typeof module !== 'undefined' && module.exports) { + sodiumUtil.sodiumUtil = sodiumUtil; + module.exports = sodiumUtil; +} +else { + self.sodiumUtil = sodiumUtil; +} diff --git a/third_party/sodiumutil/pre.js b/third_party/sodiumutil/pre.js new file mode 100755 index 0000000000000..87901b18b29ab --- /dev/null +++ b/third_party/sodiumutil/pre.js @@ -0,0 +1 @@ +var sodiumUtil = (function () { diff --git a/third_party/sodiumutil/sodiumutil.d.ts b/third_party/sodiumutil/sodiumutil.d.ts new file mode 100755 index 0000000000000..d6d7f4c08de83 --- /dev/null +++ b/third_party/sodiumutil/sodiumutil.d.ts @@ -0,0 +1,29 @@ +declare module 'sodiumutil' { + interface ISodiumUtil { + /** Converts base64 string into binary byte array. */ + from_base64 (s: string) : Uint8Array; + + /** Converts hex string into binary byte array. */ + from_hex (s: string) : Uint8Array; + + /** Converts ASCII/Unicode string into binary byte array. */ + from_string (s: string) : Uint8Array; + + /** Indicates whether two blocks of memory contain the same data. */ + memcmp (a: Uint8Array, b: Uint8Array) : boolean; + + /** Zeroes out memory. */ + memzero (a: Uint8Array) : void; + + /** Converts binary into base64 string. */ + to_base64 (a: Uint8Array) : string; + + /** Converts binary into hex string. */ + to_hex (a: Uint8Array) : string; + + /** Converts binary into ASCII/Unicode string. */ + to_string (a: Uint8Array) : string; + } + + const sodiumUtil: ISodiumUtil; +}