webext-active-tab
Version:
WebExtension module: Track `activeTab` permission; automatically inject content scripts
48 lines (47 loc) • 1.99 kB
JavaScript
import { injectContentScript } from 'webext-content-scripts';
import { addActiveTabListener, possiblyActiveTabs } from './tracker.js';
import { isContentScriptRegistered } from './utils.js';
const gotNavigation = typeof chrome === 'object' && 'webNavigation' in chrome;
const scripts = chrome.runtime.getManifest().content_scripts;
async function injectToTabUnlessRegistered({ id: tabId, origin }) {
if (tabId === undefined) {
return;
}
const frames = gotNavigation
// Only with `webNavigation` we can inject into the frames
? await chrome.webNavigation.getAllFrames({ tabId })
// Without it, we only inject it into the top frame
: [{ frameId: 0, url: origin }];
// .map() needed for async loop
void frames?.map(async ({ frameId, url }) => injectIfActive({ frameId, url, tabId }));
}
async function injectIfActive({ tabId, frameId, url }) {
const { origin } = new URL(url);
if (
// Check origin because the request might be for a frame; cross-origin frames do not receive activeTab
possiblyActiveTabs.get(tabId) === origin
// Don't inject if already registered
&& !(await isContentScriptRegistered(url))) {
console.debug('activeTab: will inject', { tabId, frameId, url });
await injectContentScript({ tabId, frameId }, scripts);
}
else {
console.debug('activeTab: won’t inject', { tabId, frameId, url }, { activeTab: possiblyActiveTabs.get(tabId) ?? 'no' });
}
}
async function tabListener(tabId, { status }, { url }) {
// Only status updates are relevant
// No URL = no permission
if (status === 'loading' && url) {
await injectIfActive({ tabId, url, frameId: 0 });
}
}
export default function register() {
addActiveTabListener(injectToTabUnlessRegistered);
if (gotNavigation) {
chrome.webNavigation.onCommitted.addListener(injectIfActive);
}
else {
chrome.tabs.onUpdated.addListener(tabListener);
}
}