UNPKG

webext-active-tab

Version:

WebExtension module: Track `activeTab` permission; automatically inject content scripts

66 lines (65 loc) 2.68 kB
import { isScriptableUrl } from 'webext-content-scripts'; import { isBackground } from 'webext-detect'; import SimpleEventTarget from 'simple-event-target'; if (!isBackground()) { throw new Error('This module is only allowed in a background script'); } const newActiveTabs = new SimpleEventTarget(); const browserAction = chrome.action ?? chrome.browserAction; // TODO: use webext-storage export const possiblyActiveTabs = new Map(); async function addIfScriptable({ url, id }) { if (id && url // Skip if it already exists. A previous change of origin already cleared this && !possiblyActiveTabs.has(id) // ActiveTab doesn't make sense on non-scriptable URLs as they generally don't have scriptable frames && isScriptableUrl(url) // Note: Do not filter by `isContentScriptRegistered`; `active-tab` also applies to random `executeScript` calls ) { const { origin } = new URL(url); console.debug('activeTab:', id, 'added', { origin }); possiblyActiveTabs.set(id, origin); newActiveTabs.emit({ id, origin }); } else { console.debug('activeTab:', id, 'not added', { origin }); } } function dropIfOriginChanged(tabId, { url }) { if (url && possiblyActiveTabs.has(tabId)) { const { origin } = new URL(url); if (possiblyActiveTabs.get(tabId) !== origin) { console.debug('activeTab:', tabId, 'removed because origin changed from', possiblyActiveTabs.get(tabId), 'to', origin); possiblyActiveTabs.delete(tabId); } } } function altListener(_, tab) { if (tab) { void addIfScriptable(tab); } } function drop(tabId) { console.debug('activeTab:', tabId, 'removed'); possiblyActiveTabs.delete(tabId); } // https://developer.chrome.com/docs/extensions/mv3/manifest/activeTab/#invoking-activeTab export function startActiveTabTracking() { browserAction?.onClicked.addListener(addIfScriptable); chrome.contextMenus?.onClicked.addListener(altListener); chrome.commands?.onCommand.addListener(altListener); chrome.tabs.onUpdated.addListener(dropIfOriginChanged); chrome.tabs.onRemoved.addListener(drop); } export function stopActiveTabTracking() { browserAction?.onClicked.removeListener(addIfScriptable); chrome.contextMenus?.onClicked.removeListener(altListener); chrome.commands?.onCommand.removeListener(altListener); chrome.tabs.onUpdated.removeListener(dropIfOriginChanged); chrome.tabs.onRemoved.removeListener(drop); possiblyActiveTabs.clear(); } export function addActiveTabListener(callback) { startActiveTabTracking(); newActiveTabs.listen(callback); }