Api invoke with progress (#483)
* Create an internal API function to open a port * Create system for running actions over a special port * Don't assign in expression
This commit is contained in:
parent
6c341a13d8
commit
5a61c311ad
@ -116,8 +116,10 @@ class Backend {
|
||||
['purgeDatabase', {handler: this._onApiPurgeDatabase.bind(this), async: true}],
|
||||
['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}],
|
||||
['log', {handler: this._onApiLog.bind(this), async: false}],
|
||||
['logIndicatorClear', {handler: this._onApiLogIndicatorClear.bind(this), async: false}]
|
||||
['logIndicatorClear', {handler: this._onApiLogIndicatorClear.bind(this), async: false}],
|
||||
['createActionPort', {handler: this._onApiCreateActionPort.bind(this), async: false}]
|
||||
]);
|
||||
this._messageHandlersWithProgress = new Map();
|
||||
|
||||
this._commandHandlers = new Map([
|
||||
['search', this._onCommandSearch.bind(this)],
|
||||
@ -787,8 +789,79 @@ class Backend {
|
||||
this._updateBadge();
|
||||
}
|
||||
|
||||
_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 = yomichan.generateId(16);
|
||||
const portName = `action-port-${id}`;
|
||||
|
||||
const port = chrome.tabs.connect(tabId, {name: portName, frameId});
|
||||
try {
|
||||
this._createActionListenerPort(port, sender, this._messageHandlersWithProgress);
|
||||
} catch (e) {
|
||||
port.disconnect();
|
||||
throw e;
|
||||
}
|
||||
|
||||
return portName;
|
||||
}
|
||||
|
||||
// Command handlers
|
||||
|
||||
_createActionListenerPort(port, sender, handlers) {
|
||||
let hasStarted = false;
|
||||
|
||||
const onProgress = (data) => {
|
||||
try {
|
||||
if (port === null) { return; }
|
||||
port.postMessage({type: 'progress', data});
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
};
|
||||
|
||||
const onMessage = async ({action, params}) => {
|
||||
if (hasStarted) { return; }
|
||||
hasStarted = true;
|
||||
port.onMessage.removeListener(onMessage);
|
||||
|
||||
try {
|
||||
port.postMessage({type: 'ack'});
|
||||
|
||||
const messageHandler = handlers.get(action);
|
||||
if (typeof messageHandler === 'undefined') {
|
||||
throw new Error('Invalid action');
|
||||
}
|
||||
const {handler, async} = messageHandler;
|
||||
|
||||
const promiseOrResult = handler(params, sender, onProgress);
|
||||
const result = async ? await promiseOrResult : promiseOrResult;
|
||||
port.postMessage({type: 'complete', data: result});
|
||||
} catch (e) {
|
||||
if (port !== null) {
|
||||
port.postMessage({type: 'error', data: e});
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
if (port === null) { return; }
|
||||
if (!hasStarted) {
|
||||
port.onMessage.removeListener(onMessage);
|
||||
}
|
||||
port.onDisconnect.removeListener(cleanup);
|
||||
port = null;
|
||||
handlers = null;
|
||||
};
|
||||
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(cleanup);
|
||||
}
|
||||
|
||||
_getErrorLevelValue(errorLevel) {
|
||||
switch (errorLevel) {
|
||||
case 'info': return 0;
|
||||
|
@ -152,6 +152,124 @@ function apiLogIndicatorClear() {
|
||||
return _apiInvoke('logIndicatorClear');
|
||||
}
|
||||
|
||||
function _apiCreateActionPort(timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = null;
|
||||
let portNameResolve;
|
||||
let portNameReject;
|
||||
const portNamePromise = new Promise((resolve2, reject2) => {
|
||||
portNameResolve = resolve2;
|
||||
portNameReject = reject2;
|
||||
});
|
||||
|
||||
const onConnect = async (port) => {
|
||||
try {
|
||||
const portName = await portNamePromise;
|
||||
if (port.name !== portName || timer === null) { return; }
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
resolve(port);
|
||||
};
|
||||
|
||||
const onError = (e) => {
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
chrome.runtime.onConnect.removeListener(onConnect);
|
||||
portNameReject(e);
|
||||
reject(e);
|
||||
};
|
||||
|
||||
timer = setTimeout(() => onError(new Error('Timeout')), timeout);
|
||||
|
||||
chrome.runtime.onConnect.addListener(onConnect);
|
||||
_apiInvoke('createActionPort').then(portNameResolve, onError);
|
||||
});
|
||||
}
|
||||
|
||||
function _apiInvokeWithProgress(action, params, onProgress, timeout=5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = null;
|
||||
let port = null;
|
||||
|
||||
if (typeof onProgress !== 'function') {
|
||||
onProgress = () => {};
|
||||
}
|
||||
|
||||
const onMessage = (message) => {
|
||||
switch (message.type) {
|
||||
case 'ack':
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
break;
|
||||
case 'progress':
|
||||
try {
|
||||
onProgress(message.data);
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
break;
|
||||
case 'complete':
|
||||
cleanup();
|
||||
resolve(message.data);
|
||||
break;
|
||||
case 'error':
|
||||
cleanup();
|
||||
reject(jsonToError(message.data));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const onDisconnect = () => {
|
||||
cleanup();
|
||||
reject(new Error('Disconnected'));
|
||||
};
|
||||
|
||||
const cleanup = () => {
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
if (port !== null) {
|
||||
port.onMessage.removeListener(onMessage);
|
||||
port.onDisconnect.removeListener(onDisconnect);
|
||||
port.disconnect();
|
||||
port = null;
|
||||
}
|
||||
onProgress = null;
|
||||
};
|
||||
|
||||
timer = setTimeout(() => {
|
||||
cleanup();
|
||||
reject(new Error('Timeout'));
|
||||
}, timeout);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
port = await _apiCreateActionPort(timeout);
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(onDisconnect);
|
||||
port.postMessage({action, params});
|
||||
} catch (e) {
|
||||
cleanup();
|
||||
reject(e);
|
||||
} finally {
|
||||
action = null;
|
||||
params = null;
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
function _apiInvoke(action, params={}) {
|
||||
const data = {action, params};
|
||||
return new Promise((resolve, reject) => {
|
||||
|
Loading…
Reference in New Issue
Block a user