webext-active-tab 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/distribution/on-active-tab.d.ts +3 -2
- package/distribution/on-active-tab.js +49 -24
- package/distribution/register-active-tab-scripts.js +11 -8
- package/package.json +44 -47
- package/distribution/register.d.ts +0 -1
- package/distribution/register.js +0 -2
- package/distribution/scripts.d.ts +0 -1
- package/distribution/scripts.js +0 -47
- package/distribution/tracker.d.ts +0 -11
- package/distribution/tracker.js +0 -65
- package/distribution/utils.d.ts +0 -7
- package/distribution/utils.js +0 -30
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StorageItemMap } from 'webext-storage';
|
|
1
2
|
type TabId = number;
|
|
2
3
|
type Origin = string;
|
|
3
4
|
export type ActiveTab = {
|
|
@@ -5,9 +6,9 @@ export type ActiveTab = {
|
|
|
5
6
|
origin: Origin;
|
|
6
7
|
};
|
|
7
8
|
/**
|
|
8
|
-
|
|
9
|
+
The list is not guaranteed to be up to date. The activeTab permission might be lost before it's detected.
|
|
9
10
|
*/
|
|
10
|
-
export declare const possiblyActiveTabs: Map<
|
|
11
|
+
export declare const possiblyActiveTabs: StorageItemMap<string, string | undefined> | Map<string, string>;
|
|
11
12
|
declare function startActiveTabTracking(): void;
|
|
12
13
|
declare function stopActiveTabTracking(): void;
|
|
13
14
|
export declare const PRIVATE: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isScriptableUrl } from 'webext-content-scripts';
|
|
2
2
|
import { isBackground } from 'webext-detect';
|
|
3
3
|
import SimpleEventTarget from 'simple-event-target';
|
|
4
|
+
import { StorageItemMap } from 'webext-storage';
|
|
4
5
|
if (!isBackground()) {
|
|
5
6
|
throw new Error('This module is only allowed in a background script');
|
|
6
7
|
}
|
|
@@ -9,63 +10,87 @@ if (!chrome.runtime.getManifest().permissions?.includes('activeTab')) {
|
|
|
9
10
|
}
|
|
10
11
|
const activation = new SimpleEventTarget();
|
|
11
12
|
const deactivation = new SimpleEventTarget();
|
|
12
|
-
const browserAction = chrome.action ?? chrome.browserAction;
|
|
13
|
-
// TODO: use https://github.com/fregante/webext-storage/issues/5
|
|
14
13
|
/**
|
|
15
|
-
|
|
14
|
+
The list is not guaranteed to be up to date. The activeTab permission might be lost before it's detected.
|
|
16
15
|
*/
|
|
17
|
-
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions -- Typed as always defined, but `chrome.storage` or `chrome.storage.session` can be missing at runtime
|
|
17
|
+
export const possiblyActiveTabs = chrome.storage?.session
|
|
18
|
+
? new StorageItemMap('webext-active-tab', {
|
|
19
|
+
area: 'session',
|
|
20
|
+
})
|
|
21
|
+
: new Map();
|
|
18
22
|
async function addIfScriptable({ url, id }) {
|
|
19
|
-
if (id && url
|
|
23
|
+
if (id !== undefined && url !== undefined
|
|
20
24
|
// Skip if it already exists. A previous change of origin already cleared this
|
|
21
|
-
&& !possiblyActiveTabs.has(id)
|
|
25
|
+
&& !await possiblyActiveTabs.has(String(id))
|
|
22
26
|
// ActiveTab doesn't make sense on non-scriptable URLs as they generally don't have scriptable frames
|
|
23
27
|
&& isScriptableUrl(url)
|
|
24
28
|
// Note: Do not filter by `isContentScriptRegistered`; `active-tab` also applies to random `executeScript` calls
|
|
25
29
|
) {
|
|
26
30
|
const { origin } = new URL(url);
|
|
27
31
|
console.debug('activeTab:', id, 'added', { origin });
|
|
28
|
-
possiblyActiveTabs.set(id, origin);
|
|
32
|
+
await possiblyActiveTabs.set(String(id), origin);
|
|
29
33
|
activation.emit({ id, origin });
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
32
|
-
console.debug('activeTab:', id, 'not added', {
|
|
36
|
+
console.debug('activeTab:', id, 'not added', { url });
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// Serialized per tab, so overlapping events can't both see the entry and emit twice
|
|
40
|
+
const pendingDrops = new Map();
|
|
41
|
+
// Drops the tab if it's active, unless it's still on `keepOrigin`
|
|
42
|
+
async function drop(tabId, keepOrigin) {
|
|
43
|
+
const previous = pendingDrops.get(tabId);
|
|
44
|
+
const { promise: current, resolve: release } = Promise.withResolvers();
|
|
45
|
+
pendingDrops.set(tabId, current);
|
|
46
|
+
try {
|
|
47
|
+
try {
|
|
48
|
+
await previous;
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
const activeOrigin = await possiblyActiveTabs.get(String(tabId));
|
|
52
|
+
if (activeOrigin !== undefined && activeOrigin !== keepOrigin) {
|
|
53
|
+
console.debug('activeTab:', tabId, 'removed', { activeOrigin, keepOrigin });
|
|
54
|
+
await possiblyActiveTabs.delete(String(tabId));
|
|
55
|
+
deactivation.emit(tabId);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
release();
|
|
60
|
+
if (pendingDrops.get(tabId) === current) {
|
|
61
|
+
pendingDrops.delete(tabId);
|
|
41
62
|
}
|
|
42
63
|
}
|
|
43
64
|
}
|
|
65
|
+
async function dropIfActive(tabId) {
|
|
66
|
+
await drop(tabId);
|
|
67
|
+
}
|
|
68
|
+
async function dropIfOriginChanged(tabId, { url }) {
|
|
69
|
+
if (url === undefined || url === '') {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
await drop(tabId, new URL(url).origin);
|
|
73
|
+
}
|
|
44
74
|
function altListener(_, tab) {
|
|
45
75
|
if (tab) {
|
|
46
76
|
void addIfScriptable(tab);
|
|
47
77
|
}
|
|
48
78
|
}
|
|
49
|
-
function drop(tabId) {
|
|
50
|
-
console.debug('activeTab:', tabId, 'removed');
|
|
51
|
-
possiblyActiveTabs.delete(tabId);
|
|
52
|
-
deactivation.emit(tabId);
|
|
53
|
-
}
|
|
54
79
|
// https://developer.chrome.com/docs/extensions/mv3/manifest/activeTab/#invoking-activeTab
|
|
55
80
|
function startActiveTabTracking() {
|
|
56
|
-
|
|
81
|
+
chrome.action?.onClicked.addListener(addIfScriptable);
|
|
57
82
|
chrome.contextMenus?.onClicked.addListener(altListener);
|
|
58
83
|
chrome.commands?.onCommand.addListener(altListener);
|
|
59
84
|
chrome.tabs.onUpdated.addListener(dropIfOriginChanged);
|
|
60
|
-
chrome.tabs.onRemoved.addListener(
|
|
85
|
+
chrome.tabs.onRemoved.addListener(dropIfActive);
|
|
61
86
|
}
|
|
62
87
|
function stopActiveTabTracking() {
|
|
63
|
-
|
|
88
|
+
chrome.action?.onClicked.removeListener(addIfScriptable);
|
|
64
89
|
chrome.contextMenus?.onClicked.removeListener(altListener);
|
|
65
90
|
chrome.commands?.onCommand.removeListener(altListener);
|
|
66
91
|
chrome.tabs.onUpdated.removeListener(dropIfOriginChanged);
|
|
67
|
-
chrome.tabs.onRemoved.removeListener(
|
|
68
|
-
possiblyActiveTabs.clear();
|
|
92
|
+
chrome.tabs.onRemoved.removeListener(dropIfActive);
|
|
93
|
+
void possiblyActiveTabs.clear();
|
|
69
94
|
}
|
|
70
95
|
export const PRIVATE = {
|
|
71
96
|
startActiveTabTracking,
|
|
@@ -1,41 +1,44 @@
|
|
|
1
1
|
import { injectContentScript } from 'webext-content-scripts';
|
|
2
2
|
import { onActiveTab, possiblyActiveTabs } from './on-active-tab.js';
|
|
3
|
-
const
|
|
3
|
+
const hasNavigation = typeof chrome === 'object' && 'webNavigation' in chrome;
|
|
4
4
|
const scripts = [];
|
|
5
5
|
async function injectAllFrames({ id: tabId, origin: url }) {
|
|
6
6
|
if (tabId === undefined) {
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
|
-
if (!
|
|
9
|
+
if (!hasNavigation) {
|
|
10
10
|
// Without it, we only inject it into the top frame
|
|
11
11
|
return injectOneFrame({ frameId: 0, url, tabId });
|
|
12
12
|
}
|
|
13
13
|
const frames = await chrome.webNavigation.getAllFrames({ tabId });
|
|
14
14
|
// .map() needed for async loop
|
|
15
|
-
void frames?.map(async ({ frameId, url }) => injectOneFrame({ frameId, url, tabId }));
|
|
15
|
+
void frames?.map(async ({ frameId, url: frameUrl }) => injectOneFrame({ frameId, url: frameUrl, tabId }));
|
|
16
16
|
}
|
|
17
17
|
async function injectOneFrame({ tabId, frameId, url }) {
|
|
18
18
|
const { origin } = new URL(url);
|
|
19
|
+
const activeOrigin = await possiblyActiveTabs.get(String(tabId));
|
|
19
20
|
// Check origin because the request might be for a frame; cross-origin frames do not receive activeTab
|
|
20
|
-
if (
|
|
21
|
+
if (activeOrigin === origin) {
|
|
21
22
|
console.debug('activeTab: will inject', { tabId, frameId, url });
|
|
22
|
-
await injectContentScript({ tabId, frameId },
|
|
23
|
+
await injectContentScript({ tabId, frameId },
|
|
24
|
+
// `webext-content-scripts` uses the `manifest.json` format
|
|
25
|
+
scripts.map(({ runAt, matchAboutBlank, ...script }) => ({ ...script, run_at: runAt, match_about_blank: matchAboutBlank })), { ignoreTargetErrors: true });
|
|
23
26
|
}
|
|
24
27
|
else {
|
|
25
|
-
console.debug('activeTab: won’t inject', { tabId, frameId, url }, { activeTab:
|
|
28
|
+
console.debug('activeTab: won’t inject', { tabId, frameId, url }, { activeTab: activeOrigin ?? 'no' });
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
async function tabListener(tabId, { status }, { url }) {
|
|
29
32
|
// Only status updates are relevant
|
|
30
33
|
// No URL = no permission
|
|
31
|
-
if (status === 'loading' && url) {
|
|
34
|
+
if (status === 'loading' && url !== undefined) {
|
|
32
35
|
await injectOneFrame({ tabId, url, frameId: 0 });
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
export default function registerActiveTabScripts(...newScripts) {
|
|
36
39
|
scripts.push(...newScripts);
|
|
37
40
|
onActiveTab.addListener(injectAllFrames);
|
|
38
|
-
if (
|
|
41
|
+
if (hasNavigation) {
|
|
39
42
|
chrome.webNavigation.onCommitted.addListener(injectOneFrame);
|
|
40
43
|
}
|
|
41
44
|
else {
|
package/package.json
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webext-active-tab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "WebExtension module: Track `activeTab` permission; automatically inject content scripts",
|
|
5
|
-
"
|
|
6
|
-
"contentscript",
|
|
7
|
-
"register",
|
|
8
|
-
"injection",
|
|
9
|
-
"permissions",
|
|
10
|
-
"request",
|
|
11
|
-
"optional_permissions",
|
|
12
|
-
"host_permissions",
|
|
13
|
-
"manifest",
|
|
14
|
-
"new hosts",
|
|
15
|
-
"chrome",
|
|
16
|
-
"firefox",
|
|
17
|
-
"browser",
|
|
18
|
-
"activetab",
|
|
19
|
-
"extension"
|
|
20
|
-
],
|
|
5
|
+
"license": "MIT",
|
|
21
6
|
"repository": "fregante/webext-active-tab",
|
|
22
7
|
"funding": "https://github.com/sponsors/fregante",
|
|
23
|
-
"license": "MIT",
|
|
24
8
|
"author": "Federico Brigante <me@fregante.com> (https://fregante.com)",
|
|
25
9
|
"type": "module",
|
|
26
10
|
"exports": {
|
|
@@ -33,46 +17,54 @@
|
|
|
33
17
|
"default": "./distribution/register-active-tab-scripts.js"
|
|
34
18
|
}
|
|
35
19
|
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=24"
|
|
23
|
+
},
|
|
36
24
|
"scripts": {
|
|
37
25
|
"build": "tsc",
|
|
38
26
|
"demo:build": "parcel build --no-cache",
|
|
39
27
|
"demo:watch": "parcel watch --no-cache --no-hmr",
|
|
40
|
-
"
|
|
28
|
+
"prepare": "tsc --sourceMap false",
|
|
41
29
|
"lint": "xo",
|
|
42
30
|
"fix": "xo --fix",
|
|
43
31
|
"test": "run-p lint build demo:build",
|
|
44
32
|
"watch": "tsc --watch"
|
|
45
33
|
},
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
34
|
+
"files": [
|
|
35
|
+
"distribution"
|
|
36
|
+
],
|
|
37
|
+
"keywords": [
|
|
38
|
+
"contentscript",
|
|
39
|
+
"register",
|
|
40
|
+
"injection",
|
|
41
|
+
"permissions",
|
|
42
|
+
"request",
|
|
43
|
+
"optional_permissions",
|
|
44
|
+
"host_permissions",
|
|
45
|
+
"manifest",
|
|
46
|
+
"new hosts",
|
|
47
|
+
"chrome",
|
|
48
|
+
"firefox",
|
|
49
|
+
"browser",
|
|
50
|
+
"activetab",
|
|
51
|
+
"extension"
|
|
52
|
+
],
|
|
63
53
|
"dependencies": {
|
|
64
|
-
"simple-event-target": "^2.
|
|
65
|
-
"webext-content-scripts": "^
|
|
66
|
-
"webext-detect": "^
|
|
54
|
+
"simple-event-target": "^2.1.0",
|
|
55
|
+
"webext-content-scripts": "^3.1.0",
|
|
56
|
+
"webext-detect": "^6.0.0",
|
|
57
|
+
"webext-storage": "^3.1.0"
|
|
67
58
|
},
|
|
68
59
|
"devDependencies": {
|
|
69
|
-
"@parcel/config-webextension": "^2.
|
|
70
|
-
"@sindresorhus/tsconfig": "^
|
|
71
|
-
"@types/chrome": "^0.
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
60
|
+
"@parcel/config-webextension": "^2.16.4",
|
|
61
|
+
"@sindresorhus/tsconfig": "^8.1.0",
|
|
62
|
+
"@types/chrome": "^0.3.6",
|
|
63
|
+
"globals": "^17.13.0",
|
|
64
|
+
"npm-run-all2": "^9.0.3",
|
|
65
|
+
"parcel": "^2.16.4",
|
|
66
|
+
"typescript": "^6.0.3",
|
|
67
|
+
"xo": "^5.0.1"
|
|
76
68
|
},
|
|
77
69
|
"@parcel/resolver-default": {
|
|
78
70
|
"packageExports": true
|
|
@@ -86,7 +78,12 @@
|
|
|
86
78
|
"sourceMap": {
|
|
87
79
|
"inline": true
|
|
88
80
|
},
|
|
89
|
-
"optimize": false
|
|
81
|
+
"optimize": false,
|
|
82
|
+
"engines": {
|
|
83
|
+
"browsers": [
|
|
84
|
+
"last 1 Chrome version"
|
|
85
|
+
]
|
|
86
|
+
}
|
|
90
87
|
}
|
|
91
88
|
},
|
|
92
89
|
"webExt": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/distribution/register.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default function register(): void;
|
package/distribution/scripts.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { injectContentScript } from 'webext-content-scripts';
|
|
2
|
-
import { addActiveTabListener, possiblyActiveTabs } from './tracker.js';
|
|
3
|
-
import { isContentScriptRegistered } from './utils.js';
|
|
4
|
-
const gotNavigation = typeof chrome === 'object' && 'webNavigation' in chrome;
|
|
5
|
-
const scripts = chrome.runtime.getManifest().content_scripts;
|
|
6
|
-
async function injectToTabUnlessRegistered({ id: tabId, origin }) {
|
|
7
|
-
if (tabId === undefined) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
const frames = gotNavigation
|
|
11
|
-
// Only with `webNavigation` we can inject into the frames
|
|
12
|
-
? await chrome.webNavigation.getAllFrames({ tabId })
|
|
13
|
-
// Without it, we only inject it into the top frame
|
|
14
|
-
: [{ frameId: 0, url: origin }];
|
|
15
|
-
// .map() needed for async loop
|
|
16
|
-
void frames?.map(async ({ frameId, url }) => injectIfActive({ frameId, url, tabId }));
|
|
17
|
-
}
|
|
18
|
-
async function injectIfActive({ tabId, frameId, url }) {
|
|
19
|
-
const { origin } = new URL(url);
|
|
20
|
-
if (
|
|
21
|
-
// Check origin because the request might be for a frame; cross-origin frames do not receive activeTab
|
|
22
|
-
possiblyActiveTabs.get(tabId) === origin
|
|
23
|
-
// Don't inject if already registered
|
|
24
|
-
&& !(await isContentScriptRegistered(url))) {
|
|
25
|
-
console.debug('activeTab: will inject', { tabId, frameId, url });
|
|
26
|
-
await injectContentScript({ tabId, frameId }, scripts);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
console.debug('activeTab: won’t inject', { tabId, frameId, url }, { activeTab: possiblyActiveTabs.get(tabId) ?? 'no' });
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function tabListener(tabId, { status }, { url }) {
|
|
33
|
-
// Only status updates are relevant
|
|
34
|
-
// No URL = no permission
|
|
35
|
-
if (status === 'loading' && url) {
|
|
36
|
-
await injectIfActive({ tabId, url, frameId: 0 });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export default function register() {
|
|
40
|
-
addActiveTabListener(injectToTabUnlessRegistered);
|
|
41
|
-
if (gotNavigation) {
|
|
42
|
-
chrome.webNavigation.onCommitted.addListener(injectIfActive);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
chrome.tabs.onUpdated.addListener(tabListener);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
type TabId = number;
|
|
2
|
-
type Origin = string;
|
|
3
|
-
export type ActiveTab = {
|
|
4
|
-
id: TabId;
|
|
5
|
-
origin: Origin;
|
|
6
|
-
};
|
|
7
|
-
export declare const possiblyActiveTabs: Map<number, string>;
|
|
8
|
-
export declare function startActiveTabTracking(): void;
|
|
9
|
-
export declare function stopActiveTabTracking(): void;
|
|
10
|
-
export declare function addActiveTabListener(callback: (tab: ActiveTab) => void): void;
|
|
11
|
-
export {};
|
package/distribution/tracker.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { isScriptableUrl } from 'webext-content-scripts';
|
|
2
|
-
import { isBackground } from 'webext-detect';
|
|
3
|
-
import SimpleEventTarget from 'simple-event-target';
|
|
4
|
-
if (!isBackground()) {
|
|
5
|
-
throw new Error('This module is only allowed in a background script');
|
|
6
|
-
}
|
|
7
|
-
const newActiveTabs = new SimpleEventTarget();
|
|
8
|
-
const browserAction = chrome.action ?? chrome.browserAction;
|
|
9
|
-
// TODO: use webext-storage
|
|
10
|
-
export const possiblyActiveTabs = new Map();
|
|
11
|
-
async function addIfScriptable({ url, id }) {
|
|
12
|
-
if (id && url
|
|
13
|
-
// Skip if it already exists. A previous change of origin already cleared this
|
|
14
|
-
&& !possiblyActiveTabs.has(id)
|
|
15
|
-
// ActiveTab doesn't make sense on non-scriptable URLs as they generally don't have scriptable frames
|
|
16
|
-
&& isScriptableUrl(url)
|
|
17
|
-
// Note: Do not filter by `isContentScriptRegistered`; `active-tab` also applies to random `executeScript` calls
|
|
18
|
-
) {
|
|
19
|
-
const { origin } = new URL(url);
|
|
20
|
-
console.debug('activeTab:', id, 'added', { origin });
|
|
21
|
-
possiblyActiveTabs.set(id, origin);
|
|
22
|
-
newActiveTabs.emit({ id, origin });
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
console.debug('activeTab:', id, 'not added', { origin });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function dropIfOriginChanged(tabId, { url }) {
|
|
29
|
-
if (url && possiblyActiveTabs.has(tabId)) {
|
|
30
|
-
const { origin } = new URL(url);
|
|
31
|
-
if (possiblyActiveTabs.get(tabId) !== origin) {
|
|
32
|
-
console.debug('activeTab:', tabId, 'removed because origin changed from', possiblyActiveTabs.get(tabId), 'to', origin);
|
|
33
|
-
possiblyActiveTabs.delete(tabId);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function altListener(_, tab) {
|
|
38
|
-
if (tab) {
|
|
39
|
-
void addIfScriptable(tab);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function drop(tabId) {
|
|
43
|
-
console.debug('activeTab:', tabId, 'removed');
|
|
44
|
-
possiblyActiveTabs.delete(tabId);
|
|
45
|
-
}
|
|
46
|
-
// https://developer.chrome.com/docs/extensions/mv3/manifest/activeTab/#invoking-activeTab
|
|
47
|
-
export function startActiveTabTracking() {
|
|
48
|
-
browserAction?.onClicked.addListener(addIfScriptable);
|
|
49
|
-
chrome.contextMenus?.onClicked.addListener(altListener);
|
|
50
|
-
chrome.commands?.onCommand.addListener(altListener);
|
|
51
|
-
chrome.tabs.onUpdated.addListener(dropIfOriginChanged);
|
|
52
|
-
chrome.tabs.onRemoved.addListener(drop);
|
|
53
|
-
}
|
|
54
|
-
export function stopActiveTabTracking() {
|
|
55
|
-
browserAction?.onClicked.removeListener(addIfScriptable);
|
|
56
|
-
chrome.contextMenus?.onClicked.removeListener(altListener);
|
|
57
|
-
chrome.commands?.onCommand.removeListener(altListener);
|
|
58
|
-
chrome.tabs.onUpdated.removeListener(dropIfOriginChanged);
|
|
59
|
-
chrome.tabs.onRemoved.removeListener(drop);
|
|
60
|
-
possiblyActiveTabs.clear();
|
|
61
|
-
}
|
|
62
|
-
export function addActiveTabListener(callback) {
|
|
63
|
-
startActiveTabTracking();
|
|
64
|
-
newActiveTabs.listen(callback);
|
|
65
|
-
}
|
package/distribution/utils.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare function isContentScriptStaticallyRegistered(url: string): boolean;
|
|
2
|
-
export declare function isContentScriptDynamicallyRegistered(url: string): Promise<boolean>;
|
|
3
|
-
/**
|
|
4
|
-
Checks whether a URL will have the content scripts automatically injected.
|
|
5
|
-
It returns a promise that resolves with string indicating the type of injection ('static' or 'dynamic') or `false` if it won't be injected on the specified URL.
|
|
6
|
-
*/
|
|
7
|
-
export declare function isContentScriptRegistered(url: string): Promise<'static' | 'dynamic' | false>;
|
package/distribution/utils.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { queryAdditionalPermissions } from 'webext-permissions';
|
|
2
|
-
import { patternToRegex } from 'webext-patterns';
|
|
3
|
-
export function isContentScriptStaticallyRegistered(url) {
|
|
4
|
-
return Boolean(chrome.runtime
|
|
5
|
-
.getManifest()
|
|
6
|
-
.content_scripts
|
|
7
|
-
?.flatMap(script => script.matches)
|
|
8
|
-
.some(pattern => patternToRegex(pattern).test(url)));
|
|
9
|
-
}
|
|
10
|
-
export async function isContentScriptDynamicallyRegistered(url) {
|
|
11
|
-
// Injected by `webext-active-tab`
|
|
12
|
-
const { origins } = await queryAdditionalPermissions({
|
|
13
|
-
strictOrigins: false,
|
|
14
|
-
});
|
|
15
|
-
// Do not replace the 2 calls above with `permissions.getAll` because it might also include hosts that are permitted by the manifest but have no content script registered
|
|
16
|
-
return patternToRegex(...origins).test(url);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
Checks whether a URL will have the content scripts automatically injected.
|
|
20
|
-
It returns a promise that resolves with string indicating the type of injection ('static' or 'dynamic') or `false` if it won't be injected on the specified URL.
|
|
21
|
-
*/
|
|
22
|
-
export async function isContentScriptRegistered(url) {
|
|
23
|
-
if (isContentScriptStaticallyRegistered(url)) {
|
|
24
|
-
return 'static';
|
|
25
|
-
}
|
|
26
|
-
if (await isContentScriptDynamicallyRegistered(url)) {
|
|
27
|
-
return 'dynamic';
|
|
28
|
-
}
|
|
29
|
-
return false;
|
|
30
|
-
}
|