diff --git a/app/scripts/util/features.js b/app/scripts/util/features.js index 281dcea0..597fe23c 100644 --- a/app/scripts/util/features.js +++ b/app/scripts/util/features.js @@ -16,6 +16,7 @@ const Features = { !isDesktop && !/^http(s?):\/\/((localhost:8085)|((app|beta)\.keeweb\.info))/.test(location.href), needFixClicks: /Edge\/14/.test(navigator.appVersion), + canUseWasmInWebWorker: !isDesktop && !/Chrome/.test(navigator.appVersion), supportsTitleBarStyles() { return this.isMac; diff --git a/app/scripts/util/kdbxweb/kdbxweb-init.js b/app/scripts/util/kdbxweb/kdbxweb-init.js index 1c6ac4c5..86b3b72e 100644 --- a/app/scripts/util/kdbxweb/kdbxweb-init.js +++ b/app/scripts/util/kdbxweb/kdbxweb-init.js @@ -1,5 +1,6 @@ import kdbxweb from 'kdbxweb'; import { Logger } from 'util/logger'; +import { Features } from 'util/features'; const logger = new Logger('argon2'); @@ -43,69 +44,116 @@ const KdbxwebInit = { totalMemory ); - const memoryDecl = `var wasmMemory=new WebAssembly.Memory({initial:${initialMemory},maximum:${totalMemory}});`; - const moduleDecl = - 'var Module={' + - 'wasmJSMethod: "native-wasm",' + - 'wasmBinary: Uint8Array.from(atob("' + - wasmBinaryBase64 + - '"), c => c.charCodeAt(0)),' + - 'print(...args) { postMessage({op:"log",args}) },' + - 'printErr(...args) { postMessage({op:"log",args}) },' + - 'postRun:' + - this.workerPostRun.toString() + - ',' + - 'calcHash:' + - this.calcHash.toString() + - ',' + - 'wasmMemory:wasmMemory,' + - 'buffer:wasmMemory.buffer,' + - 'TOTAL_MEMORY:' + - initialMemory * WASM_PAGE_SIZE + - '}'; - const script = argon2LoaderCode.replace(/^var Module.*?}/, memoryDecl + moduleDecl); - const blob = new Blob([script], { type: 'application/javascript' }); - const objectUrl = URL.createObjectURL(blob); - const worker = new Worker(objectUrl); - const onMessage = e => { - switch (e.data.op) { - case 'log': - logger.debug(...e.data.args); - break; - case 'postRun': - logger.debug('WebAssembly runtime loaded', logger.ts(ts)); - URL.revokeObjectURL(objectUrl); + if (Features.canUseWasmInWebWorker) { + const memoryDecl = `var wasmMemory=new WebAssembly.Memory({initial:${initialMemory},maximum:${totalMemory}});`; + const moduleDecl = + 'var Module={' + + 'wasmJSMethod: "native-wasm",' + + 'wasmBinary: Uint8Array.from(atob("' + + wasmBinaryBase64 + + '"), c => c.charCodeAt(0)),' + + 'print(...args) { postMessage({op:"log",args}) },' + + 'printErr(...args) { postMessage({op:"log",args}) },' + + 'postRun:' + + this.workerPostRun.toString() + + ',' + + 'calcHash:' + + this.calcHash.toString() + + ',' + + 'wasmMemory:wasmMemory,' + + 'buffer:wasmMemory.buffer,' + + 'TOTAL_MEMORY:' + + initialMemory * WASM_PAGE_SIZE + + '}'; + const script = argon2LoaderCode.replace( + /^var Module.*?}/, + memoryDecl + moduleDecl + ); + const blob = new Blob([script], { type: 'application/javascript' }); + const objectUrl = URL.createObjectURL(blob); + const worker = new Worker(objectUrl); + const onMessage = e => { + switch (e.data.op) { + case 'log': + logger.debug(...e.data.args); + break; + case 'postRun': + logger.debug( + 'WebAssembly runtime loaded (web worker)', + logger.ts(ts) + ); + URL.revokeObjectURL(objectUrl); + clearTimeout(loadTimeout); + worker.removeEventListener('message', onMessage); + this.runtimeModule = { + hash(args) { + return new Promise((resolve, reject) => { + worker.postMessage(args); + const onHashMessage = e => { + worker.removeEventListener( + 'message', + onHashMessage + ); + worker.terminate(); + KdbxwebInit.runtimeModule = null; + if (!e.data || e.data.error || !e.data.hash) { + const ex = + (e.data && e.data.error) || + 'unexpected error'; + logger.error('Worker error', ex); + reject(ex); + } else { + resolve(e.data.hash); + } + }; + worker.addEventListener('message', onHashMessage); + }); + } + }; + resolve(this.runtimeModule); + break; + default: + logger.error('Unknown message', e.data); + URL.revokeObjectURL(objectUrl); + reject('Load error'); + } + }; + worker.addEventListener('message', onMessage); + } else { + // Chrome and Electron crash if we use WASM in WebWorker + // see https://github.com/keeweb/keeweb/issues/1263 + const wasmMemory = new WebAssembly.Memory({ + initial: initialMemory, + maximum: totalMemory + }); + global.Module = { + wasmJSMethod: 'native-wasm', + wasmBinary: Uint8Array.from(atob(wasmBinaryBase64), c => c.charCodeAt(0)), + print(...args) { + logger.debug(...args); + }, + printErr(...args) { + logger.debug(...args); + }, + postRun: () => { + logger.debug('WebAssembly runtime loaded (main thread)', logger.ts(ts)); clearTimeout(loadTimeout); - worker.removeEventListener('message', onMessage); - this.runtimeModule = { - hash(args) { - return new Promise((resolve, reject) => { - worker.postMessage(args); - const onHashMessage = e => { - worker.removeEventListener('message', onHashMessage); - worker.terminate(); - KdbxwebInit.runtimeModule = null; - if (!e.data || e.data.error || !e.data.hash) { - const ex = - (e.data && e.data.error) || 'unexpected error'; - logger.error('Worker error', ex); - reject(ex); - } - resolve(e.data.hash); - }; - worker.addEventListener('message', onHashMessage); - }); + resolve({ + hash: args => { + const hash = this.calcHash(global.Module, args); + global.Module.unloadRuntime(); + global.Module = undefined; + return Promise.resolve(hash); } - }; - resolve(this.runtimeModule); - break; - default: - logger.error('Unknown message', e.data); - URL.revokeObjectURL(objectUrl); - reject('Load error'); - } - }; - worker.addEventListener('message', onMessage); + }); + }, + wasmMemory, + buffer: wasmMemory.buffer, + TOTAL_MEMORY: initialMemory * WASM_PAGE_SIZE + }; + // eslint-disable-next-line no-eval + eval(argon2LoaderCode); + } } catch (err) { reject(err); } diff --git a/package-lock.json b/package-lock.json index bc98600e..08c8f182 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1361,9 +1361,9 @@ } }, "argon2-browser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/argon2-browser/-/argon2-browser-1.11.1.tgz", - "integrity": "sha512-C+WsBLSkwQExkDYB7vriugrBTXq2z+fTRDlGWqr2zm89TaKo7zYtSGARMgoBxpDnmNNzduNlZJmpY2j0Dp7ZOQ==" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/argon2-browser/-/argon2-browser-1.12.0.tgz", + "integrity": "sha512-qdEW311acXGGLTjslqO5f7cKG3foP6QUkeuav9au00aRY/B5AvNrqAVlL47a2MUdr6i7dx8Xp+5OreRGoXvCgQ==" }, "argparse": { "version": "1.0.10", diff --git a/package.json b/package.json index ca839fb8..6ab3c466 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@babel/plugin-external-helpers": "^7.2.0", "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/preset-env": "^7.6.2", - "argon2-browser": "1.11.1", + "argon2-browser": "1.12.0", "autoprefixer": "^9.6.1", "babel-cli": "^6.26.0", "babel-eslint": "^10.0.3", diff --git a/release-notes.md b/release-notes.md index 0dbf3dcc..ac401d74 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,6 +2,7 @@ Release notes ------------- ##### v1.11.10 (2019-10-15) `-` fix #1305: WebDAV issues +`-` fix #1263: desktop apps crashes when argon2 is used ##### v1.11.9 (2019-10-13) `-` fix #1300: selecting auto-type sequence items issues