Something went wrong. Try again.
Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
Something went wrong. Try again.
5.7 kB · 165 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166/* * Copyright (C) 2023-2025 Yomitan Authors * Copyright (C) 2021-2022 Yomichan Authors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */
/** * Injects a stylesheet into a tab. * @param {'file'|'code'} type The type of content to inject; either 'file' or 'code'. * @param {string} content The content to inject. * - If type is `'file'`, this argument should be a path to a file. * - If type is `'code'`, this argument should be the CSS content. * @param {number} tabId The id of the tab to inject into. * @param {number|undefined} frameId The id of the frame to inject into. * @param {boolean} allFrames Whether or not the stylesheet should be injected into all frames. * @returns {Promise<void>} */export function injectStylesheet(type, content, tabId, frameId, allFrames) { return new Promise((resolve, reject) => { /** @type {chrome.scripting.InjectionTarget} */ const target = { tabId, allFrames, }; /** @type {chrome.scripting.CSSInjection} */ const details = ( type === 'file' ? {origin: 'AUTHOR', files: [content], target} : {origin: 'USER', css: content, target} ); if (!allFrames && typeof frameId === 'number') { details.target.frameIds = [frameId]; } chrome.scripting.insertCSS(details, () => { const e = chrome.runtime.lastError; if (e) { reject(new Error(e.message)); } else { resolve(); } }); });}
/** * Checks whether or not a content script is registered. * @param {string} id The identifier used with a call to `registerContentScript`. * @returns {Promise<boolean>} `true` if a script is registered, `false` otherwise. */export async function isContentScriptRegistered(id) { const scripts = await getRegisteredContentScripts([id]); for (const script of scripts) { if (script.id === id) { return true; } } return false;}
/** * Registers a dynamic content script. * Note: if the fallback handler is used and the 'webNavigation' permission isn't granted, * there is a possibility that the script can be injected more than once due to the events used. * Therefore, a reentrant check may need to be performed by the content script. * @param {string} id A unique identifier for the registration. * @param {import('script-manager').RegistrationDetails} details The script registration details. * @throws An error is thrown if the id is already in use. */export async function registerContentScript(id, details) { if (await isContentScriptRegistered(id)) { throw new Error('Registration already exists'); }
const details2 = createContentScriptRegistrationOptions(details, id); await /** @type {Promise<void>} */ (new Promise((resolve, reject) => { chrome.scripting.registerContentScripts([details2], () => { const e = chrome.runtime.lastError; if (e) { reject(new Error(e.message)); } else { resolve(); } }); }));}
/** * Unregisters a previously registered content script. * @param {string} id The identifier passed to a previous call to `registerContentScript`. * @returns {Promise<void>} */export async function unregisterContentScript(id) { return new Promise((resolve, reject) => { chrome.scripting.unregisterContentScripts({ids: [id]}, () => { const e = chrome.runtime.lastError; if (e) { reject(new Error(e.message)); } else { resolve(); } }); });}
/** * @param {import('script-manager').RegistrationDetails} details * @param {string} id * @returns {chrome.scripting.RegisteredContentScript} */function createContentScriptRegistrationOptions(details, id) { const {css, js, allFrames, matches, runAt, world} = details; /** @type {chrome.scripting.RegisteredContentScript} */ const options = { id: id, persistAcrossSessions: true, }; if (Array.isArray(css)) { options.css = [...css]; } if (Array.isArray(js)) { options.js = [...js]; } if (typeof allFrames !== 'undefined') { options.allFrames = allFrames; } if (Array.isArray(matches)) { options.matches = [...matches]; } if (typeof runAt !== 'undefined') { options.runAt = runAt; } if (typeof world !== 'undefined') { options.world = world; } return options;}
/** * @param {string[]} ids * @returns {Promise<chrome.scripting.RegisteredContentScript[]>} */function getRegisteredContentScripts(ids) { return new Promise((resolve, reject) => { chrome.scripting.getRegisteredContentScripts({ids}, (result) => { const e = chrome.runtime.lastError; if (e) { reject(new Error(e.message)); } else { resolve(result); } }); });}