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}],
|
['purgeDatabase', {handler: this._onApiPurgeDatabase.bind(this), async: true}],
|
||||||
['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}],
|
['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}],
|
||||||
['log', {handler: this._onApiLog.bind(this), async: false}],
|
['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([
|
this._commandHandlers = new Map([
|
||||||
['search', this._onCommandSearch.bind(this)],
|
['search', this._onCommandSearch.bind(this)],
|
||||||
@ -787,8 +789,79 @@ class Backend {
|
|||||||
this._updateBadge();
|
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
|
// 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) {
|
_getErrorLevelValue(errorLevel) {
|
||||||
switch (errorLevel) {
|
switch (errorLevel) {
|
||||||
case 'info': return 0;
|
case 'info': return 0;
|
||||||
|
@ -152,6 +152,124 @@ function apiLogIndicatorClear() {
|
|||||||
return _apiInvoke('logIndicatorClear');
|
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={}) {
|
function _apiInvoke(action, params={}) {
|
||||||
const data = {action, params};
|
const data = {action, params};
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user