diff options
Diffstat (limited to 'ext/bg/js/api.js')
| -rw-r--r-- | ext/bg/js/api.js | 174 | 
1 files changed, 167 insertions, 7 deletions
| diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index f768e6f9..93d9c155 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -144,24 +144,46 @@ async function apiTemplateRender(template, data, dynamic) {      }  } -async function apiCommandExec(command) { +async function apiCommandExec(command, params) {      const handlers = apiCommandExec.handlers;      if (handlers.hasOwnProperty(command)) {          const handler = handlers[command]; -        handler(); +        handler(params);      }  }  apiCommandExec.handlers = { -    search: () => { -        chrome.tabs.create({url: chrome.extension.getURL('/bg/search.html')}); +    search: async (params) => { +        const url = chrome.extension.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: () => { -        chrome.runtime.openOptionsPage(); +    options: (params) => { +        if (!(params && params.newTab)) { +            chrome.runtime.openOptionsPage(); +        } else { +            const manifest = chrome.runtime.getManifest(); +            const url = chrome.extension.getURL(manifest.options_ui.page); +            chrome.tabs.create({url}); +        }      },      toggle: async () => { @@ -176,7 +198,7 @@ apiCommandExec.handlers = {  };  async function apiAudioGetUrl(definition, source, optionsContext) { -    return audioBuildUrl(definition, source, optionsContext); +    return audioGetUrl(definition, source, optionsContext);  }  async function apiInjectScreenshot(definition, fields, screenshot) { @@ -241,3 +263,141 @@ function apiFrameInformationGet(sender) {      const frameId = sender.frameId;      return Promise.resolve({frameId});  } + +function apiInjectStylesheet(css, sender) { +    if (!sender.tab) { +        return Promise.reject(new Error('Invalid tab')); +    } + +    const tabId = sender.tab.id; +    const frameId = sender.frameId; +    const details = { +        code: css, +        runAt: 'document_start', +        cssOrigin: 'user', +        allFrames: false +    }; +    if (typeof frameId === 'number') { +        details.frameId = frameId; +    } + +    return new Promise((resolve, reject) => { +        chrome.tabs.insertCSS(tabId, details, () => { +            const e = chrome.runtime.lastError; +            if (e) { +                reject(new Error(e.message)); +            } else { +                resolve(); +            } +        }); +    }); +} + +async function apiGetEnvironmentInfo() { +    const browser = await apiGetBrowser(); +    const platform = await new Promise((resolve) => chrome.runtime.getPlatformInfo(resolve)); +    return { +        browser, +        platform: { +            os: platform.os +        } +    }; +} + +async function apiGetBrowser() { +    if (EXTENSION_IS_BROWSER_EDGE) { +        return 'edge'; +    } +    if (typeof browser !== 'undefined') { +        try { +            const info = await browser.runtime.getBrowserInfo(); +            if (info.name === 'Fennec') { +                return 'firefox-mobile'; +            } +        } catch (e) { } +        return 'firefox'; +    } else { +        return 'chrome'; +    } +} + +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. +    } +} |