WIP worker work
This commit is contained in:
parent
17412bbb2f
commit
fdbc5f3c1d
9 changed files with 248 additions and 23 deletions
|
@ -84,10 +84,11 @@ async function build() {
|
||||||
// also creates the directories where the theme css bundles are placed in,
|
// also creates the directories where the theme css bundles are placed in,
|
||||||
// so do it first
|
// so do it first
|
||||||
const themeAssets = await copyThemeAssets(themes, legacy);
|
const themeAssets = await copyThemeAssets(themes, legacy);
|
||||||
const jsBundlePath = await buildJs();
|
const jsBundlePath = await buildJs("src/main.js", `${PROJECT_ID}.js`);
|
||||||
const jsLegacyBundlePath = await buildJsLegacy();
|
const jsLegacyBundlePath = await buildJsLegacy("src/main.js", `${PROJECT_ID}-legacy.js`);
|
||||||
|
const jsWorkerPath = await buildWorkerJsLegacy("src/worker.js", `worker.js`);
|
||||||
const cssBundlePaths = await buildCssBundles(legacy ? buildCssLegacy : buildCss, themes, themeAssets);
|
const cssBundlePaths = await buildCssBundles(legacy ? buildCssLegacy : buildCss, themes, themeAssets);
|
||||||
const assetPaths = createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, themeAssets);
|
const assetPaths = createAssetPaths(jsBundlePath, jsLegacyBundlePath, jsWorkerPath, cssBundlePaths, themeAssets);
|
||||||
|
|
||||||
let manifestPath;
|
let manifestPath;
|
||||||
if (offline) {
|
if (offline) {
|
||||||
|
@ -98,7 +99,7 @@ async function build() {
|
||||||
console.log(`built ${PROJECT_ID} ${version} successfully`);
|
console.log(`built ${PROJECT_ID} ${version} successfully`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, themeAssets) {
|
function createAssetPaths(jsBundlePath, jsLegacyBundlePath, jsWorkerPath, cssBundlePaths, themeAssets) {
|
||||||
function trim(path) {
|
function trim(path) {
|
||||||
if (!path.startsWith(targetDir)) {
|
if (!path.startsWith(targetDir)) {
|
||||||
throw new Error("invalid target path: " + targetDir);
|
throw new Error("invalid target path: " + targetDir);
|
||||||
|
@ -108,6 +109,7 @@ function createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, them
|
||||||
return {
|
return {
|
||||||
jsBundle: () => trim(jsBundlePath),
|
jsBundle: () => trim(jsBundlePath),
|
||||||
jsLegacyBundle: () => trim(jsLegacyBundlePath),
|
jsLegacyBundle: () => trim(jsLegacyBundlePath),
|
||||||
|
jsWorker: () => trim(jsWorkerPath),
|
||||||
cssMainBundle: () => trim(cssBundlePaths.main),
|
cssMainBundle: () => trim(cssBundlePaths.main),
|
||||||
cssThemeBundle: themeName => trim(cssBundlePaths.themes[themeName]),
|
cssThemeBundle: themeName => trim(cssBundlePaths.themes[themeName]),
|
||||||
cssThemeBundles: () => Object.values(cssBundlePaths.themes).map(a => trim(a)),
|
cssThemeBundles: () => Object.values(cssBundlePaths.themes).map(a => trim(a)),
|
||||||
|
@ -180,23 +182,24 @@ async function buildHtml(doc, version, assetPaths, manifestPath) {
|
||||||
await fs.writeFile(path.join(targetDir, "index.html"), doc.html(), "utf8");
|
await fs.writeFile(path.join(targetDir, "index.html"), doc.html(), "utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildJs() {
|
async function buildJs(inputFile, outputName) {
|
||||||
// create js bundle
|
// create js bundle
|
||||||
const bundle = await rollup({
|
const bundle = await rollup({
|
||||||
input: 'src/main.js',
|
input: inputFile,
|
||||||
plugins: [removeJsComments({comments: "none"})]
|
plugins: [removeJsComments({comments: "none"})]
|
||||||
});
|
});
|
||||||
const {output} = await bundle.generate({
|
const {output} = await bundle.generate({
|
||||||
format: 'es',
|
format: 'es',
|
||||||
|
// TODO: can remove this?
|
||||||
name: `${PROJECT_ID}Bundle`
|
name: `${PROJECT_ID}Bundle`
|
||||||
});
|
});
|
||||||
const code = output[0].code;
|
const code = output[0].code;
|
||||||
const bundlePath = resource(`${PROJECT_ID}.js`, code);
|
const bundlePath = resource(outputName, code);
|
||||||
await fs.writeFile(bundlePath, code, "utf8");
|
await fs.writeFile(bundlePath, code, "utf8");
|
||||||
return bundlePath;
|
return bundlePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildJsLegacy() {
|
async function buildJsLegacy(inputFile, outputName) {
|
||||||
// compile down to whatever IE 11 needs
|
// compile down to whatever IE 11 needs
|
||||||
const babelPlugin = babel.babel({
|
const babelPlugin = babel.babel({
|
||||||
babelHelpers: 'bundled',
|
babelHelpers: 'bundled',
|
||||||
|
@ -214,7 +217,7 @@ async function buildJsLegacy() {
|
||||||
});
|
});
|
||||||
// create js bundle
|
// create js bundle
|
||||||
const rollupConfig = {
|
const rollupConfig = {
|
||||||
input: ['src/legacy-polyfill.js', 'src/main.js'],
|
input: ['src/legacy-polyfill.js', inputFile],
|
||||||
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin, removeJsComments({comments: "none"})]
|
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin, removeJsComments({comments: "none"})]
|
||||||
};
|
};
|
||||||
const bundle = await rollup(rollupConfig);
|
const bundle = await rollup(rollupConfig);
|
||||||
|
@ -223,7 +226,39 @@ async function buildJsLegacy() {
|
||||||
name: `${PROJECT_ID}Bundle`
|
name: `${PROJECT_ID}Bundle`
|
||||||
});
|
});
|
||||||
const code = output[0].code;
|
const code = output[0].code;
|
||||||
const bundlePath = resource(`${PROJECT_ID}-legacy.js`, code);
|
const bundlePath = resource(outputName, code);
|
||||||
|
await fs.writeFile(bundlePath, code, "utf8");
|
||||||
|
return bundlePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildWorkerJsLegacy(inputFile, outputName) {
|
||||||
|
// compile down to whatever IE 11 needs
|
||||||
|
const babelPlugin = babel.babel({
|
||||||
|
babelHelpers: 'bundled',
|
||||||
|
exclude: 'node_modules/**',
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
useBuiltIns: "entry",
|
||||||
|
corejs: "3",
|
||||||
|
targets: "IE 11"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
// create js bundle
|
||||||
|
const rollupConfig = {
|
||||||
|
input: ['src/worker-polyfill.js', inputFile],
|
||||||
|
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin, removeJsComments({comments: "none"})]
|
||||||
|
};
|
||||||
|
const bundle = await rollup(rollupConfig);
|
||||||
|
const {output} = await bundle.generate({
|
||||||
|
format: 'iife',
|
||||||
|
name: `${PROJECT_ID}Bundle`
|
||||||
|
});
|
||||||
|
const code = output[0].code;
|
||||||
|
const bundlePath = resource(outputName, code);
|
||||||
await fs.writeFile(bundlePath, code, "utf8");
|
await fs.writeFile(bundlePath, code, "utf8");
|
||||||
return bundlePath;
|
return bundlePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export class TimelineViewModel extends ViewModel {
|
||||||
if (firstTile.shape === "gap") {
|
if (firstTile.shape === "gap") {
|
||||||
return firstTile.fill();
|
return firstTile.fill();
|
||||||
} else {
|
} else {
|
||||||
await this._timeline.loadAtTop(50);
|
await this._timeline.loadAtTop(10);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {SessionInfo} from "./decryption/SessionInfo.js";
|
||||||
import {DecryptionPreparation} from "./decryption/DecryptionPreparation.js";
|
import {DecryptionPreparation} from "./decryption/DecryptionPreparation.js";
|
||||||
import {SessionDecryption} from "./decryption/SessionDecryption.js";
|
import {SessionDecryption} from "./decryption/SessionDecryption.js";
|
||||||
import {SessionCache} from "./decryption/SessionCache.js";
|
import {SessionCache} from "./decryption/SessionCache.js";
|
||||||
|
import {DecryptionWorker} from "./decryption/DecryptionWorker.js";
|
||||||
|
|
||||||
function getSenderKey(event) {
|
function getSenderKey(event) {
|
||||||
return event.content?.["sender_key"];
|
return event.content?.["sender_key"];
|
||||||
|
@ -38,7 +39,9 @@ export class Decryption {
|
||||||
constructor({pickleKey, olm}) {
|
constructor({pickleKey, olm}) {
|
||||||
this._pickleKey = pickleKey;
|
this._pickleKey = pickleKey;
|
||||||
this._olm = olm;
|
this._olm = olm;
|
||||||
// this._worker = new MessageHandler(new Worker("worker-2580578233.js"));
|
// this._decryptor = new DecryptionWorker(new Worker("./src/worker.js"));
|
||||||
|
this._decryptor = new DecryptionWorker(new Worker("worker-3074010154.js"));
|
||||||
|
this._initPromise = this._decryptor.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
createSessionCache(fallback) {
|
createSessionCache(fallback) {
|
||||||
|
@ -55,6 +58,7 @@ export class Decryption {
|
||||||
* @return {DecryptionPreparation}
|
* @return {DecryptionPreparation}
|
||||||
*/
|
*/
|
||||||
async prepareDecryptAll(roomId, events, sessionCache, txn) {
|
async prepareDecryptAll(roomId, events, sessionCache, txn) {
|
||||||
|
await this._initPromise;
|
||||||
const errors = new Map();
|
const errors = new Map();
|
||||||
const validEvents = [];
|
const validEvents = [];
|
||||||
|
|
||||||
|
@ -85,7 +89,7 @@ export class Decryption {
|
||||||
errors.set(event.event_id, new DecryptionError("MEGOLM_NO_SESSION", event));
|
errors.set(event.event_id, new DecryptionError("MEGOLM_NO_SESSION", event));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sessionDecryptions.push(new SessionDecryption(sessionInfo, eventsForSession));
|
sessionDecryptions.push(new SessionDecryption(sessionInfo, eventsForSession, this._decryptor));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
64
src/matrix/e2ee/megolm/decryption/DecryptionWorker.js
Normal file
64
src/matrix/e2ee/megolm/decryption/DecryptionWorker.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class DecryptionWorker {
|
||||||
|
constructor(worker) {
|
||||||
|
this._worker = worker;
|
||||||
|
this._requests = new Map();
|
||||||
|
this._counter = 0;
|
||||||
|
this._worker.addEventListener("message", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(e) {
|
||||||
|
if (e.type === "message") {
|
||||||
|
const message = e.data;
|
||||||
|
console.log("worker reply", message);
|
||||||
|
const request = this._requests.get(message.replyToId);
|
||||||
|
if (request) {
|
||||||
|
if (message.type === "success") {
|
||||||
|
request.resolve(message.payload);
|
||||||
|
} else if (message.type === "error") {
|
||||||
|
request.reject(new Error(message.stack));
|
||||||
|
}
|
||||||
|
this._requests.delete(message.ref_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(message) {
|
||||||
|
this._counter += 1;
|
||||||
|
message.id = this._counter;
|
||||||
|
let resolve;
|
||||||
|
let reject;
|
||||||
|
const promise = new Promise((_resolve, _reject) => {
|
||||||
|
resolve = _resolve;
|
||||||
|
reject = _reject;
|
||||||
|
});
|
||||||
|
this._requests.set(message.id, {reject, resolve});
|
||||||
|
this._worker.postMessage(message);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypt(session, ciphertext) {
|
||||||
|
const sessionKey = session.export_session(session.first_known_index());
|
||||||
|
return this._send({type: "megolm_decrypt", ciphertext, sessionKey});
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
return this._send({type: "load_olm", path: "olm_legacy-3232457086.js"});
|
||||||
|
// return this._send({type: "load_olm", path: "../lib/olm/olm_legacy.js"});
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,10 +22,11 @@ import {ReplayDetectionEntry} from "./ReplayDetectionEntry.js";
|
||||||
* Does the actual decryption of all events for a given megolm session in a batch
|
* Does the actual decryption of all events for a given megolm session in a batch
|
||||||
*/
|
*/
|
||||||
export class SessionDecryption {
|
export class SessionDecryption {
|
||||||
constructor(sessionInfo, events) {
|
constructor(sessionInfo, events, decryptor) {
|
||||||
sessionInfo.retain();
|
sessionInfo.retain();
|
||||||
this._sessionInfo = sessionInfo;
|
this._sessionInfo = sessionInfo;
|
||||||
this._events = events;
|
this._events = events;
|
||||||
|
this._decryptor = decryptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
async decryptAll() {
|
async decryptAll() {
|
||||||
|
@ -38,7 +39,7 @@ export class SessionDecryption {
|
||||||
try {
|
try {
|
||||||
const {session} = this._sessionInfo;
|
const {session} = this._sessionInfo;
|
||||||
const ciphertext = event.content.ciphertext;
|
const ciphertext = event.content.ciphertext;
|
||||||
const {plaintext, message_index: messageIndex} = await this._decrypt(session, ciphertext);
|
const {plaintext, message_index: messageIndex} = await this._decryptor.decrypt(session, ciphertext);
|
||||||
let payload;
|
let payload;
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(plaintext);
|
payload = JSON.parse(plaintext);
|
||||||
|
@ -63,12 +64,6 @@ export class SessionDecryption {
|
||||||
return {results, errors, replayEntries};
|
return {results, errors, replayEntries};
|
||||||
}
|
}
|
||||||
|
|
||||||
async _decrypt(session, ciphertext) {
|
|
||||||
// const sessionKey = session.export_session(session.first_known_index());
|
|
||||||
// return this._worker.decrypt(sessionKey, ciphertext);
|
|
||||||
return session.decrypt(ciphertext);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this._sessionInfo.release();
|
this._sessionInfo.release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class Timeline {
|
||||||
|
|
||||||
/** @package */
|
/** @package */
|
||||||
async load() {
|
async load() {
|
||||||
const entries = await this._timelineReader.readFromEnd(50);
|
const entries = await this._timelineReader.readFromEnd(25);
|
||||||
this._remoteEntries.setManySorted(entries);
|
this._remoteEntries.setManySorted(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
src/worker-polyfill.js
Normal file
19
src/worker-polyfill.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// polyfills needed for IE11
|
||||||
|
import "regenerator-runtime/runtime";
|
||||||
|
import "core-js/modules/es.promise";
|
108
src/worker.js
Normal file
108
src/worker.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function asErrorMessage(err) {
|
||||||
|
return {
|
||||||
|
type: "error",
|
||||||
|
message: err.message,
|
||||||
|
stack: err.stack
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function asSuccessMessage(payload) {
|
||||||
|
return {
|
||||||
|
type: "success",
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageHandler {
|
||||||
|
constructor() {
|
||||||
|
this._olm = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(e) {
|
||||||
|
if (e.type === "message") {
|
||||||
|
this._handleMessage(e.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendReply(refMessage, reply) {
|
||||||
|
reply.replyToId = refMessage.id;
|
||||||
|
self.postMessage(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
_toMessage(fn) {
|
||||||
|
try {
|
||||||
|
let payload = fn();
|
||||||
|
if (payload instanceof Promise) {
|
||||||
|
return payload.then(
|
||||||
|
payload => asSuccessMessage(payload),
|
||||||
|
err => asErrorMessage(err)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return asSuccessMessage(payload);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return asErrorMessage(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadOlm(path) {
|
||||||
|
return this._toMessage(async () => {
|
||||||
|
// might have some problems here with window vs self as global object?
|
||||||
|
if (self.msCrypto && !self.crypto) {
|
||||||
|
self.crypto = self.msCrypto;
|
||||||
|
}
|
||||||
|
self.importScripts(path);
|
||||||
|
const olm = self.olm_exports;
|
||||||
|
// mangle the globals enough to make olm load believe it is running in a browser
|
||||||
|
self.window = self;
|
||||||
|
self.document = {};
|
||||||
|
await olm.init();
|
||||||
|
delete self.document;
|
||||||
|
delete self.window;
|
||||||
|
this._olm = olm;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_megolmDecrypt(sessionKey, ciphertext) {
|
||||||
|
return this._toMessage(() => {
|
||||||
|
let session;
|
||||||
|
try {
|
||||||
|
session = new this._olm.InboundGroupSession();
|
||||||
|
session.import_session(sessionKey);
|
||||||
|
// returns object with plaintext and message_index
|
||||||
|
return session.decrypt(ciphertext);
|
||||||
|
} finally {
|
||||||
|
session?.free();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleMessage(message) {
|
||||||
|
switch (message.type) {
|
||||||
|
case "load_olm":
|
||||||
|
this._sendReply(message, await this._loadOlm(message.path));
|
||||||
|
break;
|
||||||
|
case "megolm_decrypt":
|
||||||
|
this._sendReply(message, this._megolmDecrypt(message.sessionKey, message.ciphertext));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener("message", new MessageHandler());
|
Reference in a new issue