Skip to content

Commit

Permalink
Remove _invokeWithProgress
Browse files Browse the repository at this point in the history
  • Loading branch information
toasted-nutbread committed Dec 19, 2023
1 parent ae4ff04 commit 322c106
Show file tree
Hide file tree
Showing 5 changed files with 3 additions and 342 deletions.
151 changes: 2 additions & 149 deletions ext/js/background/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ import {AnkiConnect} from '../comm/anki-connect.js';
import {ClipboardMonitor} from '../comm/clipboard-monitor.js';
import {ClipboardReader} from '../comm/clipboard-reader.js';
import {Mecab} from '../comm/mecab.js';
import {clone, deferPromise, generateId, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js';
import {clone, deferPromise, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js';
import {ExtensionError} from '../core/extension-error.js';
import {parseJson, readResponseJson} from '../core/json.js';
import {readResponseJson} from '../core/json.js';
import {AnkiUtil} from '../data/anki-util.js';
import {OptionsUtil} from '../data/options-util.js';
import {PermissionsUtil} from '../data/permissions-util.js';
Expand All @@ -36,7 +36,6 @@ import {JapaneseUtil} from '../language/sandbox/japanese-util.js';
import {Translator} from '../language/translator.js';
import {AudioDownloader} from '../media/audio-downloader.js';
import {MediaUtil} from '../media/media-util.js';
import {yomitan} from '../yomitan.js';
import {ClipboardReaderProxy, DictionaryDatabaseProxy, OffscreenProxy, TranslatorProxy} from './offscreen-proxy.js';
import {ProfileConditionsUtil} from './profile-conditions-util.js';
import {RequestBuilder} from './request-builder.js';
Expand Down Expand Up @@ -181,7 +180,6 @@ export class Backend {
['getMedia', this._onApiGetMedia.bind(this)],
['log', this._onApiLog.bind(this)],
['logIndicatorClear', this._onApiLogIndicatorClear.bind(this)],
['createActionPort', this._onApiCreateActionPort.bind(this)],
['modifySettings', this._onApiModifySettings.bind(this)],
['getSettings', this._onApiGetSettings.bind(this)],
['setAllSettings', this._onApiSetAllSettings.bind(this)],
Expand All @@ -196,10 +194,6 @@ export class Backend {
['openCrossFramePort', this._onApiOpenCrossFramePort.bind(this)]
]));
/* eslint-enable no-multi-spaces */
/** @type {import('backend').MessageHandlerWithProgressMap} */
this._messageHandlersWithProgress = new Map(/** @type {import('backend').MessageHandlerWithProgressMapInit} */ ([
// Empty
]));

/** @type {Map<string, (params?: import('core').SerializableObject) => void>} */
this._commandHandlers = new Map(/** @type {[name: string, handler: (params?: import('core').SerializableObject) => void][]} */ ([
Expand Down Expand Up @@ -748,31 +742,6 @@ export class Backend {
this._updateBadge();
}

/** @type {import('api').Handler<import('api').CreateActionPortDetails, import('api').CreateActionPortResult, true>} */
_onApiCreateActionPort(_params, sender) {
if (!sender || !sender.tab) { throw new Error('Invalid sender'); }
const tabId = sender.tab.id;
if (typeof tabId !== 'number') { throw new Error('Sender has invalid tab ID'); }

const frameId = sender.frameId;
const id = generateId(16);
/** @type {import('cross-frame-api').ActionPortDetails} */
const details = {
name: 'action-port',
id
};

const port = chrome.tabs.connect(tabId, {name: JSON.stringify(details), frameId});
try {
this._createActionListenerPort(port, sender, this._messageHandlersWithProgress);
} catch (e) {
port.disconnect();
throw e;
}

return details;
}

/** @type {import('api').Handler<import('api').ModifySettingsDetails, import('api').ModifySettingsResult>} */
_onApiModifySettings({targets, source}) {
return this._modifySettings(targets, source);
Expand Down Expand Up @@ -1485,107 +1454,6 @@ export class Backend {
return results;
}

/**
* @param {chrome.runtime.Port} port
* @param {chrome.runtime.MessageSender} sender
* @param {import('backend').MessageHandlerWithProgressMap} handlers
*/
_createActionListenerPort(port, sender, handlers) {
let done = false;
let hasStarted = false;
/** @type {?string} */
let messageString = '';

/**
* @param {...unknown} data
*/
const onProgress = (...data) => {
try {
if (done) { return; }
port.postMessage(/** @type {import('backend').InvokeWithProgressResponseProgressMessage} */ ({type: 'progress', data}));
} catch (e) {
// NOP
}
};

/**
* @param {import('backend').InvokeWithProgressRequestMessage} message
*/
const onMessage = (message) => {
if (hasStarted) { return; }

try {
const {action} = message;
switch (action) {
case 'fragment':
messageString += message.data;
break;
case 'invoke':
if (messageString !== null) {
hasStarted = true;
port.onMessage.removeListener(onMessage);

/** @type {{action: string, params?: import('core').SerializableObject}} */
const messageData = parseJson(messageString);
messageString = null;
onMessageComplete(messageData);
}
break;
}
} catch (e) {
cleanup(e);
}
};

/**
* @param {{action: string, params?: import('core').SerializableObject}} message
*/
const onMessageComplete = async (message) => {
try {
const {action, params} = message;
port.postMessage(/** @type {import('backend').InvokeWithProgressResponseAcknowledgeMessage} */ ({type: 'ack'}));

const messageHandler = handlers.get(action);
if (typeof messageHandler === 'undefined') {
throw new Error('Invalid action');
}
const {handler, async, contentScript} = messageHandler;

if (!contentScript) {
this._validatePrivilegedMessageSender(sender);
}

const promiseOrResult = handler(params, sender, onProgress);
const result = async ? await promiseOrResult : promiseOrResult;
port.postMessage(/** @type {import('backend').InvokeWithProgressResponseCompleteMessage} */ ({type: 'complete', data: result}));
} catch (e) {
cleanup(e);
}
};

const onDisconnect = () => {
cleanup(null);
};

/**
* @param {unknown} error
*/
const cleanup = (error) => {
if (done) { return; }
if (error !== null) {
port.postMessage(/** @type {import('backend').InvokeWithProgressResponseErrorMessage} */ ({type: 'error', data: ExtensionError.serialize(error)}));
}
if (!hasStarted) {
port.onMessage.removeListener(onMessage);
}
port.onDisconnect.removeListener(onDisconnect);
done = true;
};

port.onMessage.addListener(onMessage);
port.onDisconnect.addListener(onDisconnect);
}

/**
* @param {?import('log').LogLevel} errorLevel
* @returns {number}
Expand Down Expand Up @@ -1694,21 +1562,6 @@ export class Backend {
}
}

/**
* @param {chrome.runtime.MessageSender} sender
* @throws {Error}
*/
_validatePrivilegedMessageSender(sender) {
let {url} = sender;
if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; }
const {tab} = sender;
if (typeof tab === 'object' && tab !== null) {
({url} = tab);
if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; }
}
throw new Error('Invalid message sender');
}

/**
* @returns {Promise<string>}
*/
Expand Down
129 changes: 0 additions & 129 deletions ext/js/comm/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import {deferPromise} from '../core.js';
import {ExtensionError} from '../core/extension-error.js';
import {parseJson} from '../core/json.js';

export class API {
/**
Expand Down Expand Up @@ -426,133 +424,6 @@ export class API {

// Utilities

/**
* @param {number} timeout
* @returns {Promise<chrome.runtime.Port>}
*/
_createActionPort(timeout) {
return new Promise((resolve, reject) => {
/** @type {?import('core').Timeout} */
let timer = null;
/** @type {import('core').DeferredPromiseDetails<import('api').CreateActionPortResult>} */
const portDetails = deferPromise();

/**
* @param {chrome.runtime.Port} port
*/
const onConnect = async (port) => {
try {
const {name: expectedName, id: expectedId} = await portDetails.promise;
/** @type {import('cross-frame-api').PortDetails} */
const portDetails2 = parseJson(port.name);
if (portDetails2.name !== expectedName || portDetails2.id !== expectedId || timer === null) { return; }
} catch (e) {
return;
}

clearTimeout(timer);
timer = null;

chrome.runtime.onConnect.removeListener(onConnect);
resolve(port);
};

/**
* @param {Error} e
*/
const onError = (e) => {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
chrome.runtime.onConnect.removeListener(onConnect);
portDetails.reject(e);
reject(e);
};

timer = setTimeout(() => onError(new Error('Timeout')), timeout);

chrome.runtime.onConnect.addListener(onConnect);
/** @type {Promise<import('api').CreateActionPortResult>} */
const createActionPortResult = this._invoke('createActionPort');
createActionPortResult.then(portDetails.resolve, onError);
});
}

/**
* @template [TReturn=unknown]
* @param {string} action
* @param {import('core').SerializableObject} params
* @param {?(...args: unknown[]) => void} onProgress0
* @param {number} [timeout]
* @returns {Promise<TReturn>}
*/
_invokeWithProgress(action, params, onProgress0, timeout = 5000) {
return new Promise((resolve, reject) => {
/** @type {?chrome.runtime.Port} */
let port = null;

const onProgress = typeof onProgress0 === 'function' ? onProgress0 : () => {};

/**
* @param {import('backend').InvokeWithProgressResponseMessage<TReturn>} message
*/
const onMessage = (message) => {
switch (message.type) {
case 'progress':
try {
onProgress(...message.data);
} catch (e) {
// NOP
}
break;
case 'complete':
cleanup();
resolve(message.data);
break;
case 'error':
cleanup();
reject(ExtensionError.deserialize(message.data));
break;
}
};

const onDisconnect = () => {
cleanup();
reject(new Error('Disconnected'));
};

const cleanup = () => {
if (port !== null) {
port.onMessage.removeListener(onMessage);
port.onDisconnect.removeListener(onDisconnect);
port.disconnect();
port = null;
}
};

(async () => {
try {
port = await this._createActionPort(timeout);
port.onMessage.addListener(onMessage);
port.onDisconnect.addListener(onDisconnect);

// Chrome has a maximum message size that can be sent, so longer messages must be fragmented.
const messageString = JSON.stringify({action, params});
const fragmentSize = 1e7; // 10 MB
for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) {
const data = messageString.substring(i, i + fragmentSize);
port.postMessage(/** @type {import('backend').InvokeWithProgressRequestFragmentMessage} */ ({action: 'fragment', data}));
}
port.postMessage(/** @type {import('backend').InvokeWithProgressRequestInvokeMessage} */ ({action: 'invoke'}));
} catch (e) {
cleanup();
reject(e);
}
})();
});
}

/**
* @template [TReturn=unknown]
* @param {string} action
Expand Down
9 changes: 0 additions & 9 deletions types/ext/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,3 @@ export type OpenCrossFramePortResult = {
export type RequestBackendReadySignalDetails = Record<string, never>;

export type RequestBackendReadySignalResult = boolean;

// createActionPort

export type CreateActionPortDetails = Record<string, never>;

export type CreateActionPortResult = {
name: 'action-port';
id: string;
};
Loading

0 comments on commit 322c106

Please sign in to comment.