Move apiCommandExec implementation into Backend

This commit is contained in:
toasted-nutbread 2019-12-09 21:41:24 -05:00
parent f07207c9bf
commit f63220b6c5
2 changed files with 143 additions and 134 deletions

View File

@ -65,58 +65,9 @@ function apiTemplateRender(template, data, dynamic) {
return utilBackend()._onApiTemplateRender({template, data, dynamic});
}
async function apiCommandExec(command, params) {
const handlers = apiCommandExec.handlers;
if (hasOwn(handlers, command)) {
const handler = handlers[command];
handler(params);
function apiCommandExec(command, params) {
return utilBackend()._onApiCommandExec({command, params});
}
}
apiCommandExec.handlers = {
search: async (params) => {
const url = chrome.runtime.getURL('/bg/search.html');
if (!(params && params.newTab)) {
try {
const tab = await _apiFindTab(1000, (url2) => (
url2 !== null &&
url2.startsWith(url) &&
(url2.length === url.length || url2[url.length] === '?' || url2[url.length] === '#')
));
if (tab !== null) {
await _apiFocusTab(tab);
return;
}
} catch (e) {
// NOP
}
}
chrome.tabs.create({url});
},
help: () => {
chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'});
},
options: (params) => {
if (!(params && params.newTab)) {
chrome.runtime.openOptionsPage();
} else {
const manifest = chrome.runtime.getManifest();
const url = chrome.runtime.getURL(manifest.options_ui.page);
chrome.tabs.create({url});
}
},
toggle: async () => {
const optionsContext = {
depth: 0,
url: window.location.href
};
const options = await apiOptionsGet(optionsContext);
options.general.enable = !options.general.enable;
await apiOptionsSave('popup');
}
};
async function apiAudioGetUrl(definition, source, optionsContext) {
return audioGetUrl(definition, source, optionsContext);
@ -208,87 +159,6 @@ async function _apiGetBrowser() {
}
}
function _apiGetTabUrl(tab) {
return new Promise((resolve) => {
chrome.tabs.sendMessage(tab.id, {action: 'getUrl'}, {frameId: 0}, (response) => {
let url = null;
if (!chrome.runtime.lastError) {
url = (response !== null && typeof response === 'object' && !Array.isArray(response) ? response.url : null);
if (url !== null && typeof url !== 'string') {
url = null;
}
}
resolve({tab, url});
});
});
}
async function _apiFindTab(timeout, checkUrl) {
// This function works around the need to have the "tabs" permission to access tab.url.
const tabs = await new Promise((resolve) => chrome.tabs.query({}, resolve));
let matchPromiseResolve = null;
const matchPromise = new Promise((resolve) => { matchPromiseResolve = resolve; });
const checkTabUrl = ({tab, url}) => {
if (checkUrl(url, tab)) {
matchPromiseResolve(tab);
}
};
const promises = [];
for (const tab of tabs) {
const promise = _apiGetTabUrl(tab);
promise.then(checkTabUrl);
promises.push(promise);
}
const racePromises = [
matchPromise,
Promise.all(promises).then(() => null)
];
if (typeof timeout === 'number') {
racePromises.push(new Promise((resolve) => setTimeout(() => resolve(null), timeout)));
}
return await Promise.race(racePromises);
}
async function _apiFocusTab(tab) {
await new Promise((resolve, reject) => {
chrome.tabs.update(tab.id, {active: true}, () => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(); }
});
});
if (!(typeof chrome.windows === 'object' && chrome.windows !== null)) {
// Windows not supported (e.g. on Firefox mobile)
return;
}
try {
const tabWindow = await new Promise((resolve) => {
chrome.windows.get(tab.windowId, {}, (tabWindow) => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(tabWindow); }
});
});
if (!tabWindow.focused) {
await new Promise((resolve, reject) => {
chrome.windows.update(tab.windowId, {focused: true}, () => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(); }
});
});
}
} catch (e) {
// Edge throws exception for no reason here.
}
}
async function apiClipboardGet() {
const clipboardPasteTarget = utilBackend().clipboardPasteTarget;
clipboardPasteTarget.innerText = '';

View File

@ -397,8 +397,11 @@ class Backend {
);
}
_onApiCommandExec({command, params}) {
return apiCommandExec(command, params);
async _onApiCommandExec({command, params}) {
const handler = Backend._commandHandlers.get(command);
if (typeof handler !== 'function') { return false; }
handler(this, params);
}
_onApiAudioGetUrl({definition, source, optionsContext}) {
@ -429,6 +432,54 @@ class Backend {
return apiClipboardGet();
}
// Command handlers
async _onCommandSearch(params) {
const url = chrome.runtime.getURL('/bg/search.html');
if (!(params && params.newTab)) {
try {
const tab = await Backend._findTab(1000, (url2) => (
url2 !== null &&
url2.startsWith(url) &&
(url2.length === url.length || url2[url.length] === '?' || url2[url.length] === '#')
));
if (tab !== null) {
await Backend._focusTab(tab);
return;
}
} catch (e) {
// NOP
}
}
chrome.tabs.create({url});
}
_onCommandHelp() {
chrome.tabs.create({url: 'https://foosoft.net/projects/yomichan/'});
}
_onCommandOptions(params) {
if (!(params && params.newTab)) {
chrome.runtime.openOptionsPage();
} else {
const manifest = chrome.runtime.getManifest();
const url = chrome.runtime.getURL(manifest.options_ui.page);
chrome.tabs.create({url});
}
}
async _onCommandToggle() {
const optionsContext = {
depth: 0,
url: window.location.href
};
const source = 'popup';
const options = await this.getOptions(optionsContext);
options.general.enable = !options.general.enable;
await this._optionsSave({source});
}
// Utilities
async _injectScreenshot(definition, fields, screenshot) {
@ -466,6 +517,87 @@ class Backend {
definition.screenshotFileName = filename;
}
static _getTabUrl(tab) {
return new Promise((resolve) => {
chrome.tabs.sendMessage(tab.id, {action: 'getUrl'}, {frameId: 0}, (response) => {
let url = null;
if (!chrome.runtime.lastError) {
url = (response !== null && typeof response === 'object' && !Array.isArray(response) ? response.url : null);
if (url !== null && typeof url !== 'string') {
url = null;
}
}
resolve({tab, url});
});
});
}
static async _findTab(timeout, checkUrl) {
// This function works around the need to have the "tabs" permission to access tab.url.
const tabs = await new Promise((resolve) => chrome.tabs.query({}, resolve));
let matchPromiseResolve = null;
const matchPromise = new Promise((resolve) => { matchPromiseResolve = resolve; });
const checkTabUrl = ({tab, url}) => {
if (checkUrl(url, tab)) {
matchPromiseResolve(tab);
}
};
const promises = [];
for (const tab of tabs) {
const promise = Backend._getTabUrl(tab);
promise.then(checkTabUrl);
promises.push(promise);
}
const racePromises = [
matchPromise,
Promise.all(promises).then(() => null)
];
if (typeof timeout === 'number') {
racePromises.push(new Promise((resolve) => setTimeout(() => resolve(null), timeout)));
}
return await Promise.race(racePromises);
}
static async _focusTab(tab) {
await new Promise((resolve, reject) => {
chrome.tabs.update(tab.id, {active: true}, () => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(); }
});
});
if (!(typeof chrome.windows === 'object' && chrome.windows !== null)) {
// Windows not supported (e.g. on Firefox mobile)
return;
}
try {
const tabWindow = await new Promise((resolve) => {
chrome.windows.get(tab.windowId, {}, (tabWindow) => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(tabWindow); }
});
});
if (!tabWindow.focused) {
await new Promise((resolve, reject) => {
chrome.windows.update(tab.windowId, {focused: true}, () => {
const e = chrome.runtime.lastError;
if (e) { reject(e); }
else { resolve(); }
});
});
}
} catch (e) {
// Edge throws exception for no reason here.
}
}
}
Backend._messageHandlers = new Map([
@ -491,5 +623,12 @@ Backend._messageHandlers = new Map([
['clipboardGet', (self, ...args) => self._onApiClipboardGet(...args)]
]);
Backend._commandHandlers = new Map([
['search', (self, ...args) => self._onCommandSearch(...args)],
['help', (self, ...args) => self._onCommandHelp(...args)],
['options', (self, ...args) => self._onCommandOptions(...args)],
['toggle', (self, ...args) => self._onCommandToggle(...args)]
]);
window.yomichan_backend = new Backend();
window.yomichan_backend.prepare();