webext-active-tab
Version:
WebExtension module: Track `activeTab` permission; automatically inject content scripts
46 lines (45 loc) • 1.83 kB
JavaScript
import { injectContentScript } from 'webext-content-scripts';
import { onActiveTab, possiblyActiveTabs } from './on-active-tab.js';
const gotNavigation = typeof chrome === 'object' && 'webNavigation' in chrome;
const scripts = [];
async function injectAllFrames({ id: tabId, origin: url }) {
if (tabId === undefined) {
return;
}
if (!gotNavigation) {
// Without it, we only inject it into the top frame
return injectOneFrame({ frameId: 0, url, tabId });
}
const frames = await chrome.webNavigation.getAllFrames({ tabId });
// .map() needed for async loop
void frames?.map(async ({ frameId, url }) => injectOneFrame({ frameId, url, tabId }));
}
async function injectOneFrame({ tabId, frameId, url }) {
const { origin } = new URL(url);
const activeOrigin = await possiblyActiveTabs.get(String(tabId));
// Check origin because the request might be for a frame; cross-origin frames do not receive activeTab
if (activeOrigin === origin) {
console.debug('activeTab: will inject', { tabId, frameId, url });
await injectContentScript({ tabId, frameId }, scripts);
}
else {
console.debug('activeTab: won’t inject', { tabId, frameId, url }, { activeTab: activeOrigin ?? 'no' });
}
}
async function tabListener(tabId, { status }, { url }) {
// Only status updates are relevant
// No URL = no permission
if (status === 'loading' && url) {
await injectOneFrame({ tabId, url, frameId: 0 });
}
}
export default function registerActiveTabScripts(...newScripts) {
scripts.push(...newScripts);
onActiveTab.addListener(injectAllFrames);
if (gotNavigation) {
chrome.webNavigation.onCommitted.addListener(injectOneFrame);
}
else {
chrome.tabs.onUpdated.addListener(tabListener);
}
}