forked from mystiq/hydrogen-web
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,
|
||||
// so do it first
|
||||
const themeAssets = await copyThemeAssets(themes, legacy);
|
||||
const jsBundlePath = await buildJs();
|
||||
const jsLegacyBundlePath = await buildJsLegacy();
|
||||
const jsBundlePath = await buildJs("src/main.js", `${PROJECT_ID}.js`);
|
||||
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 assetPaths = createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, themeAssets);
|
||||
const assetPaths = createAssetPaths(jsBundlePath, jsLegacyBundlePath, jsWorkerPath, cssBundlePaths, themeAssets);
|
||||
|
||||
let manifestPath;
|
||||
if (offline) {
|
||||
|
@ -98,7 +99,7 @@ async function build() {
|
|||
console.log(`built ${PROJECT_ID} ${version} successfully`);
|
||||
}
|
||||
|
||||
function createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, themeAssets) {
|
||||
function createAssetPaths(jsBundlePath, jsLegacyBundlePath, jsWorkerPath, cssBundlePaths, themeAssets) {
|
||||
function trim(path) {
|
||||
if (!path.startsWith(targetDir)) {
|
||||
throw new Error("invalid target path: " + targetDir);
|
||||
|
@ -108,6 +109,7 @@ function createAssetPaths(jsBundlePath, jsLegacyBundlePath, cssBundlePaths, them
|
|||
return {
|
||||
jsBundle: () => trim(jsBundlePath),
|
||||
jsLegacyBundle: () => trim(jsLegacyBundlePath),
|
||||
jsWorker: () => trim(jsWorkerPath),
|
||||
cssMainBundle: () => trim(cssBundlePaths.main),
|
||||
cssThemeBundle: themeName => trim(cssBundlePaths.themes[themeName]),
|
||||
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");
|
||||
}
|
||||
|
||||
async function buildJs() {
|
||||
async function buildJs(inputFile, outputName) {
|
||||
// create js bundle
|
||||
const bundle = await rollup({
|
||||
input: 'src/main.js',
|
||||
input: inputFile,
|
||||
plugins: [removeJsComments({comments: "none"})]
|
||||
});
|
||||
const {output} = await bundle.generate({
|
||||
format: 'es',
|
||||
// TODO: can remove this?
|
||||
name: `${PROJECT_ID}Bundle`
|
||||
});
|
||||
const code = output[0].code;
|
||||
const bundlePath = resource(`${PROJECT_ID}.js`, code);
|
||||
const bundlePath = resource(outputName, code);
|
||||
await fs.writeFile(bundlePath, code, "utf8");
|
||||
return bundlePath;
|
||||
}
|
||||
|
||||
async function buildJsLegacy() {
|
||||
async function buildJsLegacy(inputFile, outputName) {
|
||||
// compile down to whatever IE 11 needs
|
||||
const babelPlugin = babel.babel({
|
||||
babelHelpers: 'bundled',
|
||||
|
@ -214,7 +217,7 @@ async function buildJsLegacy() {
|
|||
});
|
||||
// create js bundle
|
||||
const rollupConfig = {
|
||||
input: ['src/legacy-polyfill.js', 'src/main.js'],
|
||||
input: ['src/legacy-polyfill.js', inputFile],
|
||||
plugins: [multi(), commonjs(), nodeResolve(), babelPlugin, removeJsComments({comments: "none"})]
|
||||
};
|
||||
const bundle = await rollup(rollupConfig);
|
||||
|
@ -223,7 +226,39 @@ async function buildJsLegacy() {
|
|||
name: `${PROJECT_ID}Bundle`
|
||||
});
|
||||
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");
|
||||
return bundlePath;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export class TimelineViewModel extends ViewModel {
|
|||
if (firstTile.shape === "gap") {
|
||||
return firstTile.fill();
|
||||
} else {
|
||||
await this._timeline.loadAtTop(50);
|
||||
await this._timeline.loadAtTop(10);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import {SessionInfo} from "./decryption/SessionInfo.js";
|
|||
import {DecryptionPreparation} from "./decryption/DecryptionPreparation.js";
|
||||
import {SessionDecryption} from "./decryption/SessionDecryption.js";
|
||||
import {SessionCache} from "./decryption/SessionCache.js";
|
||||
import {DecryptionWorker} from "./decryption/DecryptionWorker.js";
|
||||
|
||||
function getSenderKey(event) {
|
||||
return event.content?.["sender_key"];
|
||||
|
@ -38,7 +39,9 @@ export class Decryption {
|
|||
constructor({pickleKey, olm}) {
|
||||
this._pickleKey = pickleKey;
|
||||
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) {
|
||||
|
@ -55,6 +58,7 @@ export class Decryption {
|
|||
* @return {DecryptionPreparation}
|
||||
*/
|
||||
async prepareDecryptAll(roomId, events, sessionCache, txn) {
|
||||
await this._initPromise;
|
||||
const errors = new Map();
|
||||
const validEvents = [];
|
||||
|
||||
|
@ -85,7 +89,7 @@ export class Decryption {
|
|||
errors.set(event.event_id, new DecryptionError("MEGOLM_NO_SESSION", event));
|
||||
}
|
||||
} 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
|
||||
*/
|
||||
export class SessionDecryption {
|
||||
constructor(sessionInfo, events) {
|
||||
constructor(sessionInfo, events, decryptor) {
|
||||
sessionInfo.retain();
|
||||
this._sessionInfo = sessionInfo;
|
||||
this._events = events;
|
||||
this._decryptor = decryptor;
|
||||
}
|
||||
|
||||
async decryptAll() {
|
||||
|
@ -38,7 +39,7 @@ export class SessionDecryption {
|
|||
try {
|
||||
const {session} = this._sessionInfo;
|
||||
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;
|
||||
try {
|
||||
payload = JSON.parse(plaintext);
|
||||
|
@ -63,12 +64,6 @@ export class SessionDecryption {
|
|||
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() {
|
||||
this._sessionInfo.release();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ export class Timeline {
|
|||
|
||||
/** @package */
|
||||
async load() {
|
||||
const entries = await this._timelineReader.readFromEnd(50);
|
||||
const entries = await this._timelineReader.readFromEnd(25);
|
||||
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());
|
Loading…
Reference in a new issue